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 <mutex>
25#include <utility>
26#include <vector>
27
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>
38#include <o3tl/functional.hxx>
39#include <o3tl/string_view.hxx>
42
43using namespace ::com::sun::star;
44using namespace ::com::sun::star::uno;
45
46
47namespace
48{
49
50class CanvasFactory
51 : public ::cppu::WeakImplHelper< lang::XServiceInfo,
52 lang::XMultiComponentFactory,
53 lang::XMultiServiceFactory >
54{
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;
59
60
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;
71
72 void checkConfigFlag( bool& r_bFlag,
73 bool& r_CacheFlag,
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;
82
83public:
84 virtual ~CanvasFactory() override;
85 explicit CanvasFactory( Reference<XComponentContext> const & xContext );
86
87 // XServiceInfo
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;
91
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;
102
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;
108};
109
110CanvasFactory::CanvasFactory( Reference<XComponentContext> const & xContext ) :
111 m_xContext(xContext),
112 m_bCacheHasForcedLastImpl(),
113 m_bCacheHasUseAcceleratedEntry(),
114 m_bCacheHasUseAAEntry()
115{
117 {
118 try
119 {
120 // read out configuration for preferred services:
121 Reference<lang::XMultiServiceFactory> xConfigProvider(
122 configuration::theDefaultProvider::get( m_xContext ) );
123
124 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
125 {
126 {"nodepath", uno::Any(OUString("/org.openoffice.Office.Canvas"))}
127 }));
128 m_xCanvasConfigNameAccess.set(
129 xConfigProvider->createInstanceWithArguments(
130 "com.sun.star.configuration.ConfigurationAccess",
131 aArgs ),
132 UNO_QUERY_THROW );
133
134 uno::Sequence<uno::Any> aArgs2(comphelper::InitAnyPropertySequence(
135 {
136 {"nodepath", uno::Any(OUString("/org.openoffice.Office.Canvas/CanvasServiceList"))}
137 }));
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);
144
145 Sequence<OUString> serviceNames = xNameAccess->getElementNames();
146 const OUString* pCurr = serviceNames.getConstArray();
147 const OUString* const pEnd = pCurr + serviceNames.getLength();
148 while( pCurr != pEnd )
149 {
150 Reference<container::XNameAccess> xEntryNameAccess(
151 xHierarchicalNameAccess->getByHierarchicalName(*pCurr),
152 UNO_QUERY );
153
154 if( xEntryNameAccess.is() )
155 {
156 Sequence<OUString> implementationList;
157 if( xEntryNameAccess->getByName("PreferredImplementations") >>= implementationList )
158 {
159 m_aAvailableImplementations.emplace_back(*pCurr,implementationList );
160 }
161 if( xEntryNameAccess->getByName("AcceleratedImplementations") >>= implementationList )
162 {
163 m_aAcceleratedImplementations.emplace_back(*pCurr,implementationList );
164 }
165 if( xEntryNameAccess->getByName("AntialiasingImplementations") >>= implementationList )
166 {
167 m_aAAImplementations.emplace_back(*pCurr,implementationList );
168 }
169
170 }
171
172 ++pCurr;
173 }
174 }
175 catch (const RuntimeException &)
176 {
177 throw;
178 }
179 catch (const Exception&)
180 {
181 }
182 }
183
184 if (m_aAvailableImplementations.empty())
185 {
186 // Ugh. Looks like configuration is borked. Fake minimal
187 // setup.
188 m_aAvailableImplementations.emplace_back(OUString("com.sun.star.rendering.Canvas"),
189 Sequence<OUString>{ "com.sun.star.comp.rendering.Canvas.VCL" } );
190
191 m_aAvailableImplementations.emplace_back(OUString("com.sun.star.rendering.SpriteCanvas"),
192 Sequence<OUString>{ "com.sun.star.comp.rendering.SpriteCanvas.VCL" } );
193 }
194}
195
196CanvasFactory::~CanvasFactory()
197{
198}
199
200
201// XServiceInfo
202OUString CanvasFactory::getImplementationName()
203{
204 return "com.sun.star.comp.rendering.CanvasFactory";
205}
206
207sal_Bool CanvasFactory::supportsService( OUString const & serviceName )
208{
209 return cppu::supportsService(this, serviceName);
210}
211
212Sequence<OUString> CanvasFactory::getSupportedServiceNames()
213{
214 return { "com.sun.star.rendering.CanvasFactory" };
215}
216
217// XMultiComponentFactory
218Sequence<OUString> CanvasFactory::getAvailableServiceNames()
219{
220 Sequence<OUString> aServiceNames(m_aAvailableImplementations.size());
221 std::transform(m_aAvailableImplementations.begin(),
222 m_aAvailableImplementations.end(),
223 aServiceNames.getArray(),
225 return aServiceNames;
226}
227
228Reference<XInterface> CanvasFactory::createInstanceWithContext(
229 OUString const & name, Reference<XComponentContext> const & xContext )
230{
231 return createInstanceWithArgumentsAndContext(
232 name, Sequence<Any>(), xContext );
233}
234
235
236Reference<XInterface> CanvasFactory::use(
237 OUString const & serviceName,
238 Sequence<Any> const & args,
239 Reference<XComponentContext> const & xContext ) const
240{
241 try {
242 return m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
243 serviceName, args, xContext);
244 }
245 catch (css::lang::IllegalArgumentException &)
246 {
247 return Reference<XInterface>();
248 }
249 catch (const RuntimeException &)
250 {
251 throw;
252 }
253 catch (const Exception &)
254 {
255 return Reference<XInterface>();
256 }
257}
258
259
260void CanvasFactory::checkConfigFlag( bool& r_bFlag,
261 bool& r_CacheFlag,
262 const OUString& nodeName ) const
263{
264 if( m_xCanvasConfigNameAccess.is() )
265 {
266 m_xCanvasConfigNameAccess->getByName( nodeName ) >>= r_bFlag;
267
268 if( r_CacheFlag != r_bFlag )
269 {
270 // cache is invalid, because of different order of
271 // elements
272 r_CacheFlag = r_bFlag;
273 m_aCachedImplementations.clear();
274 }
275 }
276}
277
278
279Reference<XInterface> CanvasFactory::lookupAndUse(
280 OUString const & serviceName, Sequence<Any> const & args,
281 Reference<XComponentContext> const & xContext ) const
282{
283 std::scoped_lock guard(m_mutex);
284
285 // forcing last entry from impl list, if config flag set
286 bool bForceLastEntry(false);
287 checkConfigFlag( bForceLastEntry,
288 m_bCacheHasForcedLastImpl,
289 "ForceSafeServiceImpl" );
290
291 // use anti-aliasing canvas, if config flag set (or not existing)
292 bool bUseAAEntry(true);
293 checkConfigFlag( bUseAAEntry,
294 m_bCacheHasUseAAEntry,
295 "UseAntialiasingCanvas" );
296
297 // use accelerated canvas, if config flag set (or not existing)
298 bool bUseAcceleratedEntry(true);
299 checkConfigFlag( bUseAcceleratedEntry,
300 m_bCacheHasUseAcceleratedEntry,
301 "UseAcceleratedCanvas" );
302
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(),
307 aEnd,
308 [&serviceName](CachePair const& cp)
309 { return serviceName == cp.first; }
310 );
311 if( aMatch != aEnd ) {
312 Reference<XInterface> xCanvas( use( aMatch->second, args, xContext ) );
313 if(xCanvas.is())
314 return xCanvas;
315 }
316
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(),
321 aAvailEnd,
322 [&serviceName](AvailPair const& ap)
323 { return serviceName == ap.first; }
324 );
325 if( aAvailImplsMatch == aAvailEnd ) {
326 return Reference<XInterface>();
327 }
328
329 const AvailVector::const_iterator aAAEnd(m_aAAImplementations.end());
330 auto aAAImplsMatch = std::find_if(
331 m_aAAImplementations.begin(),
332 aAAEnd,
333 [&serviceName](AvailPair const& ap)
334 { return serviceName == ap.first; }
335 );
336 if( aAAImplsMatch == aAAEnd ) {
337 return Reference<XInterface>();
338 }
339
340 const AvailVector::const_iterator aAccelEnd(m_aAcceleratedImplementations.end());
341 auto aAccelImplsMatch = std::find_if(
342 m_aAcceleratedImplementations.begin(),
343 aAccelEnd,
344 [&serviceName](AvailPair const& ap)
345 { return serviceName == ap.first; }
346 );
347 if( aAccelImplsMatch == aAccelEnd ) {
348 return Reference<XInterface>();
349 }
350
351 const Sequence<OUString> aPreferredImpls( aAvailImplsMatch->second );
352 const OUString* pCurrImpl = aPreferredImpls.begin();
353 const OUString* const pEndImpl = aPreferredImpls.end();
354
355 const Sequence<OUString> aAAImpls( aAAImplsMatch->second );
356
357 const Sequence<OUString> aAccelImpls( aAccelImplsMatch->second );
358
359 // force last entry from impl list, if config flag set
360 if (bForceLastEntry && pCurrImpl != pEndImpl)
361 pCurrImpl = pEndImpl-1;
362
363 for(; pCurrImpl != pEndImpl; ++pCurrImpl)
364 {
365 const OUString aCurrName(pCurrImpl->trim());
366
367 // Skia works only with vclcanvas.
368 if( SkiaHelper::isVCLSkiaEnabled() && !aCurrName.endsWith(".VCL"))
369 continue;
370
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 == o3tl::trim(src); }
377 ));
378
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 == o3tl::trim(src); }
385 ));
386
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) )
394 {
395 Reference<XInterface> xCanvas(use(aCurrName, args, xContext));
396
397 if(xCanvas.is())
398 {
399 if( aMatch != aEnd )
400 {
401 // cache entry exists, replace dysfunctional
402 // implementation name
403 aMatch->second = aCurrName;
404 }
405 else
406 {
407 // new service name, add new cache entry
408 m_aCachedImplementations.emplace_back(serviceName, aCurrName);
409 }
410
411 return xCanvas;
412 }
413 }
414 }
415
416 return Reference<XInterface>();
417}
418
419
420Reference<XInterface> CanvasFactory::createInstanceWithArgumentsAndContext(
421 OUString const & preferredOne, Sequence<Any> const & args,
422 Reference<XComponentContext> const & xContext )
423{
424 Reference<XInterface> xCanvas(lookupAndUse(preferredOne, args, xContext));
425 if (!xCanvas.is())
426 // last resort: try service name directly
427 xCanvas = use(preferredOne, args, xContext);
428
429 if (xCanvas.is())
430 {
431 Reference<lang::XServiceName> xServiceName(xCanvas, uno::UNO_QUERY);
432 SAL_INFO("canvas", "using " << (xServiceName.is() ? xServiceName->getServiceName()
433 : OUString("(unknown)")));
434 }
435 return xCanvas;
436}
437
438// XMultiServiceFactory
439
440Reference<XInterface> CanvasFactory::createInstance( OUString const & name )
441{
442 return createInstanceWithArgumentsAndContext(
443 name, Sequence<Any>(), m_xContext );
444}
445
446
447Reference<XInterface> CanvasFactory::createInstanceWithArguments(
448 OUString const & name, Sequence<Any> const & args )
449{
450 return createInstanceWithArgumentsAndContext(
451 name, args, m_xContext );
452}
453
454} // anon namespace
455
456
457extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
459 css::uno::Sequence<css::uno::Any> const &)
460{
461 return cppu::acquire(new CanvasFactory(context));
462}
463
464
465/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Reference< XComponentContext > m_xContext
HRESULT createInstance(REFIID iid, Ifc **ppIfc)
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:458
static bool IsFuzzing()
Sequence< OUString > aServiceNames
Mutex m_mutex
#define SAL_INFO(area, stream)
VCL_DLLPUBLIC bool isVCLSkiaEnabled()
css::uno::Sequence< css::uno::Any > InitAnyPropertySequence(::std::initializer_list< ::std::pair< OUString, css::uno::Any > > vInit)
css::uno::Sequence< OUString > getSupportedServiceNames()
OUString getImplementationName()
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
use
unsigned char sal_Bool