1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20 #include <sal/config.h>
21 #include <sal/log.hxx>
22
23 #include <algorithm>
24 #include <utility>
25 #include <vector>
26
27 #include <com/sun/star/configuration/theDefaultProvider.hpp>
28 #include <com/sun/star/container/XHierarchicalNameAccess.hpp>
29 #include <com/sun/star/container/XNameAccess.hpp>
30 #include <com/sun/star/lang/IllegalArgumentException.hpp>
31 #include <com/sun/star/lang/XServiceInfo.hpp>
32 #include <com/sun/star/lang/XServiceName.hpp>
33 #include <com/sun/star/uno/XComponentContext.hpp>
34 #include <comphelper/propertysequence.hxx>
35 #include <cppuhelper/implbase.hxx>
36 #include <cppuhelper/supportsservice.hxx>
37 #include <osl/mutex.hxx>
38 #include <o3tl/functional.hxx>
39 #include <config_features.h>
40 #if HAVE_FEATURE_OPENGL
41 #include <vcl/opengl/OpenGLWrapper.hxx>
42 #endif
43 #include <unotools/configmgr.hxx>
44
45 using namespace ::com::sun::star;
46 using namespace ::com::sun::star::uno;
47
48
49 namespace
50 {
51
52 class CanvasFactory
53 : public ::cppu::WeakImplHelper< lang::XServiceInfo,
54 lang::XMultiComponentFactory,
55 lang::XMultiServiceFactory >
56 {
57 typedef std::pair< OUString, Sequence< OUString > > AvailPair;
58 typedef std::pair< OUString, OUString > CachePair;
59 typedef std::vector< AvailPair > AvailVector;
60 typedef std::vector< CachePair > CacheVector;
61
62
63 mutable ::osl::Mutex m_mutex;
64 Reference<XComponentContext> m_xContext;
65 Reference<container::XNameAccess> m_xCanvasConfigNameAccess;
66 AvailVector m_aAvailableImplementations;
67 AvailVector m_aAcceleratedImplementations;
68 AvailVector m_aAAImplementations;
69 mutable CacheVector m_aCachedImplementations;
70 mutable bool m_bCacheHasForcedLastImpl;
71 mutable bool m_bCacheHasUseAcceleratedEntry;
72 mutable bool m_bCacheHasUseAAEntry;
73
74 void checkConfigFlag( bool& r_bFlag,
75 bool& r_CacheFlag,
76 const OUString& nodeName ) const;
77 Reference<XInterface> use(
78 OUString const & serviceName,
79 Sequence<Any> const & args,
80 Reference<XComponentContext> const & xContext ) const;
81 Reference<XInterface> lookupAndUse(
82 OUString const & serviceName, Sequence<Any> const & args,
83 Reference<XComponentContext> const & xContext ) const;
84
85 public:
86 virtual ~CanvasFactory() override;
87 explicit CanvasFactory( Reference<XComponentContext> const & xContext );
88
89 // XServiceInfo
90 virtual OUString SAL_CALL getImplementationName() override;
91 virtual sal_Bool SAL_CALL supportsService( OUString const & serviceName ) override;
92 virtual Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
93
94 // XMultiComponentFactory
95 virtual Sequence<OUString> SAL_CALL getAvailableServiceNames() override;
96 virtual Reference<XInterface> SAL_CALL createInstanceWithContext(
97 OUString const & name,
98 Reference<XComponentContext> const & xContext ) override;
99 virtual Reference<XInterface> SAL_CALL
100 createInstanceWithArgumentsAndContext(
101 OUString const & name,
102 Sequence<Any> const & args,
103 Reference<XComponentContext> const & xContext ) override;
104
105 // XMultiServiceFactory
106 virtual Reference<XInterface> SAL_CALL createInstance(
107 OUString const & name ) override;
108 virtual Reference<XInterface> SAL_CALL createInstanceWithArguments(
109 OUString const & name, Sequence<Any> const & args ) override;
110 };
111
CanvasFactory(Reference<XComponentContext> const & xContext)112 CanvasFactory::CanvasFactory( Reference<XComponentContext> const & xContext ) :
113 m_mutex(),
114 m_xContext(xContext),
115 m_xCanvasConfigNameAccess(),
116 m_aAvailableImplementations(),
117 m_aAcceleratedImplementations(),
118 m_aAAImplementations(),
119 m_aCachedImplementations(),
120 m_bCacheHasForcedLastImpl(),
121 m_bCacheHasUseAcceleratedEntry(),
122 m_bCacheHasUseAAEntry()
123 {
124 if (!utl::ConfigManager::IsFuzzing())
125 {
126 try
127 {
128 // read out configuration for preferred services:
129 Reference<lang::XMultiServiceFactory> xConfigProvider(
130 configuration::theDefaultProvider::get( m_xContext ) );
131
132 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
133 {
134 {"nodepath", uno::Any(OUString("/org.openoffice.Office.Canvas"))}
135 }));
136 m_xCanvasConfigNameAccess.set(
137 xConfigProvider->createInstanceWithArguments(
138 "com.sun.star.configuration.ConfigurationAccess",
139 aArgs ),
140 UNO_QUERY_THROW );
141
142 uno::Sequence<uno::Any> aArgs2(comphelper::InitAnyPropertySequence(
143 {
144 {"nodepath", uno::Any(OUString("/org.openoffice.Office.Canvas/CanvasServiceList"))}
145 }));
146 Reference<container::XNameAccess> xNameAccess(
147 xConfigProvider->createInstanceWithArguments(
148 "com.sun.star.configuration.ConfigurationAccess",
149 aArgs2 ), UNO_QUERY_THROW );
150 Reference<container::XHierarchicalNameAccess> xHierarchicalNameAccess(
151 xNameAccess, UNO_QUERY_THROW);
152
153 Sequence<OUString> serviceNames = xNameAccess->getElementNames();
154 const OUString* pCurr = serviceNames.getConstArray();
155 const OUString* const pEnd = pCurr + serviceNames.getLength();
156 while( pCurr != pEnd )
157 {
158 Reference<container::XNameAccess> xEntryNameAccess(
159 xHierarchicalNameAccess->getByHierarchicalName(*pCurr),
160 UNO_QUERY );
161
162 if( xEntryNameAccess.is() )
163 {
164 Sequence<OUString> implementationList;
165 if( xEntryNameAccess->getByName("PreferredImplementations") >>= implementationList )
166 {
167 m_aAvailableImplementations.emplace_back(*pCurr,implementationList );
168 }
169 if( xEntryNameAccess->getByName("AcceleratedImplementations") >>= implementationList )
170 {
171 m_aAcceleratedImplementations.emplace_back(*pCurr,implementationList );
172 }
173 if( xEntryNameAccess->getByName("AntialiasingImplementations") >>= implementationList )
174 {
175 m_aAAImplementations.emplace_back(*pCurr,implementationList );
176 }
177
178 }
179
180 ++pCurr;
181 }
182 }
183 catch (const RuntimeException &)
184 {
185 throw;
186 }
187 catch (const Exception&)
188 {
189 }
190 }
191
192 if (m_aAvailableImplementations.empty())
193 {
194 // Ugh. Looks like configuration is borked. Fake minimal
195 // setup.
196 Sequence<OUString> aServices { "com.sun.star.comp.rendering.Canvas.VCL" };
197 m_aAvailableImplementations.emplace_back(OUString("com.sun.star.rendering.Canvas"),
198 aServices );
199
200 aServices[0] = "com.sun.star.comp.rendering.SpriteCanvas.VCL";
201 m_aAvailableImplementations.emplace_back(OUString("com.sun.star.rendering.SpriteCanvas"),
202 aServices );
203 }
204 }
205
~CanvasFactory()206 CanvasFactory::~CanvasFactory()
207 {
208 }
209
210
211 // XServiceInfo
getImplementationName()212 OUString CanvasFactory::getImplementationName()
213 {
214 return "com.sun.star.comp.rendering.CanvasFactory";
215 }
216
supportsService(OUString const & serviceName)217 sal_Bool CanvasFactory::supportsService( OUString const & serviceName )
218 {
219 return cppu::supportsService(this, serviceName);
220 }
221
getSupportedServiceNames()222 Sequence<OUString> CanvasFactory::getSupportedServiceNames()
223 {
224 return { "com.sun.star.rendering.CanvasFactory" };
225 }
226
227 // XMultiComponentFactory
getAvailableServiceNames()228 Sequence<OUString> CanvasFactory::getAvailableServiceNames()
229 {
230 Sequence<OUString> aServiceNames(m_aAvailableImplementations.size());
231 std::transform(m_aAvailableImplementations.begin(),
232 m_aAvailableImplementations.end(),
233 aServiceNames.getArray(),
234 o3tl::select1st< AvailPair >());
235 return aServiceNames;
236 }
237
createInstanceWithContext(OUString const & name,Reference<XComponentContext> const & xContext)238 Reference<XInterface> CanvasFactory::createInstanceWithContext(
239 OUString const & name, Reference<XComponentContext> const & xContext )
240 {
241 return createInstanceWithArgumentsAndContext(
242 name, Sequence<Any>(), xContext );
243 }
244
245
use(OUString const & serviceName,Sequence<Any> const & args,Reference<XComponentContext> const & xContext) const246 Reference<XInterface> CanvasFactory::use(
247 OUString const & serviceName,
248 Sequence<Any> const & args,
249 Reference<XComponentContext> const & xContext ) const
250 {
251 try {
252 return m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
253 serviceName, args, xContext);
254 }
255 catch (css::lang::IllegalArgumentException &)
256 {
257 return Reference<XInterface>();
258 }
259 catch (const RuntimeException &)
260 {
261 throw;
262 }
263 catch (const Exception &)
264 {
265 return Reference<XInterface>();
266 }
267 }
268
269
checkConfigFlag(bool & r_bFlag,bool & r_CacheFlag,const OUString & nodeName) const270 void CanvasFactory::checkConfigFlag( bool& r_bFlag,
271 bool& r_CacheFlag,
272 const OUString& nodeName ) const
273 {
274 if( m_xCanvasConfigNameAccess.is() )
275 {
276 m_xCanvasConfigNameAccess->getByName( nodeName ) >>= r_bFlag;
277
278 if( r_CacheFlag != r_bFlag )
279 {
280 // cache is invalid, because of different order of
281 // elements
282 r_CacheFlag = r_bFlag;
283 m_aCachedImplementations.clear();
284 }
285 }
286 }
287
288
lookupAndUse(OUString const & serviceName,Sequence<Any> const & args,Reference<XComponentContext> const & xContext) const289 Reference<XInterface> CanvasFactory::lookupAndUse(
290 OUString const & serviceName, Sequence<Any> const & args,
291 Reference<XComponentContext> const & xContext ) const
292 {
293 ::osl::MutexGuard guard(m_mutex);
294
295 // forcing last entry from impl list, if config flag set
296 bool bForceLastEntry(false);
297 checkConfigFlag( bForceLastEntry,
298 m_bCacheHasForcedLastImpl,
299 "ForceSafeServiceImpl" );
300
301 // tdf#93870 - force VCL canvas in OpenGL mode for now.
302 #if HAVE_FEATURE_OPENGL
303 if( OpenGLWrapper::isVCLOpenGLEnabled() )
304 bForceLastEntry = true;
305 #endif
306
307 // use anti-aliasing canvas, if config flag set (or not existing)
308 bool bUseAAEntry(true);
309 checkConfigFlag( bUseAAEntry,
310 m_bCacheHasUseAAEntry,
311 "UseAntialiasingCanvas" );
312
313 // use accelerated canvas, if config flag set (or not existing)
314 bool bUseAcceleratedEntry(true);
315 checkConfigFlag( bUseAcceleratedEntry,
316 m_bCacheHasUseAcceleratedEntry,
317 "UseAcceleratedCanvas" );
318
319 // try to reuse last working implementation for given service name
320 const CacheVector::iterator aEnd(m_aCachedImplementations.end());
321 CacheVector::iterator aMatch;
322 if( (aMatch=std::find_if(
323 m_aCachedImplementations.begin(),
324 aEnd,
325 [&serviceName](CachePair const& cp)
326 { return serviceName == cp.first; }
327 )) != aEnd) {
328 Reference<XInterface> xCanvas( use( aMatch->second, args, xContext ) );
329 if(xCanvas.is())
330 return xCanvas;
331 }
332
333 // lookup in available service list
334 const AvailVector::const_iterator aAvailEnd(m_aAvailableImplementations.end());
335 AvailVector::const_iterator aAvailImplsMatch;
336 if( (aAvailImplsMatch=std::find_if(
337 m_aAvailableImplementations.begin(),
338 aAvailEnd,
339 [&serviceName](AvailPair const& ap)
340 { return serviceName == ap.first; }
341 )) == aAvailEnd ) {
342 return Reference<XInterface>();
343 }
344
345 const AvailVector::const_iterator aAAEnd(m_aAAImplementations.end());
346 AvailVector::const_iterator aAAImplsMatch;
347 if( (aAAImplsMatch=std::find_if(
348 m_aAAImplementations.begin(),
349 aAAEnd,
350 [&serviceName](AvailPair const& ap)
351 { return serviceName == ap.first; }
352 )) == aAAEnd) {
353 return Reference<XInterface>();
354 }
355
356 const AvailVector::const_iterator aAccelEnd(m_aAcceleratedImplementations.end());
357 AvailVector::const_iterator aAccelImplsMatch;
358 if( (aAccelImplsMatch=std::find_if(
359 m_aAcceleratedImplementations.begin(),
360 aAccelEnd,
361 [&serviceName](AvailPair const& ap)
362 { return serviceName == ap.first; }
363 )) == aAccelEnd ) {
364 return Reference<XInterface>();
365 }
366
367 const Sequence<OUString> aPreferredImpls( aAvailImplsMatch->second );
368 const OUString* pCurrImpl = aPreferredImpls.getConstArray();
369 const OUString* const pEndImpl = pCurrImpl + aPreferredImpls.getLength();
370
371 const Sequence<OUString> aAAImpls( aAAImplsMatch->second );
372 const OUString* const pFirstAAImpl = aAAImpls.getConstArray();
373 const OUString* const pEndAAImpl = pFirstAAImpl + aAAImpls.getLength();
374
375 const Sequence<OUString> aAccelImpls( aAccelImplsMatch->second );
376 const OUString* const pFirstAccelImpl = aAccelImpls.getConstArray();
377 const OUString* const pEndAccelImpl = pFirstAccelImpl + aAccelImpls.getLength();
378
379 // force last entry from impl list, if config flag set
380 if( bForceLastEntry )
381 pCurrImpl = pEndImpl-1;
382
383 while( pCurrImpl != pEndImpl )
384 {
385 const OUString aCurrName(pCurrImpl->trim());
386
387 // check whether given canvas service is listed in the
388 // sequence of "accelerated canvas implementations"
389 const bool bIsAcceleratedImpl(
390 std::any_of(pFirstAccelImpl,
391 pEndAccelImpl,
392 [&aCurrName](OUString const& src)
393 { return aCurrName == src.trim(); }
394 ));
395
396 // check whether given canvas service is listed in the
397 // sequence of "antialiasing canvas implementations"
398 const bool bIsAAImpl(
399 std::any_of(pFirstAAImpl,
400 pEndAAImpl,
401 [&aCurrName](OUString const& src)
402 { return aCurrName == src.trim(); }
403 ));
404
405 // try to instantiate canvas *only* if either accel and AA
406 // property match preference, *or*, if there's a mismatch, only
407 // go for a less capable canvas (that effectively let those
408 // pour canvas impls still work as fallbacks, should an
409 // accelerated/AA one fail). Property implies configuration:
410 // http://en.wikipedia.org/wiki/Truth_table#Logical_implication
411 if( (!bIsAAImpl || bUseAAEntry) && (!bIsAcceleratedImpl || bUseAcceleratedEntry) )
412 {
413 Reference<XInterface> xCanvas(
414 use( pCurrImpl->trim(), args, xContext ) );
415
416 if(xCanvas.is())
417 {
418 if( aMatch != aEnd )
419 {
420 // cache entry exists, replace dysfunctional
421 // implementation name
422 aMatch->second = pCurrImpl->trim();
423 }
424 else
425 {
426 // new service name, add new cache entry
427 m_aCachedImplementations.emplace_back(serviceName,
428 pCurrImpl->trim());
429 }
430
431 return xCanvas;
432 }
433 }
434
435 ++pCurrImpl;
436 }
437
438 return Reference<XInterface>();
439 }
440
441
createInstanceWithArgumentsAndContext(OUString const & preferredOne,Sequence<Any> const & args,Reference<XComponentContext> const & xContext)442 Reference<XInterface> CanvasFactory::createInstanceWithArgumentsAndContext(
443 OUString const & preferredOne, Sequence<Any> const & args,
444 Reference<XComponentContext> const & xContext )
445 {
446 Reference<XInterface> xCanvas(lookupAndUse(preferredOne, args, xContext));
447 if (!xCanvas.is())
448 // last resort: try service name directly
449 xCanvas = use(preferredOne, args, xContext);
450
451 if (xCanvas.is())
452 {
453 Reference<lang::XServiceName> xServiceName(xCanvas, uno::UNO_QUERY);
454 SAL_INFO("canvas", "using " << (xServiceName.is() ? xServiceName->getServiceName()
455 : OUString("(unknown)")));
456 }
457 return xCanvas;
458 }
459
460 // XMultiServiceFactory
461
createInstance(OUString const & name)462 Reference<XInterface> CanvasFactory::createInstance( OUString const & name )
463 {
464 return createInstanceWithArgumentsAndContext(
465 name, Sequence<Any>(), m_xContext );
466 }
467
468
createInstanceWithArguments(OUString const & name,Sequence<Any> const & args)469 Reference<XInterface> CanvasFactory::createInstanceWithArguments(
470 OUString const & name, Sequence<Any> const & args )
471 {
472 return createInstanceWithArgumentsAndContext(
473 name, args, m_xContext );
474 }
475
476 } // anon namespace
477
478
479 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
com_sun_star_comp_rendering_CanvasFactory_get_implementation(css::uno::XComponentContext * context,css::uno::Sequence<css::uno::Any> const &)480 com_sun_star_comp_rendering_CanvasFactory_get_implementation(css::uno::XComponentContext* context,
481 css::uno::Sequence<css::uno::Any> const &)
482 {
483 return cppu::acquire(new CanvasFactory(context));
484 }
485
486
487 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
488