/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ::com::sun::star; using namespace ::com::sun::star::uno; namespace { class CanvasFactory : public ::cppu::WeakImplHelper< lang::XServiceInfo, lang::XMultiComponentFactory, lang::XMultiServiceFactory > { typedef std::pair< OUString, Sequence< OUString > > AvailPair; typedef std::pair< OUString, OUString > CachePair; typedef std::vector< AvailPair > AvailVector; typedef std::vector< CachePair > CacheVector; mutable std::mutex m_mutex; Reference m_xContext; Reference m_xCanvasConfigNameAccess; AvailVector m_aAvailableImplementations; AvailVector m_aAcceleratedImplementations; AvailVector m_aAAImplementations; mutable CacheVector m_aCachedImplementations; mutable bool m_bCacheHasForcedLastImpl; mutable bool m_bCacheHasUseAcceleratedEntry; mutable bool m_bCacheHasUseAAEntry; void checkConfigFlag( bool& r_bFlag, bool& r_CacheFlag, const OUString& nodeName ) const; Reference use( OUString const & serviceName, Sequence const & args, Reference const & xContext ) const; Reference lookupAndUse( OUString const & serviceName, Sequence const & args, Reference const & xContext ) const; public: virtual ~CanvasFactory() override; explicit CanvasFactory( Reference const & xContext ); // XServiceInfo virtual OUString SAL_CALL getImplementationName() override; virtual sal_Bool SAL_CALL supportsService( OUString const & serviceName ) override; virtual Sequence SAL_CALL getSupportedServiceNames() override; // XMultiComponentFactory virtual Sequence SAL_CALL getAvailableServiceNames() override; virtual Reference SAL_CALL createInstanceWithContext( OUString const & name, Reference const & xContext ) override; virtual Reference SAL_CALL createInstanceWithArgumentsAndContext( OUString const & name, Sequence const & args, Reference const & xContext ) override; // XMultiServiceFactory virtual Reference SAL_CALL createInstance( OUString const & name ) override; virtual Reference SAL_CALL createInstanceWithArguments( OUString const & name, Sequence const & args ) override; }; CanvasFactory::CanvasFactory( Reference const & xContext ) : m_xContext(xContext), m_bCacheHasForcedLastImpl(), m_bCacheHasUseAcceleratedEntry(), m_bCacheHasUseAAEntry() { if (!utl::ConfigManager::IsFuzzing()) { try { // read out configuration for preferred services: Reference xConfigProvider( configuration::theDefaultProvider::get( m_xContext ) ); uno::Sequence aArgs(comphelper::InitAnyPropertySequence( { {"nodepath", uno::Any(OUString("/org.openoffice.Office.Canvas"))} })); m_xCanvasConfigNameAccess.set( xConfigProvider->createInstanceWithArguments( "com.sun.star.configuration.ConfigurationAccess", aArgs ), UNO_QUERY_THROW ); uno::Sequence aArgs2(comphelper::InitAnyPropertySequence( { {"nodepath", uno::Any(OUString("/org.openoffice.Office.Canvas/CanvasServiceList"))} })); Reference xNameAccess( xConfigProvider->createInstanceWithArguments( "com.sun.star.configuration.ConfigurationAccess", aArgs2 ), UNO_QUERY_THROW ); Reference xHierarchicalNameAccess( xNameAccess, UNO_QUERY_THROW); Sequence serviceNames = xNameAccess->getElementNames(); const OUString* pCurr = serviceNames.getConstArray(); const OUString* const pEnd = pCurr + serviceNames.getLength(); while( pCurr != pEnd ) { Reference xEntryNameAccess( xHierarchicalNameAccess->getByHierarchicalName(*pCurr), UNO_QUERY ); if( xEntryNameAccess.is() ) { Sequence implementationList; if( xEntryNameAccess->getByName("PreferredImplementations") >>= implementationList ) { m_aAvailableImplementations.emplace_back(*pCurr,implementationList ); } if( xEntryNameAccess->getByName("AcceleratedImplementations") >>= implementationList ) { m_aAcceleratedImplementations.emplace_back(*pCurr,implementationList ); } if( xEntryNameAccess->getByName("AntialiasingImplementations") >>= implementationList ) { m_aAAImplementations.emplace_back(*pCurr,implementationList ); } } ++pCurr; } } catch (const RuntimeException &) { throw; } catch (const Exception&) { } } if (m_aAvailableImplementations.empty()) { // Ugh. Looks like configuration is borked. Fake minimal // setup. m_aAvailableImplementations.emplace_back(OUString("com.sun.star.rendering.Canvas"), Sequence{ "com.sun.star.comp.rendering.Canvas.VCL" } ); m_aAvailableImplementations.emplace_back(OUString("com.sun.star.rendering.SpriteCanvas"), Sequence{ "com.sun.star.comp.rendering.SpriteCanvas.VCL" } ); } } CanvasFactory::~CanvasFactory() { } // XServiceInfo OUString CanvasFactory::getImplementationName() { return "com.sun.star.comp.rendering.CanvasFactory"; } sal_Bool CanvasFactory::supportsService( OUString const & serviceName ) { return cppu::supportsService(this, serviceName); } Sequence CanvasFactory::getSupportedServiceNames() { return { "com.sun.star.rendering.CanvasFactory" }; } // XMultiComponentFactory Sequence CanvasFactory::getAvailableServiceNames() { Sequence aServiceNames(m_aAvailableImplementations.size()); std::transform(m_aAvailableImplementations.begin(), m_aAvailableImplementations.end(), aServiceNames.getArray(), o3tl::select1st< AvailPair >()); return aServiceNames; } Reference CanvasFactory::createInstanceWithContext( OUString const & name, Reference const & xContext ) { return createInstanceWithArgumentsAndContext( name, Sequence(), xContext ); } Reference CanvasFactory::use( OUString const & serviceName, Sequence const & args, Reference const & xContext ) const { try { return m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( serviceName, args, xContext); } catch (css::lang::IllegalArgumentException &) { return Reference(); } catch (const RuntimeException &) { throw; } catch (const Exception &) { return Reference(); } } void CanvasFactory::checkConfigFlag( bool& r_bFlag, bool& r_CacheFlag, const OUString& nodeName ) const { if( m_xCanvasConfigNameAccess.is() ) { m_xCanvasConfigNameAccess->getByName( nodeName ) >>= r_bFlag; if( r_CacheFlag != r_bFlag ) { // cache is invalid, because of different order of // elements r_CacheFlag = r_bFlag; m_aCachedImplementations.clear(); } } } Reference CanvasFactory::lookupAndUse( OUString const & serviceName, Sequence const & args, Reference const & xContext ) const { std::scoped_lock guard(m_mutex); // forcing last entry from impl list, if config flag set bool bForceLastEntry(false); checkConfigFlag( bForceLastEntry, m_bCacheHasForcedLastImpl, "ForceSafeServiceImpl" ); // use anti-aliasing canvas, if config flag set (or not existing) bool bUseAAEntry(true); checkConfigFlag( bUseAAEntry, m_bCacheHasUseAAEntry, "UseAntialiasingCanvas" ); // use accelerated canvas, if config flag set (or not existing) bool bUseAcceleratedEntry(true); checkConfigFlag( bUseAcceleratedEntry, m_bCacheHasUseAcceleratedEntry, "UseAcceleratedCanvas" ); // try to reuse last working implementation for given service name const CacheVector::iterator aEnd(m_aCachedImplementations.end()); auto aMatch = std::find_if( m_aCachedImplementations.begin(), aEnd, [&serviceName](CachePair const& cp) { return serviceName == cp.first; } ); if( aMatch != aEnd ) { Reference xCanvas( use( aMatch->second, args, xContext ) ); if(xCanvas.is()) return xCanvas; } // lookup in available service list const AvailVector::const_iterator aAvailEnd(m_aAvailableImplementations.end()); auto aAvailImplsMatch = std::find_if( m_aAvailableImplementations.begin(), aAvailEnd, [&serviceName](AvailPair const& ap) { return serviceName == ap.first; } ); if( aAvailImplsMatch == aAvailEnd ) { return Reference(); } const AvailVector::const_iterator aAAEnd(m_aAAImplementations.end()); auto aAAImplsMatch = std::find_if( m_aAAImplementations.begin(), aAAEnd, [&serviceName](AvailPair const& ap) { return serviceName == ap.first; } ); if( aAAImplsMatch == aAAEnd ) { return Reference(); } const AvailVector::const_iterator aAccelEnd(m_aAcceleratedImplementations.end()); auto aAccelImplsMatch = std::find_if( m_aAcceleratedImplementations.begin(), aAccelEnd, [&serviceName](AvailPair const& ap) { return serviceName == ap.first; } ); if( aAccelImplsMatch == aAccelEnd ) { return Reference(); } const Sequence aPreferredImpls( aAvailImplsMatch->second ); const OUString* pCurrImpl = aPreferredImpls.begin(); const OUString* const pEndImpl = aPreferredImpls.end(); const Sequence aAAImpls( aAAImplsMatch->second ); const Sequence aAccelImpls( aAccelImplsMatch->second ); // force last entry from impl list, if config flag set if (bForceLastEntry && pCurrImpl != pEndImpl) pCurrImpl = pEndImpl-1; for(; pCurrImpl != pEndImpl; ++pCurrImpl) { const OUString aCurrName(pCurrImpl->trim()); // Skia works only with vclcanvas. if( SkiaHelper::isVCLSkiaEnabled() && !aCurrName.endsWith(".VCL")) continue; // check whether given canvas service is listed in the // sequence of "accelerated canvas implementations" const bool bIsAcceleratedImpl( std::any_of(aAccelImpls.begin(), aAccelImpls.end(), [&aCurrName](OUString const& src) { return aCurrName == o3tl::trim(src); } )); // check whether given canvas service is listed in the // sequence of "antialiasing canvas implementations" const bool bIsAAImpl( std::any_of(aAAImpls.begin(), aAAImpls.end(), [&aCurrName](OUString const& src) { return aCurrName == o3tl::trim(src); } )); // try to instantiate canvas *only* if either accel and AA // property match preference, *or*, if there's a mismatch, only // go for a less capable canvas (that effectively let those // pour canvas impls still work as fallbacks, should an // accelerated/AA one fail). Property implies configuration: // http://en.wikipedia.org/wiki/Truth_table#Logical_implication if( (!bIsAAImpl || bUseAAEntry) && (!bIsAcceleratedImpl || bUseAcceleratedEntry) ) { Reference xCanvas(use(aCurrName, args, xContext)); if(xCanvas.is()) { if( aMatch != aEnd ) { // cache entry exists, replace dysfunctional // implementation name aMatch->second = aCurrName; } else { // new service name, add new cache entry m_aCachedImplementations.emplace_back(serviceName, aCurrName); } return xCanvas; } } } return Reference(); } Reference CanvasFactory::createInstanceWithArgumentsAndContext( OUString const & preferredOne, Sequence const & args, Reference const & xContext ) { Reference xCanvas(lookupAndUse(preferredOne, args, xContext)); if (!xCanvas.is()) // last resort: try service name directly xCanvas = use(preferredOne, args, xContext); if (xCanvas.is()) { Reference xServiceName(xCanvas, uno::UNO_QUERY); SAL_INFO("canvas", "using " << (xServiceName.is() ? xServiceName->getServiceName() : OUString("(unknown)"))); } return xCanvas; } // XMultiServiceFactory Reference CanvasFactory::createInstance( OUString const & name ) { return createInstanceWithArgumentsAndContext( name, Sequence(), m_xContext ); } Reference CanvasFactory::createInstanceWithArguments( OUString const & name, Sequence const & args ) { return createInstanceWithArgumentsAndContext( name, args, m_xContext ); } } // anon namespace extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* com_sun_star_comp_rendering_CanvasFactory_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence const &) { return cppu::acquire(new CanvasFactory(context)); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */