Coverage Report

Created: 2025-12-31 10:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sfx2/source/notify/globalevents.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/types.h>
21
22
#include <com/sun/star/task/theJobExecutor.hpp>
23
#include <com/sun/star/container/XNameReplace.hpp>
24
#include <com/sun/star/container/XSet.hpp>
25
#include <com/sun/star/document/XEventListener.hpp>
26
#include <com/sun/star/document/XEventBroadcaster.hpp>
27
#include <com/sun/star/document/XDocumentEventListener.hpp>
28
#include <com/sun/star/frame/XGlobalEventBroadcaster.hpp>
29
#include <com/sun/star/lang/DisposedException.hpp>
30
#include <com/sun/star/lang/NoSupportException.hpp>
31
#include <com/sun/star/lang/XServiceInfo.hpp>
32
#include <com/sun/star/uno/Type.hxx>
33
34
#include <cppuhelper/implbase.hxx>
35
#include <comphelper/interfacecontainer4.hxx>
36
#include <cppuhelper/supportsservice.hxx>
37
#include <comphelper/enumhelper.hxx>
38
#include <rtl/ref.hxx>
39
#include <sfx2/app.hxx>
40
#include <comphelper/diagnose_ex.hxx>
41
#include <unotools/eventcfg.hxx>
42
#include <eventsupplier.hxx>
43
44
#include <mutex>
45
#include <set>
46
#include <vector>
47
48
using namespace css;
49
50
namespace {
51
52
typedef ::std::vector< css::uno::Reference< css::frame::XModel > > TModelList;
53
54
55
//TODO: remove support of obsolete document::XEventBroadcaster/Listener
56
class SfxGlobalEvents_Impl : public ::cppu::WeakImplHelper< css::lang::XServiceInfo
57
                                                           , css::frame::XGlobalEventBroadcaster
58
                                                           , css::document::XEventBroadcaster
59
                                                           , css::document::XEventListener
60
                                                           , css::lang::XComponent
61
                                                            >
62
{
63
    std::mutex m_aLock;
64
    rtl::Reference< GlobalEventConfig > m_xEvents;
65
    css::uno::Reference< css::document::XEventListener > m_xJobExecutorListener;
66
    ::comphelper::OInterfaceContainerHelper4<document::XEventListener> m_aLegacyListeners;
67
    ::comphelper::OInterfaceContainerHelper4<document::XDocumentEventListener> m_aDocumentListeners;
68
    std::multiset<css::uno::Reference<css::lang::XEventListener>> m_disposeListeners;
69
    TModelList m_lModels;
70
    bool m_disposed;
71
72
public:
73
    explicit SfxGlobalEvents_Impl(const css::uno::Reference < css::uno::XComponentContext >& rxContext);
74
75
    virtual OUString SAL_CALL getImplementationName() override
76
0
    {
77
0
        return u"com.sun.star.comp.sfx2.GlobalEventBroadcaster"_ustr;
78
0
    }
79
80
    virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
81
0
    {
82
0
        return cppu::supportsService(this, ServiceName);
83
0
    }
84
85
    virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
86
0
    {
87
0
        css::uno::Sequence< OUString > aSeq { u"com.sun.star.frame.GlobalEventBroadcaster"_ustr };
88
0
        return aSeq;
89
0
    }
90
91
    // css.document.XEventBroadcaster
92
    virtual css::uno::Reference< css::container::XNameReplace > SAL_CALL getEvents() override;
93
94
    virtual void SAL_CALL addEventListener(const css::uno::Reference< css::document::XEventListener >& xListener) override;
95
96
    virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::document::XEventListener >& xListener) override;
97
98
    // css.document.XDocumentEventBroadcaster
99
    virtual void SAL_CALL addDocumentEventListener( const css::uno::Reference< css::document::XDocumentEventListener >& Listener ) override;
100
    virtual void SAL_CALL removeDocumentEventListener( const css::uno::Reference< css::document::XDocumentEventListener >& Listener ) override;
101
    virtual void SAL_CALL notifyDocumentEvent( const OUString& EventName, const css::uno::Reference< css::frame::XController2 >& ViewController, const css::uno::Any& Supplement ) override;
102
103
    // css.document.XEventListener
104
    virtual void SAL_CALL notifyEvent(const css::document::EventObject& aEvent) override;
105
106
    // css.document.XDocumentEventListener
107
    virtual void SAL_CALL documentEventOccured( const css::document::DocumentEvent& Event ) override;
108
109
    // css.container.XSet
110
    virtual sal_Bool SAL_CALL has(const css::uno::Any& aElement) override;
111
112
    virtual void SAL_CALL insert(const css::uno::Any& aElement) override;
113
114
    virtual void SAL_CALL remove(const css::uno::Any& aElement) override;
115
116
    // css.container.XEnumerationAccess
117
    virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override;
118
119
    // css.container.XElementAccess
120
    virtual css::uno::Type SAL_CALL getElementType() override;
121
122
    virtual sal_Bool SAL_CALL hasElements() override;
123
124
    // css.lang.XEventListener
125
    virtual void SAL_CALL disposing(const css::lang::EventObject& aEvent) override;
126
127
    // css.lang.XComponent
128
    void SAL_CALL dispose() override;
129
130
    void SAL_CALL addEventListener(css::uno::Reference<css::lang::XEventListener> const & xListener)
131
        override;
132
133
    void SAL_CALL removeEventListener(
134
        css::uno::Reference<css::lang::XEventListener> const & aListener) override;
135
136
private:
137
138
    // threadsafe
139
    void implts_notifyJobExecution(const css::document::EventObject& aEvent);
140
    void implts_checkAndExecuteEventBindings(const css::document::DocumentEvent& aEvent);
141
    void implts_notifyListener(const css::document::DocumentEvent& aEvent);
142
143
    // not threadsafe
144
    TModelList::iterator impl_searchDoc(const css::uno::Reference< css::frame::XModel >& xModel);
145
};
146
147
SfxGlobalEvents_Impl::SfxGlobalEvents_Impl( const uno::Reference < uno::XComponentContext >& rxContext)
148
3
    : m_xJobExecutorListener( task::theJobExecutor::get( rxContext ), uno::UNO_QUERY_THROW )
149
3
    , m_disposed(false)
150
3
{
151
3
    osl_atomic_increment(&m_refCount);
152
3
    SfxApplication::GetOrCreate();
153
3
    m_xEvents = new GlobalEventConfig();
154
3
    osl_atomic_decrement(&m_refCount);
155
3
}
156
157
uno::Reference< container::XNameReplace > SAL_CALL SfxGlobalEvents_Impl::getEvents()
158
0
{
159
    // SAFE ->
160
0
    std::scoped_lock aLock(m_aLock);
161
0
    if (m_disposed) {
162
0
        throw css::lang::DisposedException();
163
0
    }
164
0
    return m_xEvents;
165
    // <- SAFE
166
0
}
167
168
169
void SAL_CALL SfxGlobalEvents_Impl::addEventListener(const uno::Reference< document::XEventListener >& xListener)
170
0
{
171
0
    std::unique_lock g(m_aLock);
172
0
    if (m_disposed)
173
0
        throw css::lang::DisposedException();
174
0
    m_aLegacyListeners.addInterface(g, xListener);
175
0
}
176
177
178
void SAL_CALL SfxGlobalEvents_Impl::removeEventListener(const uno::Reference< document::XEventListener >& xListener)
179
0
{
180
0
    std::unique_lock g(m_aLock);
181
    // The below removeInterface will silently do nothing when m_disposed
182
0
    m_aLegacyListeners.removeInterface(g, xListener);
183
0
}
184
185
186
void SAL_CALL SfxGlobalEvents_Impl::addDocumentEventListener( const uno::Reference< document::XDocumentEventListener >& Listener )
187
12.1k
{
188
12.1k
    std::unique_lock g(m_aLock);
189
12.1k
    if (m_disposed)
190
0
        throw css::lang::DisposedException();
191
12.1k
    m_aDocumentListeners.addInterface( g, Listener );
192
12.1k
}
193
194
195
void SAL_CALL SfxGlobalEvents_Impl::removeDocumentEventListener( const uno::Reference< document::XDocumentEventListener >& Listener )
196
12.1k
{
197
12.1k
    std::unique_lock g(m_aLock);
198
    // The below removeInterface will silently do nothing when m_disposed:
199
12.1k
    m_aDocumentListeners.removeInterface( g, Listener );
200
12.1k
}
201
202
203
void SAL_CALL SfxGlobalEvents_Impl::notifyDocumentEvent( const OUString& /*_EventName*/,
204
        const uno::Reference< frame::XController2 >& /*_ViewController*/, const uno::Any& /*_Supplement*/ )
205
0
{
206
    // we're a multiplexer only, no chance to generate artificial events here
207
0
    throw lang::NoSupportException(OUString(), *this);
208
0
}
209
210
211
void SAL_CALL SfxGlobalEvents_Impl::notifyEvent(const document::EventObject& aEvent)
212
0
{
213
    // The below implts_* will silently do nothing when m_disposed:
214
0
    document::DocumentEvent aDocEvent(aEvent.Source, aEvent.EventName, nullptr, uno::Any());
215
0
    implts_notifyJobExecution(aEvent);
216
0
    implts_checkAndExecuteEventBindings(aDocEvent);
217
0
    implts_notifyListener(aDocEvent);
218
0
}
219
220
221
void SAL_CALL SfxGlobalEvents_Impl::documentEventOccured( const document::DocumentEvent& Event )
222
0
{
223
    // The below implts_* will silently do nothing when m_disposed:
224
0
    implts_notifyJobExecution(document::EventObject(Event.Source, Event.EventName));
225
0
    implts_checkAndExecuteEventBindings(Event);
226
0
    implts_notifyListener(Event);
227
0
}
228
229
230
void SAL_CALL SfxGlobalEvents_Impl::disposing(const lang::EventObject& aEvent)
231
0
{
232
0
    uno::Reference< frame::XModel > xDoc(aEvent.Source, uno::UNO_QUERY);
233
234
    // SAFE ->
235
0
    std::scoped_lock g(m_aLock);
236
0
    TModelList::iterator pIt = impl_searchDoc(xDoc);
237
0
    if (pIt != m_lModels.end())
238
0
        m_lModels.erase(pIt);
239
    // <- SAFE
240
0
}
241
242
0
void SfxGlobalEvents_Impl::dispose() {
243
0
    std::multiset<css::uno::Reference<css::lang::XEventListener>> listeners;
244
0
    {
245
0
        std::unique_lock g(m_aLock);
246
0
        if (m_disposed)
247
0
            return;
248
0
        m_disposed = true;
249
0
        auto tmpEvents = std::move(m_xEvents);
250
0
        auto tmpModels = std::move(m_lModels);
251
0
        m_xJobExecutorListener.clear();
252
0
        m_disposeListeners.swap(listeners);
253
0
        m_lModels.clear();
254
0
        g.unlock();
255
        // clear events&models outside lock because it will trigger a call back into us
256
0
        tmpEvents.clear();
257
0
        tmpModels.clear();
258
0
        g.lock();
259
0
        m_aLegacyListeners.disposeAndClear(g, {getXWeak()});
260
0
        m_aDocumentListeners.disposeAndClear(g, {getXWeak()});
261
0
    }
262
0
    for (auto const & i: listeners) {
263
0
        try {
264
0
            i->disposing({getXWeak()});
265
0
        } catch (css::lang::DisposedException &) {}
266
0
    }
267
0
}
268
269
void SfxGlobalEvents_Impl::addEventListener(
270
    css::uno::Reference<css::lang::XEventListener> const & xListener)
271
0
{
272
0
    if (!xListener.is()) {
273
0
        throw css::uno::RuntimeException(u"null listener"_ustr);
274
0
    }
275
0
    {
276
0
        std::scoped_lock g(m_aLock);
277
0
        if (!m_disposed) {
278
0
            m_disposeListeners.insert(xListener);
279
0
            return;
280
0
        }
281
0
    }
282
0
    try {
283
0
        xListener->disposing({getXWeak()});
284
0
    } catch (css::lang::DisposedException &) {}
285
0
}
286
287
void SfxGlobalEvents_Impl::removeEventListener(
288
    css::uno::Reference<css::lang::XEventListener> const & aListener)
289
0
{
290
0
    std::scoped_lock g(m_aLock);
291
0
    auto const i = m_disposeListeners.find(aListener);
292
0
    if (i != m_disposeListeners.end()) {
293
0
        m_disposeListeners.erase(i);
294
0
    }
295
0
}
296
297
sal_Bool SAL_CALL SfxGlobalEvents_Impl::has(const uno::Any& aElement)
298
0
{
299
0
    uno::Reference< frame::XModel > xDoc;
300
0
    aElement >>= xDoc;
301
302
0
    bool bHas = false;
303
304
    // SAFE ->
305
0
    std::scoped_lock g(m_aLock);
306
0
    if (m_disposed) {
307
0
        throw css::lang::DisposedException();
308
0
    }
309
0
    TModelList::iterator pIt = impl_searchDoc(xDoc);
310
0
    if (pIt != m_lModels.end())
311
0
        bHas = true;
312
    // <- SAFE
313
314
0
    return bHas;
315
0
}
316
317
318
void SAL_CALL SfxGlobalEvents_Impl::insert( const uno::Any& aElement )
319
0
{
320
0
    uno::Reference< frame::XModel > xDoc;
321
0
    aElement >>= xDoc;
322
0
    if (!xDoc.is())
323
0
        throw lang::IllegalArgumentException(
324
0
                u"Can not locate at least the model parameter."_ustr,
325
0
                static_cast< container::XSet* >(this),
326
0
                0);
327
328
    // SAFE ->
329
0
    {
330
0
        std::scoped_lock g(m_aLock);
331
0
        if (m_disposed) {
332
0
            throw css::lang::DisposedException();
333
0
        }
334
0
        TModelList::iterator pIt = impl_searchDoc(xDoc);
335
0
        if (pIt != m_lModels.end())
336
0
            throw container::ElementExistException(
337
0
                OUString(),
338
0
                static_cast<container::XSet*>(this));
339
0
        m_lModels.push_back(xDoc);
340
0
    }
341
    // <- SAFE
342
343
0
    uno::Reference< document::XDocumentEventBroadcaster > xDocBroadcaster(xDoc, uno::UNO_QUERY );
344
0
    if (xDocBroadcaster.is())
345
0
        xDocBroadcaster->addDocumentEventListener(this);
346
0
    else
347
0
    {
348
        // try the "legacy version" of XDocumentEventBroadcaster, which is XEventBroadcaster
349
0
        uno::Reference< document::XEventBroadcaster > xBroadcaster(xDoc, uno::UNO_QUERY);
350
0
        if (xBroadcaster.is())
351
0
            xBroadcaster->addEventListener(static_cast< document::XEventListener* >(this));
352
0
    }
353
0
}
354
355
356
void SAL_CALL SfxGlobalEvents_Impl::remove( const uno::Any& aElement )
357
0
{
358
0
    uno::Reference< frame::XModel > xDoc;
359
0
    aElement >>= xDoc;
360
0
    if (!xDoc.is())
361
0
        throw lang::IllegalArgumentException(
362
0
                u"Can not locate at least the model parameter."_ustr,
363
0
                static_cast< container::XSet* >(this),
364
0
                0);
365
366
    // SAFE ->
367
0
    {
368
0
        std::scoped_lock g(m_aLock);
369
0
        TModelList::iterator pIt = impl_searchDoc(xDoc);
370
0
        if (pIt == m_lModels.end())
371
0
            throw container::NoSuchElementException(
372
0
                OUString(),
373
0
                static_cast<container::XSet*>(this));
374
0
        m_lModels.erase(pIt);
375
0
    }
376
    // <- SAFE
377
378
0
    uno::Reference< document::XDocumentEventBroadcaster > xDocBroadcaster(xDoc, uno::UNO_QUERY );
379
0
    if (xDocBroadcaster.is())
380
0
        xDocBroadcaster->removeDocumentEventListener(this);
381
0
    else
382
0
    {
383
        // try the "legacy version" of XDocumentEventBroadcaster, which is XEventBroadcaster
384
0
        uno::Reference< document::XEventBroadcaster > xBroadcaster(xDoc, uno::UNO_QUERY);
385
0
        if (xBroadcaster.is())
386
0
            xBroadcaster->removeEventListener(static_cast< document::XEventListener* >(this));
387
0
    }
388
0
}
389
390
391
uno::Reference< container::XEnumeration > SAL_CALL SfxGlobalEvents_Impl::createEnumeration()
392
12.1k
{
393
    // SAFE ->
394
12.1k
    std::scoped_lock g(m_aLock);
395
12.1k
    if (m_disposed) {
396
0
        throw css::lang::DisposedException();
397
0
    }
398
12.1k
    uno::Sequence<uno::Any> models(m_lModels.size());
399
12.1k
    auto modelsRange = asNonConstRange(models);
400
12.1k
    for (size_t i = 0; i < m_lModels.size(); ++i)
401
0
    {
402
0
        modelsRange[i] <<= m_lModels[i];
403
0
    }
404
12.1k
    uno::Reference<container::XEnumeration> xEnum(new ::comphelper::OAnyEnumeration(models));
405
    // <- SAFE
406
407
12.1k
    return xEnum;
408
12.1k
}
409
410
411
uno::Type SAL_CALL SfxGlobalEvents_Impl::getElementType()
412
0
{
413
0
    return cppu::UnoType<frame::XModel>::get();
414
0
}
415
416
417
sal_Bool SAL_CALL SfxGlobalEvents_Impl::hasElements()
418
0
{
419
    // SAFE ->
420
0
    std::scoped_lock g(m_aLock);
421
0
    if (m_disposed) {
422
0
        throw css::lang::DisposedException();
423
0
    }
424
0
    return (!m_lModels.empty());
425
    // <- SAFE
426
0
}
427
428
429
void SfxGlobalEvents_Impl::implts_notifyJobExecution(const document::EventObject& aEvent)
430
0
{
431
0
    css::uno::Reference<css::document::XEventListener> listener;
432
0
    {
433
0
        std::scoped_lock g(m_aLock);
434
0
        listener = m_xJobExecutorListener;
435
0
    }
436
0
    if (!listener.is()) {
437
0
        return;
438
0
    }
439
0
    try
440
0
    {
441
0
        listener->notifyEvent(aEvent);
442
0
    }
443
0
    catch(const uno::RuntimeException&)
444
0
        { throw; }
445
0
    catch(const uno::Exception&)
446
0
        {}
447
0
}
448
449
450
void SfxGlobalEvents_Impl::implts_checkAndExecuteEventBindings(const document::DocumentEvent& aEvent)
451
0
{
452
0
    rtl::Reference<GlobalEventConfig> events;
453
0
    {
454
0
        std::scoped_lock g(m_aLock);
455
0
        events = m_xEvents;
456
0
    }
457
0
    if (!events.is()) {
458
0
        return;
459
0
    }
460
0
    try
461
0
    {
462
0
        if ( events->hasByName( aEvent.EventName ) )
463
0
        {
464
0
            uno::Sequence < beans::PropertyValue > aAny = events->getByName2(aEvent.EventName);
465
0
            SfxEvents_Impl::Execute(aAny, aEvent, nullptr);
466
0
        }
467
0
    }
468
0
    catch ( uno::RuntimeException const & )
469
0
    {
470
0
        throw;
471
0
    }
472
0
    catch ( uno::Exception const & )
473
0
    {
474
0
       DBG_UNHANDLED_EXCEPTION("sfx.notify");
475
0
    }
476
0
}
477
478
479
void SfxGlobalEvents_Impl::implts_notifyListener(const document::DocumentEvent& aEvent)
480
0
{
481
0
    std::unique_lock g(m_aLock);
482
483
0
    document::EventObject aLegacyEvent(aEvent.Source, aEvent.EventName);
484
0
    m_aLegacyListeners.notifyEach(g,
485
0
        &document::XEventListener::notifyEvent, aLegacyEvent,
486
0
        [] { TOOLS_WARN_EXCEPTION("sfx.notify", "ignoring"); });
487
0
    m_aDocumentListeners.notifyEach(g,
488
0
        &document::XDocumentEventListener::documentEventOccured, aEvent,
489
0
        [] { TOOLS_WARN_EXCEPTION("sfx.notify", "ignoring"); });
490
0
}
491
492
493
// not threadsafe ... must be locked from outside!
494
TModelList::iterator SfxGlobalEvents_Impl::impl_searchDoc(const uno::Reference< frame::XModel >& xModel)
495
0
{
496
0
    if (!xModel.is())
497
0
        return m_lModels.end();
498
499
0
    return std::find_if(m_lModels.begin(), m_lModels.end(),
500
0
        [&xModel](const uno::Reference< frame::XModel >& rxModel) {
501
0
            return rxModel == xModel;
502
0
        });
503
0
}
504
505
} // namespace
506
507
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
508
com_sun_star_comp_sfx2_GlobalEventBroadcaster_get_implementation(
509
    css::uno::XComponentContext *context,
510
    css::uno::Sequence<css::uno::Any> const &)
511
3
{
512
3
    return cppu::acquire(new SfxGlobalEvents_Impl(context));
513
3
}
514
515
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */