1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
21 #include <sal/log.hxx>
28 #include <com/sun/star/configuration/theDefaultProvider.hpp>
29 #include <com/sun/star/container/XHierarchicalNameAccess.hpp>
30 #include <com/sun/star/container/XNameAccess.hpp>
31 #include <com/sun/star/lang/IllegalArgumentException.hpp>
32 #include <com/sun/star/lang/XServiceInfo.hpp>
33 #include <com/sun/star/lang/XServiceName.hpp>
34 #include <com/sun/star/uno/XComponentContext.hpp>
35 #include <comphelper/propertysequence.hxx>
36 #include <cppuhelper/implbase.hxx>
37 #include <cppuhelper/supportsservice.hxx>
38 #include <o3tl/functional.hxx>
39 #include <config_features.h>
40 #include <vcl/skia/SkiaHelper.hxx>
41 #include <unotools/configmgr.hxx>
43 using namespace ::com::sun::star
;
44 using namespace ::com::sun::star::uno
;
51 : public ::cppu::WeakImplHelper
< lang::XServiceInfo
,
52 lang::XMultiComponentFactory
,
53 lang::XMultiServiceFactory
>
55 typedef std::pair
< OUString
, Sequence
< OUString
> > AvailPair
;
56 typedef std::pair
< OUString
, OUString
> CachePair
;
57 typedef std::vector
< AvailPair
> AvailVector
;
58 typedef std::vector
< CachePair
> CacheVector
;
61 mutable std::mutex m_mutex
;
62 Reference
<XComponentContext
> m_xContext
;
63 Reference
<container::XNameAccess
> m_xCanvasConfigNameAccess
;
64 AvailVector m_aAvailableImplementations
;
65 AvailVector m_aAcceleratedImplementations
;
66 AvailVector m_aAAImplementations
;
67 mutable CacheVector m_aCachedImplementations
;
68 mutable bool m_bCacheHasForcedLastImpl
;
69 mutable bool m_bCacheHasUseAcceleratedEntry
;
70 mutable bool m_bCacheHasUseAAEntry
;
72 void checkConfigFlag( bool& r_bFlag
,
74 const OUString
& nodeName
) const;
75 Reference
<XInterface
> use(
76 OUString
const & serviceName
,
77 Sequence
<Any
> const & args
,
78 Reference
<XComponentContext
> const & xContext
) const;
79 Reference
<XInterface
> lookupAndUse(
80 OUString
const & serviceName
, Sequence
<Any
> const & args
,
81 Reference
<XComponentContext
> const & xContext
) const;
84 virtual ~CanvasFactory() override
;
85 explicit CanvasFactory( Reference
<XComponentContext
> const & xContext
);
88 virtual OUString SAL_CALL
getImplementationName() override
;
89 virtual sal_Bool SAL_CALL
supportsService( OUString
const & serviceName
) override
;
90 virtual Sequence
<OUString
> SAL_CALL
getSupportedServiceNames() override
;
92 // XMultiComponentFactory
93 virtual Sequence
<OUString
> SAL_CALL
getAvailableServiceNames() override
;
94 virtual Reference
<XInterface
> SAL_CALL
createInstanceWithContext(
95 OUString
const & name
,
96 Reference
<XComponentContext
> const & xContext
) override
;
97 virtual Reference
<XInterface
> SAL_CALL
98 createInstanceWithArgumentsAndContext(
99 OUString
const & name
,
100 Sequence
<Any
> const & args
,
101 Reference
<XComponentContext
> const & xContext
) override
;
103 // XMultiServiceFactory
104 virtual Reference
<XInterface
> SAL_CALL
createInstance(
105 OUString
const & name
) override
;
106 virtual Reference
<XInterface
> SAL_CALL
createInstanceWithArguments(
107 OUString
const & name
, Sequence
<Any
> const & args
) override
;
110 CanvasFactory::CanvasFactory( Reference
<XComponentContext
> const & xContext
) :
111 m_xContext(xContext
),
112 m_bCacheHasForcedLastImpl(),
113 m_bCacheHasUseAcceleratedEntry(),
114 m_bCacheHasUseAAEntry()
116 if (!utl::ConfigManager::IsFuzzing())
120 // read out configuration for preferred services:
121 Reference
<lang::XMultiServiceFactory
> xConfigProvider(
122 configuration::theDefaultProvider::get( m_xContext
) );
124 uno::Sequence
<uno::Any
> aArgs(comphelper::InitAnyPropertySequence(
126 {"nodepath", uno::Any(OUString("/org.openoffice.Office.Canvas"))}
128 m_xCanvasConfigNameAccess
.set(
129 xConfigProvider
->createInstanceWithArguments(
130 "com.sun.star.configuration.ConfigurationAccess",
134 uno::Sequence
<uno::Any
> aArgs2(comphelper::InitAnyPropertySequence(
136 {"nodepath", uno::Any(OUString("/org.openoffice.Office.Canvas/CanvasServiceList"))}
138 Reference
<container::XNameAccess
> xNameAccess(
139 xConfigProvider
->createInstanceWithArguments(
140 "com.sun.star.configuration.ConfigurationAccess",
141 aArgs2
), UNO_QUERY_THROW
);
142 Reference
<container::XHierarchicalNameAccess
> xHierarchicalNameAccess(
143 xNameAccess
, UNO_QUERY_THROW
);
145 Sequence
<OUString
> serviceNames
= xNameAccess
->getElementNames();
146 const OUString
* pCurr
= serviceNames
.getConstArray();
147 const OUString
* const pEnd
= pCurr
+ serviceNames
.getLength();
148 while( pCurr
!= pEnd
)
150 Reference
<container::XNameAccess
> xEntryNameAccess(
151 xHierarchicalNameAccess
->getByHierarchicalName(*pCurr
),
154 if( xEntryNameAccess
.is() )
156 Sequence
<OUString
> implementationList
;
157 if( xEntryNameAccess
->getByName("PreferredImplementations") >>= implementationList
)
159 m_aAvailableImplementations
.emplace_back(*pCurr
,implementationList
);
161 if( xEntryNameAccess
->getByName("AcceleratedImplementations") >>= implementationList
)
163 m_aAcceleratedImplementations
.emplace_back(*pCurr
,implementationList
);
165 if( xEntryNameAccess
->getByName("AntialiasingImplementations") >>= implementationList
)
167 m_aAAImplementations
.emplace_back(*pCurr
,implementationList
);
175 catch (const RuntimeException
&)
179 catch (const Exception
&)
184 if (m_aAvailableImplementations
.empty())
186 // Ugh. Looks like configuration is borked. Fake minimal
188 m_aAvailableImplementations
.emplace_back(OUString("com.sun.star.rendering.Canvas"),
189 Sequence
<OUString
>{ "com.sun.star.comp.rendering.Canvas.VCL" } );
191 m_aAvailableImplementations
.emplace_back(OUString("com.sun.star.rendering.SpriteCanvas"),
192 Sequence
<OUString
>{ "com.sun.star.comp.rendering.SpriteCanvas.VCL" } );
196 CanvasFactory::~CanvasFactory()
202 OUString
CanvasFactory::getImplementationName()
204 return "com.sun.star.comp.rendering.CanvasFactory";
207 sal_Bool
CanvasFactory::supportsService( OUString
const & serviceName
)
209 return cppu::supportsService(this, serviceName
);
212 Sequence
<OUString
> CanvasFactory::getSupportedServiceNames()
214 return { "com.sun.star.rendering.CanvasFactory" };
217 // XMultiComponentFactory
218 Sequence
<OUString
> CanvasFactory::getAvailableServiceNames()
220 Sequence
<OUString
> aServiceNames(m_aAvailableImplementations
.size());
221 std::transform(m_aAvailableImplementations
.begin(),
222 m_aAvailableImplementations
.end(),
223 aServiceNames
.getArray(),
224 o3tl::select1st
< AvailPair
>());
225 return aServiceNames
;
228 Reference
<XInterface
> CanvasFactory::createInstanceWithContext(
229 OUString
const & name
, Reference
<XComponentContext
> const & xContext
)
231 return createInstanceWithArgumentsAndContext(
232 name
, Sequence
<Any
>(), xContext
);
236 Reference
<XInterface
> CanvasFactory::use(
237 OUString
const & serviceName
,
238 Sequence
<Any
> const & args
,
239 Reference
<XComponentContext
> const & xContext
) const
242 return m_xContext
->getServiceManager()->createInstanceWithArgumentsAndContext(
243 serviceName
, args
, xContext
);
245 catch (css::lang::IllegalArgumentException
&)
247 return Reference
<XInterface
>();
249 catch (const RuntimeException
&)
253 catch (const Exception
&)
255 return Reference
<XInterface
>();
260 void CanvasFactory::checkConfigFlag( bool& r_bFlag
,
262 const OUString
& nodeName
) const
264 if( m_xCanvasConfigNameAccess
.is() )
266 m_xCanvasConfigNameAccess
->getByName( nodeName
) >>= r_bFlag
;
268 if( r_CacheFlag
!= r_bFlag
)
270 // cache is invalid, because of different order of
272 r_CacheFlag
= r_bFlag
;
273 m_aCachedImplementations
.clear();
279 Reference
<XInterface
> CanvasFactory::lookupAndUse(
280 OUString
const & serviceName
, Sequence
<Any
> const & args
,
281 Reference
<XComponentContext
> const & xContext
) const
283 std::scoped_lock
guard(m_mutex
);
285 // forcing last entry from impl list, if config flag set
286 bool bForceLastEntry(false);
287 checkConfigFlag( bForceLastEntry
,
288 m_bCacheHasForcedLastImpl
,
289 "ForceSafeServiceImpl" );
291 // use anti-aliasing canvas, if config flag set (or not existing)
292 bool bUseAAEntry(true);
293 checkConfigFlag( bUseAAEntry
,
294 m_bCacheHasUseAAEntry
,
295 "UseAntialiasingCanvas" );
297 // use accelerated canvas, if config flag set (or not existing)
298 bool bUseAcceleratedEntry(true);
299 checkConfigFlag( bUseAcceleratedEntry
,
300 m_bCacheHasUseAcceleratedEntry
,
301 "UseAcceleratedCanvas" );
303 // try to reuse last working implementation for given service name
304 const CacheVector::iterator
aEnd(m_aCachedImplementations
.end());
305 auto aMatch
= std::find_if(
306 m_aCachedImplementations
.begin(),
308 [&serviceName
](CachePair
const& cp
)
309 { return serviceName
== cp
.first
; }
311 if( aMatch
!= aEnd
) {
312 Reference
<XInterface
> xCanvas( use( aMatch
->second
, args
, xContext
) );
317 // lookup in available service list
318 const AvailVector::const_iterator
aAvailEnd(m_aAvailableImplementations
.end());
319 auto aAvailImplsMatch
= std::find_if(
320 m_aAvailableImplementations
.begin(),
322 [&serviceName
](AvailPair
const& ap
)
323 { return serviceName
== ap
.first
; }
325 if( aAvailImplsMatch
== aAvailEnd
) {
326 return Reference
<XInterface
>();
329 const AvailVector::const_iterator
aAAEnd(m_aAAImplementations
.end());
330 auto aAAImplsMatch
= std::find_if(
331 m_aAAImplementations
.begin(),
333 [&serviceName
](AvailPair
const& ap
)
334 { return serviceName
== ap
.first
; }
336 if( aAAImplsMatch
== aAAEnd
) {
337 return Reference
<XInterface
>();
340 const AvailVector::const_iterator
aAccelEnd(m_aAcceleratedImplementations
.end());
341 auto aAccelImplsMatch
= std::find_if(
342 m_aAcceleratedImplementations
.begin(),
344 [&serviceName
](AvailPair
const& ap
)
345 { return serviceName
== ap
.first
; }
347 if( aAccelImplsMatch
== aAccelEnd
) {
348 return Reference
<XInterface
>();
351 const Sequence
<OUString
> aPreferredImpls( aAvailImplsMatch
->second
);
352 const OUString
* pCurrImpl
= aPreferredImpls
.begin();
353 const OUString
* const pEndImpl
= aPreferredImpls
.end();
355 const Sequence
<OUString
> aAAImpls( aAAImplsMatch
->second
);
357 const Sequence
<OUString
> aAccelImpls( aAccelImplsMatch
->second
);
359 // force last entry from impl list, if config flag set
360 if (bForceLastEntry
&& pCurrImpl
!= pEndImpl
)
361 pCurrImpl
= pEndImpl
-1;
363 for(; pCurrImpl
!= pEndImpl
; ++pCurrImpl
)
365 const OUString
aCurrName(pCurrImpl
->trim());
367 // Skia works only with vclcanvas.
368 if( SkiaHelper::isVCLSkiaEnabled() && !aCurrName
.endsWith(".VCL"))
371 // check whether given canvas service is listed in the
372 // sequence of "accelerated canvas implementations"
373 const bool bIsAcceleratedImpl(
374 std::any_of(aAccelImpls
.begin(), aAccelImpls
.end(),
375 [&aCurrName
](OUString
const& src
)
376 { return aCurrName
== src
.trim(); }
379 // check whether given canvas service is listed in the
380 // sequence of "antialiasing canvas implementations"
381 const bool bIsAAImpl(
382 std::any_of(aAAImpls
.begin(), aAAImpls
.end(),
383 [&aCurrName
](OUString
const& src
)
384 { return aCurrName
== src
.trim(); }
387 // try to instantiate canvas *only* if either accel and AA
388 // property match preference, *or*, if there's a mismatch, only
389 // go for a less capable canvas (that effectively let those
390 // pour canvas impls still work as fallbacks, should an
391 // accelerated/AA one fail). Property implies configuration:
392 // http://en.wikipedia.org/wiki/Truth_table#Logical_implication
393 if( (!bIsAAImpl
|| bUseAAEntry
) && (!bIsAcceleratedImpl
|| bUseAcceleratedEntry
) )
395 Reference
<XInterface
> xCanvas(use(aCurrName
, args
, xContext
));
401 // cache entry exists, replace dysfunctional
402 // implementation name
403 aMatch
->second
= aCurrName
;
407 // new service name, add new cache entry
408 m_aCachedImplementations
.emplace_back(serviceName
, aCurrName
);
416 return Reference
<XInterface
>();
420 Reference
<XInterface
> CanvasFactory::createInstanceWithArgumentsAndContext(
421 OUString
const & preferredOne
, Sequence
<Any
> const & args
,
422 Reference
<XComponentContext
> const & xContext
)
424 Reference
<XInterface
> xCanvas(lookupAndUse(preferredOne
, args
, xContext
));
426 // last resort: try service name directly
427 xCanvas
= use(preferredOne
, args
, xContext
);
431 Reference
<lang::XServiceName
> xServiceName(xCanvas
, uno::UNO_QUERY
);
432 SAL_INFO("canvas", "using " << (xServiceName
.is() ? xServiceName
->getServiceName()
433 : OUString("(unknown)")));
438 // XMultiServiceFactory
440 Reference
<XInterface
> CanvasFactory::createInstance( OUString
const & name
)
442 return createInstanceWithArgumentsAndContext(
443 name
, Sequence
<Any
>(), m_xContext
);
447 Reference
<XInterface
> CanvasFactory::createInstanceWithArguments(
448 OUString
const & name
, Sequence
<Any
> const & args
)
450 return createInstanceWithArgumentsAndContext(
451 name
, args
, m_xContext
);
457 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
458 com_sun_star_comp_rendering_CanvasFactory_get_implementation(css::uno::XComponentContext
* context
,
459 css::uno::Sequence
<css::uno::Any
> const &)
461 return cppu::acquire(new CanvasFactory(context
));
465 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */