Coverage Report

Created: 2026-02-14 09:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/unoxml/source/events/eventdispatcher.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 <eventdispatcher.hxx>
21
22
#include <event.hxx>
23
#include <mutationevent.hxx>
24
#include <uievent.hxx>
25
#include <mouseevent.hxx>
26
27
#include "../dom/document.hxx"
28
29
#include <osl/mutex.hxx>
30
31
#include <utility>
32
33
using namespace css::uno;
34
using namespace css::xml::dom;
35
using namespace css::xml::dom::events;
36
37
namespace DOM::events {
38
39
    void CEventDispatcher::addListener(xmlNodePtr pNode, const OUString& aType, const Reference<XEventListener>& aListener, bool bCapture)
40
0
    {
41
0
        TypeListenerMap *const pTMap = bCapture
42
0
            ? (& m_CaptureListeners) : (& m_TargetListeners);
43
44
        // get the multimap for the specified type
45
0
        ListenerMap *pMap = nullptr;
46
0
        auto tIter = pTMap->find(aType);
47
0
        if (tIter == pTMap->end()) {
48
            // the map has to be created
49
0
            auto const pair = pTMap->emplace(aType, ListenerMap());
50
0
            pMap = & pair.first->second;
51
0
        } else {
52
0
            pMap = & tIter->second;
53
0
        }
54
0
        assert(pMap != nullptr);
55
0
        pMap->emplace(pNode, aListener);
56
0
    }
57
58
    void CEventDispatcher::removeListener(xmlNodePtr pNode, const OUString& aType, const Reference<XEventListener>& aListener, bool bCapture)
59
0
    {
60
0
        TypeListenerMap *const pTMap = bCapture
61
0
            ? (& m_CaptureListeners) : (& m_TargetListeners);
62
63
        // get the multimap for the specified type
64
0
        auto tIter = pTMap->find(aType);
65
0
        if (tIter == pTMap->end())
66
0
            return;
67
68
0
        ListenerMap & rMap = tIter->second;
69
        // find listeners of specified type for specified node
70
0
        ListenerMap::iterator iter = rMap.find(pNode);
71
0
        while (iter != rMap.end() && iter->first == pNode)
72
0
        {
73
            // erase all references to specified listener
74
0
            if (iter->second.is() && iter->second == aListener)
75
0
            {
76
0
                iter = rMap.erase(iter);
77
0
            }
78
0
            else
79
0
                ++iter;
80
0
        }
81
0
    }
82
83
    bool CEventDispatcher::hasListeners() const
84
2.93M
    {
85
2.93M
        return !m_CaptureListeners.empty() || !m_TargetListeners.empty();
86
2.93M
    }
87
88
    CEventDispatcher::~CEventDispatcher()
89
195k
    {
90
195k
    }
91
92
    void CEventDispatcher::callListeners(
93
            TypeListenerMap const& rTMap,
94
            xmlNodePtr const pNode,
95
            const OUString& aType, Reference< XEvent > const& xEvent)
96
0
    {
97
        // get the multimap for the specified type
98
0
        TypeListenerMap::const_iterator tIter = rTMap.find(aType);
99
0
        if (tIter != rTMap.end()) {
100
0
            ListenerMap const& rMap = tIter->second;
101
0
            auto iterRange = rMap.equal_range(pNode);
102
0
            for( auto iter = iterRange.first; iter != iterRange.second; ++iter )
103
0
            {
104
0
                if(iter->second.is())
105
0
                    (iter->second)->handleEvent(xEvent);
106
0
            }
107
0
        }
108
0
    }
109
110
    void CEventDispatcher::dispatchEvent(
111
            DOM::CDocument & rDocument, ::osl::Mutex & rMutex,
112
            xmlNodePtr const pNode, Reference<XNode> const& xNode,
113
            Reference< XEvent > const& i_xEvent) const
114
0
    {
115
0
        TypeListenerMap captureListeners;
116
0
        TypeListenerMap targetListeners;
117
0
        {
118
0
            ::osl::MutexGuard g(rMutex);
119
120
0
            captureListeners = m_CaptureListeners;
121
0
            targetListeners = m_TargetListeners;
122
0
        }
123
124
0
        if (captureListeners.empty() && targetListeners.empty())
125
0
            return;
126
127
0
        rtl::Reference<CEvent> pEvent; // pointer to internal event representation
128
129
0
        OUString const aType = i_xEvent->getType();
130
0
        if (aType == "DOMSubtreeModified"          ||
131
0
            aType == "DOMNodeInserted"             ||
132
0
            aType == "DOMNodeRemoved"              ||
133
0
            aType == "DOMNodeRemovedFromDocument"  ||
134
0
            aType == "DOMNodeInsertedIntoDocument" ||
135
0
            aType == "DOMAttrModified"             ||
136
0
            aType == "DOMCharacterDataModified"    )
137
0
        {
138
0
                Reference< XMutationEvent > const aMEvent(i_xEvent,
139
0
                        UNO_QUERY_THROW);
140
                // dispatch a mutation event
141
                // we need to clone the event in order to have complete control
142
                // over the implementation
143
0
                rtl::Reference<CMutationEvent> pMEvent = new CMutationEvent;
144
0
                pMEvent->initMutationEvent(
145
0
                    aType, aMEvent->getBubbles(), aMEvent->getCancelable(),
146
0
                    aMEvent->getRelatedNode(), aMEvent->getPrevValue(),
147
0
                    aMEvent->getNewValue(), aMEvent->getAttrName(),
148
0
                    aMEvent->getAttrChange());
149
0
                pEvent = pMEvent;
150
0
        } else if ( // UIEvent
151
0
            aType == "DOMFocusIn"  ||
152
0
            aType == "DOMFocusOut" ||
153
0
            aType == "DOMActivate" )
154
0
        {
155
0
            Reference< XUIEvent > const aUIEvent(i_xEvent, UNO_QUERY_THROW);
156
0
            rtl::Reference<CUIEvent> pUIEvent = new CUIEvent;
157
0
            pUIEvent->initUIEvent(aType,
158
0
                aUIEvent->getBubbles(), aUIEvent->getCancelable(),
159
0
                aUIEvent->getView(), aUIEvent->getDetail());
160
0
            pEvent = pUIEvent;
161
0
        } else if ( // MouseEvent
162
0
            aType == "click"     ||
163
0
            aType == "mousedown" ||
164
0
            aType == "mouseup"   ||
165
0
            aType == "mouseover" ||
166
0
            aType == "mousemove" ||
167
0
            aType == "mouseout"  )
168
0
        {
169
0
            Reference< XMouseEvent > const aMouseEvent(i_xEvent,
170
0
                    UNO_QUERY_THROW);
171
0
            rtl::Reference<CMouseEvent> pMouseEvent = new CMouseEvent;
172
0
            pMouseEvent->initMouseEvent(aType,
173
0
                aMouseEvent->getBubbles(), aMouseEvent->getCancelable(),
174
0
                aMouseEvent->getView(), aMouseEvent->getDetail(),
175
0
                aMouseEvent->getScreenX(), aMouseEvent->getScreenY(),
176
0
                aMouseEvent->getClientX(), aMouseEvent->getClientY(),
177
0
                aMouseEvent->getCtrlKey(), aMouseEvent->getAltKey(),
178
0
                aMouseEvent->getShiftKey(), aMouseEvent->getMetaKey(),
179
0
                aMouseEvent->getButton(), aMouseEvent->getRelatedTarget());
180
0
            pEvent = pMouseEvent;
181
0
        }
182
0
        else // generic event
183
0
        {
184
0
            pEvent = new CEvent;
185
0
            pEvent->initEvent(
186
0
                aType, i_xEvent->getBubbles(), i_xEvent->getCancelable());
187
0
        }
188
0
        pEvent->m_target.set(xNode, UNO_QUERY_THROW);
189
0
        pEvent->m_currentTarget = i_xEvent->getCurrentTarget();
190
0
        pEvent->m_time = i_xEvent->getTimeStamp();
191
192
        // create the reference to the private event implementation
193
        // that will be dispatched to the listeners
194
0
        Reference< XEvent > const xEvent(pEvent);
195
196
        // build the path from target node to the root
197
0
        typedef std::vector< ::std::pair<Reference<XEventTarget>, xmlNodePtr> >
198
0
            NodeVector_t;
199
0
        NodeVector_t captureVector;
200
0
        {
201
0
            ::osl::MutexGuard g(rMutex);
202
203
0
            xmlNodePtr cur = pNode;
204
0
            while (cur != nullptr)
205
0
            {
206
0
                rtl::Reference< CNode > const xRef(rDocument.GetCNode(cur));
207
0
                captureVector.emplace_back(Reference< XEventTarget >(xRef), cur);
208
0
                cur = cur->parent;
209
0
            }
210
0
        }
211
212
        // the capture vector now holds the node path from target to root
213
        // first we must search for capture listeners in order root to
214
        // to target. after that, any target listeners have to be called
215
        // then bubbeling phase listeners are called in target to root
216
        // order
217
        // start at the root
218
0
        auto rinode = std::as_const(captureVector).rbegin();
219
0
        if (rinode == std::as_const(captureVector).rend())
220
0
            return;
221
222
        // capturing phase:
223
0
        pEvent->m_phase = PhaseType_CAPTURING_PHASE;
224
0
        while (rinode != std::as_const(captureVector).rend())
225
0
        {
226
0
            pEvent->m_currentTarget = rinode->first;
227
0
            callListeners(captureListeners, rinode->second, aType, xEvent);
228
0
            if  (pEvent->m_canceled) return;
229
0
            ++rinode;
230
0
        }
231
232
0
        NodeVector_t::const_iterator inode = captureVector.begin();
233
234
        // target phase
235
0
        pEvent->m_phase = PhaseType_AT_TARGET;
236
0
        pEvent->m_currentTarget = inode->first;
237
0
        callListeners(targetListeners, inode->second, aType, xEvent);
238
0
        if  (pEvent->m_canceled) return;
239
        // bubbeling phase
240
0
        ++inode;
241
0
        if (i_xEvent->getBubbles()) {
242
0
            pEvent->m_phase = PhaseType_BUBBLING_PHASE;
243
0
            while (inode != captureVector.end())
244
0
            {
245
0
                pEvent->m_currentTarget = inode->first;
246
0
                callListeners(targetListeners,
247
0
                        inode->second, aType, xEvent);
248
0
                if  (pEvent->m_canceled) return;
249
0
                ++inode;
250
0
            }
251
0
        }
252
0
    }
253
}
254
255
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */