Coverage Report

Created: 2026-04-09 11:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/unoxml/source/dom/node.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 <node.hxx>
21
22
#include <string.h>
23
24
#include <libxml/xmlstring.h>
25
26
#include <algorithm>
27
28
#include <osl/mutex.hxx>
29
#include <osl/diagnose.h>
30
#include <sal/log.hxx>
31
32
#include <com/sun/star/xml/dom/DOMException.hpp>
33
#include <com/sun/star/xml/dom/events/XMutationEvent.hpp>
34
#include <com/sun/star/xml/sax/FastToken.hpp>
35
36
#include <comphelper/servicehelper.hxx>
37
38
#include "document.hxx"
39
#include "attr.hxx"
40
#include "childlist.hxx"
41
42
#include <eventdispatcher.hxx>
43
44
using namespace css;
45
using namespace css::uno;
46
using namespace css::xml::dom;
47
using namespace css::xml::dom::events;
48
using namespace css::xml::sax;
49
50
namespace DOM
51
{
52
    void pushContext(Context& io_rContext)
53
596k
    {
54
        // Explicitly use a temp. variable.
55
        // Windows/VC++ seems to mess up if .back() is directly passed as
56
        // parameter. i.e. Don't use push_back( .back() );
57
596k
        Context::NamespaceVectorType::value_type aVal = io_rContext.maNamespaces.back();
58
596k
        io_rContext.maNamespaces.push_back(std::move(aVal));
59
596k
    }
60
61
    void popContext(Context& io_rContext)
62
596k
    {
63
596k
        io_rContext.maNamespaces.pop_back();
64
596k
    }
65
66
    void addNamespaces(Context& io_rContext, xmlNodePtr pNode)
67
596k
    {
68
        // add node's namespaces to current context
69
603k
        for (xmlNsPtr pNs = pNode->nsDef; pNs != nullptr; pNs = pNs->next) {
70
6.96k
            const xmlChar *pPrefix = pNs->prefix;
71
            // prefix can be NULL when xmlns attribute is empty (xmlns="")
72
6.96k
            OString prefix(reinterpret_cast<const char*>(pPrefix),
73
6.96k
                           pPrefix ? strlen(reinterpret_cast<const char*>(pPrefix)) : 0);
74
6.96k
            const xmlChar *pHref = pNs->href;
75
6.96k
            OUString val(reinterpret_cast<const char*>(pHref),
76
6.96k
                strlen(reinterpret_cast<const char*>(pHref)),
77
6.96k
                RTL_TEXTENCODING_UTF8);
78
79
6.96k
            Context::NamespaceMapType::iterator aIter=
80
6.96k
                io_rContext.maNamespaceMap.find(val);
81
6.96k
            if( aIter != io_rContext.maNamespaceMap.end() )
82
6.27k
            {
83
6.27k
                Context::Namespace aNS;
84
6.27k
                aNS.maPrefix = prefix;
85
6.27k
                aNS.mnToken = aIter->second;
86
87
6.27k
                io_rContext.maNamespaces.back().push_back(aNS);
88
89
6.27k
                SAL_INFO("unoxml", "Added with token " << aIter->second);
90
6.27k
            }
91
6.96k
        }
92
596k
    }
93
94
    sal_Int32 getToken( const Context& rContext, const char* pToken )
95
1.30M
    {
96
1.30M
        const Sequence<sal_Int8> aSeq( reinterpret_cast<sal_Int8 const *>(pToken), strlen( pToken ) );
97
1.30M
        return rContext.mxTokenHandler->getTokenFromUTF8( aSeq );
98
1.30M
    }
99
100
    sal_Int32 getTokenWithPrefix( const Context& rContext, const char* pPrefix, const char* pName )
101
598k
    {
102
598k
        sal_Int32 nNamespaceToken = FastToken::DONTKNOW;
103
598k
        OString prefix(pPrefix,
104
598k
                       strlen(pPrefix));
105
106
598k
        SAL_INFO("unoxml", "getTokenWithPrefix(): prefix " << pPrefix << ", name " << pName);
107
108
598k
        Context::NamespaceVectorType::value_type::const_iterator aIter;
109
598k
        if( (aIter=std::find_if(rContext.maNamespaces.back().begin(),
110
598k
                                rContext.maNamespaces.back().end(),
111
711k
                                [&prefix](const Context::Namespace &aNamespace){ return aNamespace.getPrefix() == prefix; } )) !=
112
598k
                                            rContext.maNamespaces.back().end() )
113
598k
        {
114
598k
            nNamespaceToken = aIter->mnToken;
115
598k
            sal_Int32 nNameToken = getToken( rContext, pName );
116
598k
            if( nNameToken == FastToken::DONTKNOW )
117
14
                nNamespaceToken = FastToken::DONTKNOW;
118
598k
            else
119
598k
                nNamespaceToken |= nNameToken;
120
598k
        }
121
122
598k
        return nNamespaceToken;
123
598k
    }
124
125
126
    CNode::CNode(CDocument const& rDocument, ::osl::Mutex const& rMutex,
127
                NodeType const& reNodeType, xmlNodePtr const& rpNode)
128
7.54M
        :   m_bUnlinked(false)
129
7.54M
        ,   m_aNodeType(reNodeType)
130
7.54M
        ,   m_aNodePtr(rpNode)
131
        // keep containing document alive
132
        // (but not if this is a document; that would create a leak!)
133
7.54M
        ,   m_xDocument( (m_aNodePtr->type != XML_DOCUMENT_NODE)
134
7.54M
                ? &const_cast<CDocument&>(rDocument) : nullptr )
135
7.54M
        ,   m_rMutex(const_cast< ::osl::Mutex & >(rMutex))
136
7.54M
    {
137
7.54M
        OSL_ASSERT(m_aNodePtr);
138
7.54M
    }
139
140
    void CNode::invalidate()
141
7.54M
    {
142
        //remove from list if this wrapper goes away
143
7.54M
        if (m_aNodePtr != nullptr && m_xDocument.is()) {
144
7.32M
            m_xDocument->RemoveCNode(m_aNodePtr, this);
145
7.32M
        }
146
        // #i113663#: unlinked nodes will not be freed by xmlFreeDoc
147
7.54M
        if (m_bUnlinked) {
148
12.3k
            xmlFreeNode(m_aNodePtr);
149
12.3k
        }
150
7.54M
        m_aNodePtr = nullptr;
151
7.54M
    }
152
153
    CNode::~CNode()
154
7.54M
    {
155
        // if this is the document itself, the mutex is already freed!
156
7.54M
        if (NodeType_DOCUMENT_NODE == m_aNodeType) {
157
219k
            invalidate();
158
7.32M
        } else {
159
7.32M
            ::osl::MutexGuard const g(m_rMutex);
160
7.32M
            invalidate(); // other nodes are still alive so must lock mutex
161
7.32M
        }
162
7.54M
    }
163
164
    CDocument & CNode::GetOwnerDocument()
165
8.14M
    {
166
8.14M
        OSL_ASSERT(m_xDocument.is());
167
8.14M
        return *m_xDocument; // needs overriding in CDocument!
168
8.14M
    }
169
170
171
    static void lcl_nsexchange(
172
            xmlNodePtr const aNode, xmlNsPtr const oldNs, xmlNsPtr const newNs)
173
862k
    {
174
        // recursively exchange any references to oldNs with references to newNs
175
862k
        xmlNodePtr cur = aNode;
176
1.29M
        while (cur != nullptr)
177
431k
        {
178
431k
            if (cur->ns == oldNs)
179
431k
                cur->ns = newNs;
180
431k
            if (cur->type == XML_ELEMENT_NODE)
181
431k
            {
182
431k
                xmlAttrPtr curAttr = cur->properties;
183
431k
                while(curAttr != nullptr)
184
23
                {
185
23
                    if (curAttr->ns == oldNs)
186
0
                        curAttr->ns = newNs;
187
23
                    curAttr = curAttr->next;
188
23
                }
189
431k
                lcl_nsexchange(cur->children, oldNs, newNs);
190
431k
            }
191
431k
            cur = cur->next;
192
431k
        }
193
862k
    }
194
195
    /*static*/ void nscleanup(const xmlNodePtr aNode, const xmlNodePtr aParent)
196
7.12M
    {
197
7.12M
        xmlNodePtr cur = aNode;
198
199
        //handle attributes
200
7.12M
        if (cur != nullptr && cur->type == XML_ELEMENT_NODE)
201
2.59M
        {
202
2.59M
            xmlAttrPtr curAttr = cur->properties;
203
2.83M
            while(curAttr != nullptr)
204
237k
            {
205
237k
                if (curAttr->ns != nullptr)
206
14.2k
                {
207
14.2k
                    xmlNsPtr ns = xmlSearchNs(cur->doc, aParent, curAttr->ns->prefix);
208
14.2k
                    if (ns != nullptr)
209
23
                        curAttr->ns = ns;
210
14.2k
                }
211
237k
                curAttr = curAttr->next;
212
237k
            }
213
2.59M
        }
214
215
11.1M
        while (cur != nullptr)
216
3.99M
        {
217
3.99M
            nscleanup(cur->children, cur);
218
3.99M
            if (cur->ns != nullptr)
219
1.45M
            {
220
1.45M
                xmlNsPtr ns = xmlSearchNs(cur->doc, aParent, cur->ns->prefix);
221
1.45M
                if (ns != nullptr && ns != cur->ns && strcmp(reinterpret_cast<char const *>(ns->href), reinterpret_cast<char const *>(cur->ns->href))==0)
222
431k
                {
223
431k
                    xmlNsPtr curDef = cur->nsDef;
224
431k
                    xmlNsPtr *refp = &(cur->nsDef); // insert point
225
862k
                    while (curDef != nullptr)
226
431k
                    {
227
431k
                        ns = xmlSearchNs(cur->doc, aParent, curDef->prefix);
228
431k
                        if (ns != nullptr && ns != curDef && strcmp(reinterpret_cast<char const *>(ns->href), reinterpret_cast<char const *>(curDef->href))==0)
229
431k
                        {
230
                            // reconnect ns pointers in sub-tree to newly found ns before
231
                            // removing redundant nsdecl to prevent dangling pointers.
232
431k
                            lcl_nsexchange(cur, curDef, ns);
233
431k
                            *refp = curDef->next;
234
431k
                            xmlFreeNs(curDef);
235
431k
                            curDef = *refp;
236
431k
                        } else {
237
0
                            refp = &(curDef->next);
238
0
                            curDef = curDef->next;
239
0
                        }
240
431k
                    }
241
431k
                }
242
1.45M
            }
243
3.99M
            cur = cur->next;
244
3.99M
        }
245
7.12M
    }
246
247
    void CNode::saxify(const Reference< XDocumentHandler >& i_xHandler)
248
0
    {
249
0
        if (!i_xHandler.is()) throw RuntimeException();
250
        // default: do nothing
251
0
    }
252
253
    void CNode::fastSaxify(Context& io_rContext)
254
0
    {
255
0
        if (!io_rContext.mxDocHandler.is()) throw RuntimeException();
256
        // default: do nothing
257
0
    }
258
259
    bool CNode::IsChildTypeAllowed(NodeType const /*nodeType*/, NodeType const*const)
260
0
    {
261
        // default: no children allowed
262
0
        return false;
263
0
    }
264
265
0
    void CNode::checkNoParent(Reference<XNode>const& xNode){
266
0
        if (xNode->getParentNode() != Reference<XNode>(this)){
267
0
            DOMException e;
268
0
            e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
269
0
            throw e;
270
0
        }
271
0
    }
272
3.12M
    void CNode::checkNoParent(const xmlNodePtr pNode){
273
3.12M
        if (pNode->parent != nullptr){
274
0
            DOMException e;
275
0
            e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
276
0
            throw e;
277
0
        }
278
3.12M
    }
279
12.3k
    void CNode::checkSameOwner(Reference<XNode>const& xNode){
280
12.3k
        if (xNode->getOwnerDocument() != getOwnerDocument()) {
281
0
            DOMException e;
282
0
            e.Code = DOMExceptionType_WRONG_DOCUMENT_ERR;
283
0
            throw e;
284
0
        }
285
12.3k
    }
286
287
    /**
288
    Adds the node newChild to the end of the list of children of this node.
289
    */
290
    Reference< XNode > SAL_CALL CNode::appendChild(
291
            Reference< XNode > const& xNewChild)
292
3.12M
    {
293
3.12M
        ::osl::ClearableMutexGuard guard(m_rMutex);
294
295
3.12M
        if (nullptr == m_aNodePtr) { return nullptr; }
296
297
3.12M
        CNode *const pNewChild(dynamic_cast<CNode*>(xNewChild.get()));
298
3.12M
        if (!pNewChild) { throw RuntimeException(); }
299
3.12M
        xmlNodePtr const cur = pNewChild->GetNodePtr();
300
3.12M
        if (!cur) { throw RuntimeException(); }
301
302
        // error checks:
303
        // from other document
304
3.12M
        if (cur->doc != m_aNodePtr->doc) {
305
0
            DOMException e;
306
0
            e.Code = DOMExceptionType_WRONG_DOCUMENT_ERR;
307
0
            throw e;
308
0
        }
309
        // same node
310
3.12M
        if (cur == m_aNodePtr) {
311
0
            DOMException e;
312
0
            e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
313
0
            throw e;
314
0
        }
315
3.12M
        checkNoParent(cur);
316
317
3.12M
        if (!IsChildTypeAllowed(pNewChild->m_aNodeType, nullptr)) {
318
0
            DOMException e;
319
0
            e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
320
0
            throw e;
321
0
        }
322
323
        // check whether this is an attribute node; it needs special handling
324
3.12M
        xmlNodePtr res = nullptr;
325
3.12M
        if (cur->type == XML_ATTRIBUTE_NODE)
326
0
        {
327
0
            xmlChar const*const pChildren((cur->children)
328
0
                    ? cur->children->content
329
0
                    : reinterpret_cast<xmlChar const*>(""));
330
0
            CAttr *const pCAttr(dynamic_cast<CAttr *>(pNewChild));
331
0
            if (!pCAttr) { throw RuntimeException(); }
332
0
            xmlNsPtr const pNs( pCAttr->GetNamespace(m_aNodePtr) );
333
0
            if (pNs) {
334
0
                res = reinterpret_cast<xmlNodePtr>(
335
0
                        xmlNewNsProp(m_aNodePtr, pNs, cur->name, pChildren));
336
0
            } else {
337
0
                res = reinterpret_cast<xmlNodePtr>(
338
0
                        xmlNewProp(m_aNodePtr, cur->name, pChildren));
339
0
            }
340
0
        }
341
3.12M
        else
342
3.12M
        {
343
3.12M
            res = xmlAddChild(m_aNodePtr, cur);
344
345
            // libxml can do optimization when appending nodes.
346
            // if res != cur, something was optimized and the newchild-wrapper
347
            // should be updated
348
3.12M
            if (res && (cur != res)) {
349
2.61k
                pNewChild->invalidate(); // cur has been freed
350
2.61k
            }
351
3.12M
        }
352
353
3.12M
        if (!res) { return nullptr; }
354
355
        // use custom ns cleanup instead of
356
        // xmlReconciliateNs(m_aNodePtr->doc, m_aNodePtr);
357
        // because that will not remove unneeded ns decls
358
3.12M
        nscleanup(res, m_aNodePtr);
359
360
3.12M
        CDocument& rDocument(GetOwnerDocument());
361
3.12M
        ::rtl::Reference<CNode> const pNode = rDocument.GetCNode(res);
362
363
3.12M
        if (!pNode.is()) { return nullptr; }
364
365
3.12M
        pNode->m_bUnlinked = false; // will be deleted by xmlFreeDoc
366
367
3.12M
        if (rDocument.GetEventDispatcher().hasListeners())
368
0
        {
369
            // dispatch DOMNodeInserted event, target is the new node
370
            // this node is the related node
371
            // does bubble
372
0
            Reference< XDocumentEvent > docevent(getOwnerDocument(), UNO_QUERY);
373
0
            Reference< XMutationEvent > event(docevent->createEvent(
374
0
                u"DOMNodeInserted"_ustr), UNO_QUERY);
375
0
            event->initMutationEvent(u"DOMNodeInserted"_ustr, true, false, this,
376
0
                OUString(), OUString(), OUString(), AttrChangeType(0) );
377
378
            // the following dispatch functions use only UNO interfaces
379
            // and call event listeners, so release mutex to prevent deadlocks.
380
0
            guard.clear();
381
382
0
            dispatchEvent(event);
383
            // dispatch subtree modified for this node
384
0
            dispatchSubtreeModified();
385
0
        }
386
387
3.12M
        return pNode;
388
3.12M
    }
389
390
    /**
391
    Returns a duplicate of this node, i.e., serves as a generic copy
392
    constructor for nodes.
393
    */
394
    Reference< XNode > SAL_CALL CNode::cloneNode(sal_Bool bDeep)
395
0
    {
396
0
        ::osl::MutexGuard const g(m_rMutex);
397
398
0
        if (nullptr == m_aNodePtr) {
399
0
            return nullptr;
400
0
        }
401
0
        ::rtl::Reference<CNode> const pNode = GetOwnerDocument().GetCNode(
402
0
            xmlCopyNode(m_aNodePtr, bDeep ? 1 : 0));
403
0
        if (!pNode.is()) { return nullptr; }
404
0
        pNode->m_bUnlinked = true; // not linked yet
405
0
        return pNode;
406
0
    }
407
408
    /**
409
    A NamedNodeMap containing the attributes of this node (if it is an Element)
410
    or null otherwise.
411
    */
412
    Reference< XNamedNodeMap > SAL_CALL CNode::getAttributes()
413
0
    {
414
        // return empty reference; only element node may override this impl
415
0
        return Reference< XNamedNodeMap>();
416
0
    }
417
418
    /**
419
    A NodeList that contains all children of this node.
420
    */
421
    Reference< XNodeList > SAL_CALL CNode::getChildNodes()
422
5.32M
    {
423
5.32M
        ::osl::MutexGuard const g(m_rMutex);
424
425
5.32M
        if (nullptr == m_aNodePtr) {
426
0
            return nullptr;
427
0
        }
428
5.32M
        Reference< XNodeList > const xNodeList(new CChildList(this, m_rMutex));
429
5.32M
        return xNodeList;
430
5.32M
    }
431
432
    /**
433
    The first child of this node.
434
    */
435
    Reference< XNode > SAL_CALL CNode::getFirstChild()
436
261k
    {
437
261k
        ::osl::MutexGuard const g(m_rMutex);
438
439
261k
        if (nullptr == m_aNodePtr) {
440
0
            return nullptr;
441
0
        }
442
261k
        return GetOwnerDocument().GetCNode(m_aNodePtr->children);
443
261k
    }
444
445
    /**
446
    The last child of this node.
447
    */
448
    Reference< XNode > SAL_CALL CNode::getLastChild()
449
0
    {
450
0
        ::osl::MutexGuard const g(m_rMutex);
451
452
0
        if (nullptr == m_aNodePtr) {
453
0
            return nullptr;
454
0
        }
455
0
        return GetOwnerDocument().GetCNode(xmlGetLastChild(m_aNodePtr));
456
0
    }
457
458
    /**
459
    Returns the local part of the qualified name of this node.
460
    */
461
    OUString SAL_CALL CNode::getLocalName()
462
0
    {
463
        // see CElement/CAttr
464
0
        return OUString();
465
0
    }
466
467
468
    /**
469
    The namespace URI of this node, or null if it is unspecified.
470
    */
471
    OUString SAL_CALL CNode::getNamespaceURI()
472
79.5k
    {
473
79.5k
        ::osl::MutexGuard const g(m_rMutex);
474
475
79.5k
        OUString aURI;
476
79.5k
        if (m_aNodePtr != nullptr &&
477
79.5k
            (m_aNodePtr->type == XML_ELEMENT_NODE || m_aNodePtr->type == XML_ATTRIBUTE_NODE) &&
478
79.5k
            m_aNodePtr->ns != nullptr)
479
79.5k
        {
480
79.5k
            const xmlChar* pHref = m_aNodePtr->ns->href;
481
79.5k
            aURI = OUString(reinterpret_cast<char const *>(pHref), strlen(reinterpret_cast<char const *>(pHref)), RTL_TEXTENCODING_UTF8);
482
79.5k
        }
483
79.5k
        return aURI;
484
79.5k
    }
485
486
    /**
487
    The node immediately following this node.
488
    */
489
    Reference< XNode > SAL_CALL CNode::getNextSibling()
490
11
    {
491
11
        ::osl::MutexGuard const g(m_rMutex);
492
493
11
        if (nullptr == m_aNodePtr) {
494
0
            return nullptr;
495
0
        }
496
11
        return GetOwnerDocument().GetCNode(m_aNodePtr->next);
497
11
    }
498
499
    /**
500
    The name of this node, depending on its type; see the table above.
501
    */
502
    OUString SAL_CALL CNode::getNodeName()
503
0
    {
504
        /*
505
        Interface        nodeName               nodeValue                       attributes
506
        --------------------------------------------------------------------------------------
507
        Attr             name of attribute      value of attribute              null
508
        CDATASection     "#cdata-section"       content of the CDATA Section    null
509
        Comment          "#comment"             content of the comment          null
510
        Document         "#document"            null                            null
511
        DocumentFragment "#document-fragment"   null                            null
512
        DocumentType     document type name     null                            null
513
        Element          tag name               null                            NamedNodeMap
514
        Entity           entity name            null                            null
515
        EntityReference  name of entity         null                            null
516
                         referenced
517
        Notation         notation name          null                            null
518
        Processing\      target                 entire content excluding        null
519
        Instruction                             the target
520
        Text             "#text"                content of the text node        null
521
        */
522
0
        return OUString();
523
0
    }
524
525
    /**
526
    A code representing the type of the underlying object, as defined above.
527
    */
528
    NodeType SAL_CALL CNode::getNodeType()
529
376k
    {
530
376k
        ::osl::MutexGuard const g(m_rMutex);
531
532
376k
        return m_aNodeType;
533
376k
    }
534
535
    /**
536
    The value of this node, depending on its type; see the table above.
537
    */
538
    OUString SAL_CALL CNode::getNodeValue()
539
0
    {
540
0
        return OUString();
541
0
    }
542
543
    /**
544
    The Document object associated with this node.
545
    */
546
    Reference< XDocument > SAL_CALL CNode::getOwnerDocument()
547
25.4k
    {
548
25.4k
        ::osl::MutexGuard const g(m_rMutex);
549
550
25.4k
        if (nullptr == m_aNodePtr) {
551
0
            return nullptr;
552
0
        }
553
25.4k
        Reference< XDocument > const xDoc(& GetOwnerDocument());
554
25.4k
        return xDoc;
555
25.4k
    }
556
557
    /**
558
    The parent of this node.
559
    */
560
    Reference< XNode > SAL_CALL CNode::getParentNode()
561
12.3k
    {
562
12.3k
        ::osl::MutexGuard const g(m_rMutex);
563
564
12.3k
        if (nullptr == m_aNodePtr) {
565
0
            return nullptr;
566
0
        }
567
12.3k
        return GetOwnerDocument().GetCNode(m_aNodePtr->parent);
568
12.3k
    }
569
570
    /**
571
    The namespace prefix of this node, or null if it is unspecified.
572
    */
573
    OUString SAL_CALL CNode::getPrefix()
574
296k
    {
575
296k
        ::osl::MutexGuard const g(m_rMutex);
576
577
296k
        OUString aPrefix;
578
296k
        if (m_aNodePtr != nullptr &&
579
296k
            (m_aNodePtr->type == XML_ELEMENT_NODE || m_aNodePtr->type == XML_ATTRIBUTE_NODE) &&
580
296k
            m_aNodePtr->ns != nullptr)
581
180k
        {
582
180k
            const xmlChar* pPrefix = m_aNodePtr->ns->prefix;
583
180k
            if( pPrefix != nullptr )
584
180k
                aPrefix = OUString(reinterpret_cast<char const *>(pPrefix), strlen(reinterpret_cast<char const *>(pPrefix)), RTL_TEXTENCODING_UTF8);
585
180k
        }
586
296k
        return aPrefix;
587
588
296k
    }
589
590
    /**
591
    The node immediately preceding this node.
592
    */
593
    Reference< XNode > SAL_CALL CNode::getPreviousSibling()
594
0
    {
595
0
        ::osl::MutexGuard const g(m_rMutex);
596
597
0
        if (nullptr == m_aNodePtr) {
598
0
            return nullptr;
599
0
        }
600
0
        return GetOwnerDocument().GetCNode(m_aNodePtr->prev);
601
0
    }
602
603
    /**
604
    Returns whether this node (if it is an element) has any attributes.
605
    */
606
    sal_Bool SAL_CALL CNode::hasAttributes()
607
0
    {
608
0
        ::osl::MutexGuard const g(m_rMutex);
609
610
0
        return (m_aNodePtr != nullptr && m_aNodePtr->properties != nullptr);
611
0
    }
612
613
    /**
614
    Returns whether this node has any children.
615
    */
616
    sal_Bool SAL_CALL CNode::hasChildNodes()
617
0
    {
618
0
        ::osl::MutexGuard const g(m_rMutex);
619
620
0
        return (m_aNodePtr != nullptr && m_aNodePtr->children != nullptr);
621
0
    }
622
623
    /**
624
    Inserts the node newChild before the existing child node refChild.
625
    */
626
    Reference< XNode > SAL_CALL CNode::insertBefore(
627
            const Reference< XNode >& newChild, const Reference< XNode >& refChild)
628
0
    {
629
0
        if (!newChild.is() || !refChild.is()) { throw RuntimeException(); }
630
631
0
        checkSameOwner(newChild);
632
633
0
        if (refChild->getParentNode() != Reference< XNode >(this)) {
634
0
            DOMException e;
635
0
            e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
636
0
            throw e;
637
0
        }
638
639
0
        ::osl::ClearableMutexGuard guard(m_rMutex);
640
641
0
        CNode *const pNewNode(dynamic_cast<CNode*>(newChild.get()));
642
0
        CNode *const pRefNode(dynamic_cast<CNode*>(refChild.get()));
643
0
        if (!pNewNode || !pRefNode) { throw RuntimeException(); }
644
0
        xmlNodePtr const pNewChild(pNewNode->GetNodePtr());
645
0
        xmlNodePtr const pRefChild(pRefNode->GetNodePtr());
646
0
        if (!pNewChild || !pRefChild) { throw RuntimeException(); }
647
648
0
        if (pNewChild == m_aNodePtr) {
649
0
            DOMException e;
650
0
            e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
651
0
            throw e;
652
0
        }
653
        // already has parent
654
0
        checkNoParent(pNewChild);
655
656
0
        if (!IsChildTypeAllowed(pNewNode->m_aNodeType, nullptr)) {
657
0
            DOMException e;
658
0
            e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
659
0
            throw e;
660
0
        }
661
662
        // attributes are unordered anyway, so just do appendChild
663
0
        if (XML_ATTRIBUTE_NODE == pNewChild->type) {
664
0
            guard.clear();
665
0
            return appendChild(newChild);
666
0
        }
667
668
0
        xmlNodePtr cur = m_aNodePtr->children;
669
670
        //search child before which to insert
671
0
        while (cur != nullptr)
672
0
        {
673
0
            if (cur == pRefChild) {
674
                // insert before
675
0
                pNewChild->next = cur;
676
0
                pNewChild->prev = cur->prev;
677
0
                cur->prev = pNewChild;
678
0
                if (pNewChild->prev != nullptr) {
679
0
                    pNewChild->prev->next = pNewChild;
680
0
                }
681
0
                pNewChild->parent = cur->parent;
682
0
                if (pNewChild->parent->children == cur) {
683
0
                    pNewChild->parent->children = pNewChild;
684
0
                }
685
                // do not update parent->last here!
686
0
                pNewNode->m_bUnlinked = false; // will be deleted by xmlFreeDoc
687
0
                break;
688
0
            }
689
0
            cur = cur->next;
690
0
        }
691
0
        return refChild;
692
0
    }
693
694
    /**
695
    Tests whether the DOM implementation implements a specific feature and
696
    that feature is supported by this node.
697
    */
698
  sal_Bool SAL_CALL CNode::isSupported(const OUString& /*feature*/, const OUString& /*ver*/)
699
0
    {
700
0
        OSL_ENSURE(false, "CNode::isSupported: not implemented (#i113683#)");
701
0
        return false;
702
0
    }
703
704
    /**
705
    Puts all Text nodes in the full depth of the sub-tree underneath this
706
    Node, including attribute nodes, into a "normal" form where only structure
707
    (e.g., elements, comments, processing instructions, CDATA sections, and
708
    entity references) separates Text nodes, i.e., there are neither adjacent
709
    Text nodes nor empty Text nodes.
710
    */
711
    void SAL_CALL CNode::normalize()
712
0
    {
713
        //XXX combine adjacent text nodes and remove empty ones
714
0
        OSL_ENSURE(false, "CNode::normalize: not implemented (#i113683#)");
715
0
    }
716
717
    /**
718
    Removes the child node indicated by oldChild from the list of children,
719
    and returns it.
720
    */
721
    Reference< XNode > SAL_CALL
722
    CNode::removeChild(const Reference< XNode >& xOldChild)
723
12.3k
    {
724
12.3k
        if (!xOldChild.is()) {
725
0
            throw RuntimeException();
726
0
        }
727
728
12.3k
        checkSameOwner(xOldChild);
729
730
12.3k
        if (xOldChild->getParentNode() != Reference< XNode >(this)) {
731
0
            DOMException e;
732
0
            e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
733
0
            throw e;
734
0
        }
735
736
12.3k
        ::osl::ClearableMutexGuard guard(m_rMutex);
737
738
12.3k
        if (!m_aNodePtr) { throw RuntimeException(); }
739
740
12.3k
        Reference<XNode> xReturn( xOldChild );
741
742
12.3k
        ::rtl::Reference<CNode> const pOld(dynamic_cast<CNode*>(xOldChild.get()));
743
12.3k
        if (!pOld.is()) { throw RuntimeException(); }
744
12.3k
        xmlNodePtr const old = pOld->GetNodePtr();
745
12.3k
        if (!old) { throw RuntimeException(); }
746
747
12.3k
        if( old->type == XML_ATTRIBUTE_NODE )
748
0
        {
749
0
            xmlAttrPtr pAttr = reinterpret_cast<xmlAttrPtr>(old);
750
0
            xmlRemoveProp( pAttr );
751
0
            pOld->invalidate(); // freed by xmlRemoveProp
752
0
            xReturn.clear();
753
0
        }
754
12.3k
        else
755
12.3k
        {
756
12.3k
            xmlUnlinkNode(old);
757
12.3k
            pOld->m_bUnlinked = true;
758
12.3k
        }
759
760
12.3k
        CDocument& rDocument(GetOwnerDocument());
761
12.3k
        if (rDocument.GetEventDispatcher().hasListeners())
762
0
        {
763
            /*DOMNodeRemoved
764
             * Fired when a node is being removed from its parent node.
765
             * This event is dispatched before the node is removed from the tree.
766
             * The target of this event is the node being removed.
767
             *   Bubbles: Yes
768
             *   Cancelable: No
769
             *   Context Info: relatedNode holds the parent node
770
             */
771
0
            Reference< XDocumentEvent > docevent(getOwnerDocument(), UNO_QUERY);
772
0
            Reference< XMutationEvent > event(docevent->createEvent(
773
0
                u"DOMNodeRemoved"_ustr), UNO_QUERY);
774
0
            event->initMutationEvent(u"DOMNodeRemoved"_ustr,
775
0
                true,
776
0
                false,
777
0
                this,
778
0
                OUString(), OUString(), OUString(), AttrChangeType(0) );
779
780
            // the following dispatch functions use only UNO interfaces
781
            // and call event listeners, so release mutex to prevent deadlocks.
782
0
            guard.clear();
783
784
0
            dispatchEvent(event);
785
            // subtree modified for this node
786
0
            dispatchSubtreeModified();
787
0
        }
788
789
12.3k
        return xReturn;
790
12.3k
    }
791
792
    /**
793
    Replaces the child node oldChild with newChild in the list of children,
794
    and returns the oldChild node.
795
    */
796
    Reference< XNode > SAL_CALL CNode::replaceChild(
797
            Reference< XNode > const& xNewChild,
798
            Reference< XNode > const& xOldChild)
799
0
     {
800
0
        if (!xOldChild.is() || !xNewChild.is()) {
801
0
            throw RuntimeException();
802
0
        }
803
804
0
        checkSameOwner(xNewChild);
805
806
0
        if (xOldChild->getParentNode() != Reference< XNode >(this)) {
807
0
            DOMException e;
808
0
            e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
809
0
            throw e;
810
0
        }
811
812
0
        ::osl::ClearableMutexGuard guard(m_rMutex);
813
814
0
        ::rtl::Reference<CNode> const pOldNode(dynamic_cast<CNode*>(xOldChild.get()));
815
0
        ::rtl::Reference<CNode> const pNewNode(dynamic_cast<CNode*>(xNewChild.get()));
816
0
        if (!pOldNode.is() || !pNewNode.is()) { throw RuntimeException(); }
817
0
        xmlNodePtr const pOld = pOldNode->GetNodePtr();
818
0
        xmlNodePtr const pNew = pNewNode->GetNodePtr();
819
0
        if (!pOld || !pNew) { throw RuntimeException(); }
820
821
0
        if (pNew == m_aNodePtr) {
822
0
            DOMException e;
823
0
            e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
824
0
            throw e;
825
0
        }
826
        // already has parent
827
0
        checkNoParent(pNew);
828
829
0
        if (!IsChildTypeAllowed(pNewNode->m_aNodeType, &pOldNode->m_aNodeType)) {
830
0
            DOMException e;
831
0
            e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
832
0
            throw e;
833
0
        }
834
835
0
        if( pOld->type == XML_ATTRIBUTE_NODE )
836
0
        {
837
            // can only replace attribute with attribute
838
0
            if ( pOld->type != pNew->type )
839
0
            {
840
0
                DOMException e;
841
0
                e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
842
0
                throw e;
843
0
            }
844
845
0
            xmlAttrPtr pAttr = reinterpret_cast<xmlAttrPtr>(pOld);
846
0
            xmlRemoveProp( pAttr );
847
0
            pOldNode->invalidate(); // freed by xmlRemoveProp
848
0
            appendChild(xNewChild);
849
0
        }
850
0
        else
851
0
        {
852
853
0
        xmlNodePtr cur = m_aNodePtr->children;
854
        //find old node in child list
855
0
        while (cur != nullptr)
856
0
        {
857
0
            if(cur == pOld)
858
0
            {
859
                // exchange nodes
860
0
                pNew->prev = pOld->prev;
861
0
                if (pNew->prev != nullptr)
862
0
                    pNew->prev->next = pNew;
863
0
                pNew->next = pOld->next;
864
0
                if (pNew->next != nullptr)
865
0
                    pNew->next->prev = pNew;
866
0
                pNew->parent = pOld->parent;
867
0
                assert(pNew->parent && "pNew->parent cannot be NULL here");
868
0
                if(pNew->parent->children == pOld)
869
0
                    pNew->parent->children = pNew;
870
0
                if(pNew->parent->last == pOld)
871
0
                    pNew->parent->last = pNew;
872
0
                pOld->next = nullptr;
873
0
                pOld->prev = nullptr;
874
0
                pOld->parent = nullptr;
875
0
                pOldNode->m_bUnlinked = true;
876
0
                pNewNode->m_bUnlinked = false; // will be deleted by xmlFreeDoc
877
0
            }
878
0
            cur = cur->next;
879
0
        }
880
0
        }
881
882
0
        guard.clear(); // release for calling event handlers
883
0
        dispatchSubtreeModified();
884
885
0
        return xOldChild;
886
0
    }
887
888
    void CNode::dispatchSubtreeModified()
889
0
    {
890
        // only uses UNO interfaces => needs no mutex
891
892
        // dispatch DOMSubtreeModified
893
        // target is _this_ node
894
0
        Reference< XDocumentEvent > docevent(getOwnerDocument(), UNO_QUERY);
895
0
        Reference< XMutationEvent > event(docevent->createEvent(
896
0
            u"DOMSubtreeModified"_ustr), UNO_QUERY);
897
0
        event->initMutationEvent(
898
0
            u"DOMSubtreeModified"_ustr, true,
899
0
            false, Reference< XNode >(),
900
0
            OUString(), OUString(), OUString(), AttrChangeType(0) );
901
0
        dispatchEvent(event);
902
0
    }
903
904
    /**
905
    The value of this node, depending on its type; see the table above.
906
    */
907
    void SAL_CALL CNode::setNodeValue(const OUString& /*nodeValue*/)
908
0
    {
909
        // use specific node implementation
910
        // if we end up down here, something went wrong
911
0
        DOMException e;
912
0
        e.Code = DOMExceptionType_NO_MODIFICATION_ALLOWED_ERR;
913
0
        throw e;
914
0
    }
915
916
    /**
917
    The namespace prefix of this node, or null if it is unspecified.
918
    */
919
    void SAL_CALL CNode::setPrefix(const OUString& prefix)
920
0
    {
921
0
        ::osl::MutexGuard const g(m_rMutex);
922
923
0
        if ((nullptr == m_aNodePtr) ||
924
0
            ((m_aNodePtr->type != XML_ELEMENT_NODE) &&
925
0
             (m_aNodePtr->type != XML_ATTRIBUTE_NODE)))
926
0
        {
927
0
            DOMException e;
928
0
            e.Code = DOMExceptionType_NO_MODIFICATION_ALLOWED_ERR;
929
0
            throw e;
930
0
        }
931
0
        OString o1 = OUStringToOString(prefix, RTL_TEXTENCODING_UTF8);
932
0
        xmlChar const *pBuf = reinterpret_cast<xmlChar const *>(o1.getStr());
933
0
        if (m_aNodePtr != nullptr && m_aNodePtr->ns != nullptr)
934
0
        {
935
0
            xmlFree(const_cast<xmlChar *>(m_aNodePtr->ns->prefix));
936
0
            m_aNodePtr->ns->prefix = xmlStrdup(pBuf);
937
0
        }
938
939
0
    }
940
941
        // --- XEventTarget
942
    void SAL_CALL CNode::addEventListener(const OUString& eventType,
943
        const Reference< css::xml::dom::events::XEventListener >& listener,
944
        sal_Bool useCapture)
945
0
    {
946
0
        ::osl::MutexGuard const g(m_rMutex);
947
948
0
        CDocument & rDocument(GetOwnerDocument());
949
0
        events::CEventDispatcher & rDispatcher(rDocument.GetEventDispatcher());
950
0
        rDispatcher.addListener(m_aNodePtr, eventType, listener, useCapture);
951
0
    }
952
953
    void SAL_CALL CNode::removeEventListener(const OUString& eventType,
954
        const Reference< css::xml::dom::events::XEventListener >& listener,
955
        sal_Bool useCapture)
956
0
    {
957
0
        ::osl::MutexGuard const g(m_rMutex);
958
959
0
        CDocument & rDocument(GetOwnerDocument());
960
0
        events::CEventDispatcher & rDispatcher(rDocument.GetEventDispatcher());
961
0
        rDispatcher.removeListener(m_aNodePtr, eventType, listener, useCapture);
962
0
    }
963
964
    sal_Bool SAL_CALL CNode::dispatchEvent(const Reference< XEvent >& evt)
965
0
    {
966
0
        CDocument * pDocument;
967
0
        events::CEventDispatcher * pDispatcher;
968
0
        xmlNodePtr pNode;
969
0
        {
970
0
            ::osl::MutexGuard const g(m_rMutex);
971
972
0
            pDocument = & GetOwnerDocument();
973
0
            pDispatcher = & pDocument->GetEventDispatcher();
974
0
            pNode = m_aNodePtr;
975
0
        }
976
        // this calls event listeners, do not call with locked mutex
977
0
        pDispatcher->dispatchEvent(*pDocument, m_rMutex, pNode, this, evt);
978
0
        return true;
979
0
    }
980
}
981
982
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */