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