Coverage Report

Created: 2026-02-14 09:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/canvas/source/factory/cf_service.cxx
Line
Count
Source
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>
35
#include <comphelper/propertysequence.hxx>
36
#include <cppuhelper/implbase.hxx>
37
#include <cppuhelper/supportsservice.hxx>
38
#include <o3tl/functional.hxx>
39
#include <o3tl/string_view.hxx>
40
#include <vcl/skia/SkiaHelper.hxx>
41
#include <comphelper/configuration.hxx>
42
#include <officecfg/Office/Canvas.hxx>
43
44
using namespace ::com::sun::star;
45
using namespace ::com::sun::star::uno;
46
47
48
namespace
49
{
50
51
class CanvasFactory
52
    : public ::cppu::WeakImplHelper< lang::XServiceInfo,
53
                                      lang::XMultiComponentFactory,
54
                                      lang::XMultiServiceFactory >
55
{
56
    typedef std::pair< OUString, Sequence< OUString > > AvailPair;
57
    typedef std::pair< OUString, OUString >             CachePair;
58
    typedef std::vector< AvailPair >                    AvailVector;
59
    typedef std::vector< CachePair >                    CacheVector;
60
61
62
    mutable std::mutex                m_mutex;
63
    Reference<XComponentContext>      m_xContext;
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
                          bool bCurrentConfigValue ) 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
83
public:
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
110
CanvasFactory::CanvasFactory( Reference<XComponentContext> const & xContext ) :
111
1
    m_xContext(xContext),
112
    m_bCacheHasForcedLastImpl(),
113
    m_bCacheHasUseAcceleratedEntry(),
114
    m_bCacheHasUseAAEntry()
115
1
{
116
1
    if (!comphelper::IsFuzzing())
117
0
    {
118
0
        try
119
0
        {
120
            // read out configuration for preferred services:
121
122
0
            Reference<container::XNameAccess> xNameAccess = officecfg::Office::Canvas::CanvasServiceList::get();
123
0
            Reference<container::XHierarchicalNameAccess> xHierarchicalNameAccess(
124
0
                xNameAccess, UNO_QUERY_THROW);
125
126
127
0
            for (auto& serviceName : xNameAccess->getElementNames())
128
0
            {
129
0
                Reference<container::XNameAccess> xEntryNameAccess(
130
0
                    xHierarchicalNameAccess->getByHierarchicalName(serviceName),
131
0
                    UNO_QUERY );
132
133
0
                if( xEntryNameAccess.is() )
134
0
                {
135
0
                    Sequence<OUString> implementationList;
136
0
                    if( xEntryNameAccess->getByName(u"PreferredImplementations"_ustr) >>= implementationList )
137
0
                    {
138
0
                        m_aAvailableImplementations.emplace_back(serviceName, implementationList);
139
0
                    }
140
0
                    if( xEntryNameAccess->getByName(u"AcceleratedImplementations"_ustr) >>= implementationList )
141
0
                    {
142
0
                        m_aAcceleratedImplementations.emplace_back(serviceName, implementationList);
143
0
                    }
144
0
                    if( xEntryNameAccess->getByName(u"AntialiasingImplementations"_ustr) >>= implementationList )
145
0
                    {
146
0
                        m_aAAImplementations.emplace_back(serviceName, implementationList);
147
0
                    }
148
0
                }
149
0
            }
150
0
        }
151
0
        catch (const RuntimeException &)
152
0
        {
153
0
            throw;
154
0
        }
155
0
        catch (const Exception&)
156
0
        {
157
0
        }
158
0
    }
159
160
1
    if (m_aAvailableImplementations.empty())
161
1
    {
162
        // Ugh. Looks like configuration is borked. Fake minimal
163
        // setup.
164
1
        m_aAvailableImplementations.emplace_back(u"com.sun.star.rendering.Canvas"_ustr,
165
1
            Sequence<OUString>{ u"com.sun.star.comp.rendering.Canvas.VCL"_ustr } );
166
167
1
        m_aAvailableImplementations.emplace_back(u"com.sun.star.rendering.SpriteCanvas"_ustr,
168
1
            Sequence<OUString>{ u"com.sun.star.comp.rendering.SpriteCanvas.VCL"_ustr } );
169
1
    }
170
1
}
171
172
CanvasFactory::~CanvasFactory()
173
1
{
174
1
}
175
176
177
// XServiceInfo
178
OUString CanvasFactory::getImplementationName()
179
0
{
180
0
    return u"com.sun.star.comp.rendering.CanvasFactory"_ustr;
181
0
}
182
183
sal_Bool CanvasFactory::supportsService( OUString const & serviceName )
184
0
{
185
0
    return cppu::supportsService(this, serviceName);
186
0
}
187
188
Sequence<OUString> CanvasFactory::getSupportedServiceNames()
189
0
{
190
0
    return { u"com.sun.star.rendering.CanvasFactory"_ustr };
191
0
}
192
193
// XMultiComponentFactory
194
Sequence<OUString> CanvasFactory::getAvailableServiceNames()
195
0
{
196
0
    Sequence<OUString> aServiceNames(m_aAvailableImplementations.size());
197
0
    std::transform(m_aAvailableImplementations.begin(),
198
0
                   m_aAvailableImplementations.end(),
199
0
                   aServiceNames.getArray(),
200
0
                   o3tl::select1st< AvailPair >());
201
0
    return aServiceNames;
202
0
}
203
204
Reference<XInterface> CanvasFactory::createInstanceWithContext(
205
    OUString const & name, Reference<XComponentContext> const & xContext )
206
0
{
207
0
    return createInstanceWithArgumentsAndContext(
208
0
        name, Sequence<Any>(), xContext );
209
0
}
210
211
212
Reference<XInterface> CanvasFactory::use(
213
    OUString const & serviceName,
214
    Sequence<Any> const & args,
215
    Reference<XComponentContext> const & xContext ) const
216
5
{
217
5
    try {
218
5
        return m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
219
5
            serviceName, args, xContext);
220
5
    }
221
5
    catch (css::lang::IllegalArgumentException &)
222
5
    {
223
0
        return Reference<XInterface>();
224
0
    }
225
5
    catch (const RuntimeException &)
226
5
    {
227
0
        throw;
228
0
    }
229
5
    catch (const Exception &)
230
5
    {
231
0
        return Reference<XInterface>();
232
0
    }
233
5
}
234
235
236
void CanvasFactory::checkConfigFlag( bool& r_bFlag,
237
                                     bool& r_CacheFlag,
238
                                     bool bCurrentConfigValue ) const
239
15
{
240
15
    r_bFlag = bCurrentConfigValue;
241
242
15
    if( r_CacheFlag != r_bFlag )
243
0
    {
244
        // cache is invalid, because of different order of
245
        // elements
246
0
        r_CacheFlag = r_bFlag;
247
0
        m_aCachedImplementations.clear();
248
0
    }
249
15
}
250
251
252
Reference<XInterface> CanvasFactory::lookupAndUse(
253
    OUString const & serviceName, Sequence<Any> const & args,
254
    Reference<XComponentContext> const & xContext ) const
255
5
{
256
5
    std::scoped_lock guard(m_mutex);
257
258
    // forcing last entry from impl list, if config flag set
259
5
    bool bForceLastEntry(false);
260
5
    checkConfigFlag( bForceLastEntry,
261
5
                     m_bCacheHasForcedLastImpl,
262
5
                     officecfg::Office::Canvas::ForceSafeServiceImpl::get() );
263
264
    // use anti-aliasing canvas, if config flag set (or not existing)
265
5
    bool bUseAAEntry(true);
266
5
    checkConfigFlag( bUseAAEntry,
267
5
                     m_bCacheHasUseAAEntry,
268
5
                     officecfg::Office::Canvas::UseAntialiasingCanvas::get() );
269
270
    // use accelerated canvas, if config flag set (or not existing)
271
5
    bool bUseAcceleratedEntry(true);
272
5
    checkConfigFlag( bUseAcceleratedEntry,
273
5
                     m_bCacheHasUseAcceleratedEntry,
274
5
                     officecfg::Office::Canvas::UseAcceleratedCanvas::get() );
275
276
    // try to reuse last working implementation for given service name
277
5
    const CacheVector::iterator aEnd(m_aCachedImplementations.end());
278
5
    auto aMatch = std::find_if(
279
5
                    m_aCachedImplementations.begin(),
280
5
                    aEnd,
281
5
                    [&serviceName](CachePair const& cp)
282
5
                    { return serviceName == cp.first; }
283
5
                    );
284
5
    if( aMatch != aEnd ) {
285
0
        Reference<XInterface> xCanvas( use( aMatch->second, args, xContext ) );
286
0
        if(xCanvas.is())
287
0
            return xCanvas;
288
0
    }
289
290
    // lookup in available service list
291
5
    const AvailVector::const_iterator aAvailEnd(m_aAvailableImplementations.end());
292
5
    auto aAvailImplsMatch = std::find_if(
293
5
                    m_aAvailableImplementations.begin(),
294
5
                    aAvailEnd,
295
5
                    [&serviceName](AvailPair const& ap)
296
5
                    { return serviceName == ap.first; }
297
5
                    );
298
5
    if( aAvailImplsMatch == aAvailEnd ) {
299
0
        return Reference<XInterface>();
300
0
    }
301
302
5
    const AvailVector::const_iterator aAAEnd(m_aAAImplementations.end());
303
5
    auto aAAImplsMatch = std::find_if(
304
5
                    m_aAAImplementations.begin(),
305
5
                    aAAEnd,
306
5
                    [&serviceName](AvailPair const& ap)
307
5
                    { return serviceName == ap.first; }
308
5
                    );
309
5
    if( aAAImplsMatch == aAAEnd ) {
310
5
        return Reference<XInterface>();
311
5
    }
312
313
0
    const AvailVector::const_iterator aAccelEnd(m_aAcceleratedImplementations.end());
314
0
    auto aAccelImplsMatch = std::find_if(
315
0
                    m_aAcceleratedImplementations.begin(),
316
0
                    aAccelEnd,
317
0
                    [&serviceName](AvailPair const& ap)
318
0
                    { return serviceName == ap.first; }
319
0
                    );
320
0
    if( aAccelImplsMatch == aAccelEnd ) {
321
0
        return Reference<XInterface>();
322
0
    }
323
324
0
    const Sequence<OUString> aPreferredImpls( aAvailImplsMatch->second );
325
0
    const OUString* pCurrImpl = aPreferredImpls.begin();
326
0
    const OUString* const pEndImpl = aPreferredImpls.end();
327
328
0
    const Sequence<OUString> aAAImpls( aAAImplsMatch->second );
329
330
0
    const Sequence<OUString> aAccelImpls( aAccelImplsMatch->second );
331
332
    // force last entry from impl list, if config flag set
333
0
    if (bForceLastEntry && pCurrImpl != pEndImpl)
334
0
        pCurrImpl = pEndImpl-1;
335
336
0
    for(; pCurrImpl != pEndImpl; ++pCurrImpl)
337
0
    {
338
0
        const OUString aCurrName(pCurrImpl->trim());
339
340
        // Skia works only with vclcanvas.
341
0
        if( SkiaHelper::isVCLSkiaEnabled() && !aCurrName.endsWith(".VCL"))
342
0
            continue;
343
344
        // check whether given canvas service is listed in the
345
        // sequence of "accelerated canvas implementations"
346
0
        const bool bIsAcceleratedImpl(
347
0
            std::any_of(aAccelImpls.begin(), aAccelImpls.end(),
348
0
                         [&aCurrName](OUString const& src)
349
0
                         { return aCurrName == o3tl::trim(src); }
350
0
                ));
351
352
        // check whether given canvas service is listed in the
353
        // sequence of "antialiasing canvas implementations"
354
0
        const bool bIsAAImpl(
355
0
            std::any_of(aAAImpls.begin(), aAAImpls.end(),
356
0
                         [&aCurrName](OUString const& src)
357
0
                         { return aCurrName == o3tl::trim(src); }
358
0
                ));
359
360
        // try to instantiate canvas *only* if either accel and AA
361
        // property match preference, *or*, if there's a mismatch, only
362
        // go for a less capable canvas (that effectively let those
363
        // pour canvas impls still work as fallbacks, should an
364
        // accelerated/AA one fail). Property implies configuration:
365
        // http://en.wikipedia.org/wiki/Truth_table#Logical_implication
366
0
        if( (!bIsAAImpl || bUseAAEntry) && (!bIsAcceleratedImpl || bUseAcceleratedEntry) )
367
0
        {
368
0
            Reference<XInterface> xCanvas(use(aCurrName, args, xContext));
369
370
0
            if(xCanvas.is())
371
0
            {
372
0
                if( aMatch != aEnd )
373
0
                {
374
                    // cache entry exists, replace dysfunctional
375
                    // implementation name
376
0
                    aMatch->second = aCurrName;
377
0
                }
378
0
                else
379
0
                {
380
                    // new service name, add new cache entry
381
0
                    m_aCachedImplementations.emplace_back(serviceName, aCurrName);
382
0
                }
383
384
0
                return xCanvas;
385
0
            }
386
0
        }
387
0
    }
388
389
0
    return Reference<XInterface>();
390
0
}
391
392
393
Reference<XInterface> CanvasFactory::createInstanceWithArgumentsAndContext(
394
    OUString const & preferredOne, Sequence<Any> const & args,
395
    Reference<XComponentContext> const & xContext )
396
5
{
397
5
    Reference<XInterface> xCanvas(lookupAndUse(preferredOne, args, xContext));
398
5
    if (!xCanvas.is())
399
        // last resort: try service name directly
400
5
        xCanvas = use(preferredOne, args, xContext);
401
402
5
    if (xCanvas.is())
403
0
    {
404
0
        Reference<lang::XServiceName> xServiceName(xCanvas, uno::UNO_QUERY);
405
0
        SAL_INFO("canvas", "using " << (xServiceName.is() ? xServiceName->getServiceName()
406
0
                                                          : u"(unknown)"_ustr));
407
0
    }
408
5
    return xCanvas;
409
5
}
410
411
// XMultiServiceFactory
412
413
Reference<XInterface> CanvasFactory::createInstance( OUString const & name )
414
0
{
415
0
    return createInstanceWithArgumentsAndContext(
416
0
        name, Sequence<Any>(), m_xContext );
417
0
}
418
419
420
Reference<XInterface> CanvasFactory::createInstanceWithArguments(
421
    OUString const & name, Sequence<Any> const & args )
422
0
{
423
0
    return createInstanceWithArgumentsAndContext(
424
0
        name, args, m_xContext );
425
0
}
426
427
} // anon namespace
428
429
430
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
431
com_sun_star_comp_rendering_CanvasFactory_get_implementation(css::uno::XComponentContext* context,
432
                                                             css::uno::Sequence<css::uno::Any> const &)
433
1
{
434
1
    return cppu::acquire(new CanvasFactory(context));
435
1
}
436
437
438
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */