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