Coverage Report

Created: 2026-06-30 11:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/unoxml/source/dom/document.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 <com/sun/star/uno/Sequence.h>
21
22
#include "document.hxx"
23
#include "attr.hxx"
24
#include "element.hxx"
25
#include "cdatasection.hxx"
26
#include "documentfragment.hxx"
27
#include "text.hxx"
28
#include "comment.hxx"
29
#include "processinginstruction.hxx"
30
#include "entityreference.hxx"
31
#include "documenttype.hxx"
32
#include "elementlist.hxx"
33
#include "domimplementation.hxx"
34
#include "entity.hxx"
35
#include "notation.hxx"
36
37
#include <event.hxx>
38
#include <mutationevent.hxx>
39
#include <uievent.hxx>
40
#include <mouseevent.hxx>
41
#include <eventdispatcher.hxx>
42
43
#include <string.h>
44
#include <libxml/xmlIO.h>
45
46
#include <osl/diagnose.h>
47
48
#include <com/sun/star/xml/sax/FastToken.hpp>
49
50
using namespace css;
51
using namespace css::io;
52
using namespace css::uno;
53
using namespace css::xml::dom;
54
using namespace css::xml::dom::events;
55
using namespace css::xml::sax;
56
57
namespace DOM
58
{
59
    static xmlNodePtr lcl_getDocumentType(xmlDocPtr const i_pDocument)
60
0
    {
61
        // find the doc type
62
0
        xmlNodePtr cur = i_pDocument->children;
63
0
        while (cur != nullptr)
64
0
        {
65
0
            if ((cur->type == XML_DOCUMENT_TYPE_NODE) ||
66
0
                (cur->type == XML_DTD_NODE)) {
67
0
                    return cur;
68
0
            }
69
0
            cur = cur->next;
70
0
        }
71
0
        return nullptr;
72
0
    }
73
74
    /// get the pointer to the root element node of the document
75
    static xmlNodePtr lcl_getDocumentRootPtr(xmlDocPtr const i_pDocument)
76
147k
    {
77
        // find the document element
78
147k
        xmlNodePtr cur = i_pDocument->children;
79
147k
        while (cur != nullptr)
80
3.04k
        {
81
3.04k
            if (cur->type == XML_ELEMENT_NODE)
82
3.04k
                break;
83
0
            cur = cur->next;
84
0
        }
85
147k
        return cur;
86
147k
    }
87
88
#if defined __GNUC__ && !defined __clang__ && __GNUC__ == 16
89
#pragma GCC diagnostic push
90
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
91
#endif
92
    CDocument::CDocument(xmlDocPtr const pDoc)
93
149k
        : CDocument_Base(*this, m_Mutex,
94
149k
                NodeType_DOCUMENT_NODE, reinterpret_cast<xmlNodePtr>(pDoc))
95
149k
        , m_aDocPtr(pDoc)
96
149k
        , m_pEventDispatcher(new events::CEventDispatcher)
97
149k
    {
98
149k
    }
99
#if defined __GNUC__ && !defined __clang__ && __GNUC__ == 16
100
#pragma GCC diagnostic pop
101
#endif
102
103
    ::rtl::Reference<CDocument> CDocument::CreateCDocument(xmlDocPtr const pDoc)
104
149k
    {
105
149k
        ::rtl::Reference<CDocument> const xDoc(new CDocument(pDoc));
106
        // add the doc itself to its nodemap!
107
149k
        xDoc->m_NodeMap.emplace(
108
149k
                reinterpret_cast<xmlNodePtr>(pDoc),
109
149k
                ::std::make_pair(
110
149k
                    xDoc.get(),
111
149k
                    xDoc.get()));
112
149k
        return xDoc;
113
149k
    }
114
115
    CDocument::~CDocument()
116
149k
    {
117
149k
        ::osl::MutexGuard const g(m_Mutex);
118
#ifdef DBG_UTIL
119
        // node map must be empty now, otherwise CDocument must not die!
120
        for (const auto& rEntry : m_NodeMap)
121
        {
122
            rtl::Reference<CNode> const xNode(rEntry.second.first);
123
            OSL_ENSURE(!xNode.is(),
124
            "CDocument::~CDocument(): ERROR: live node in document node map!");
125
        }
126
#endif
127
149k
        xmlFreeDoc(m_aDocPtr);
128
149k
    }
129
130
131
    events::CEventDispatcher & CDocument::GetEventDispatcher()
132
1.91M
    {
133
1.91M
        return *m_pEventDispatcher;
134
1.91M
    }
135
136
    ::rtl::Reference< CElement > CDocument::GetDocumentElement()
137
0
    {
138
0
        xmlNodePtr const pNode = lcl_getDocumentRootPtr(m_aDocPtr);
139
0
        ::rtl::Reference< CElement > const xRet(
140
0
            dynamic_cast<CElement*>(GetCNode(pNode).get()));
141
0
        return xRet;
142
0
    }
143
144
    void
145
    CDocument::RemoveCNode(xmlNodePtr const pNode, CNode const*const pCNode)
146
4.62M
    {
147
4.62M
        nodemap_t::iterator const i = m_NodeMap.find(pNode);
148
4.62M
        if (i == m_NodeMap.end())
149
0
            return;
150
151
        // #i113681# consider this scenario:
152
        // T1 calls ~CNode
153
        // T2 calls getCNode:    lookup will find i->second->first invalid
154
        //                       so a new CNode is created and inserted
155
        // T1 calls removeCNode: i->second->second now points to a
156
        //                       different CNode instance!
157
158
        // check that the CNode is the right one
159
4.62M
        CNode *const pCurrent = i->second.second;
160
4.62M
        if (pCurrent == pCNode) {
161
4.62M
            m_NodeMap.erase(i);
162
4.62M
        }
163
4.62M
    }
164
165
    /** NB: this is the CNode factory.
166
        it is the only place where CNodes may be instantiated.
167
        all CNodes must be registered at the m_NodeMap.
168
     */
169
    ::rtl::Reference<CNode>
170
    CDocument::GetCNode(xmlNodePtr const pNode, bool const bCreate)
171
6.92M
    {
172
6.92M
        if (nullptr == pNode) {
173
131k
            return nullptr;
174
131k
        }
175
        //check whether there is already an instance for this node
176
6.79M
        nodemap_t::const_iterator const i = m_NodeMap.find(pNode);
177
6.79M
        if (i != m_NodeMap.end()) {
178
            // #i113681# check that the CNode is still alive
179
2.17M
            rtl::Reference<CNode> const xNode(i->second.first);
180
2.17M
            if (xNode.is())
181
2.17M
            {
182
2.17M
                return xNode;
183
2.17M
            }
184
2.17M
        }
185
186
4.62M
        if (!bCreate) { return nullptr; }
187
188
        // there is not yet an instance wrapping this node,
189
        // create it and store it in the map
190
191
4.62M
        ::rtl::Reference<CNode> pCNode;
192
4.62M
        switch (pNode->type)
193
4.62M
        {
194
2.75M
            case XML_ELEMENT_NODE:
195
                // m_aNodeType = NodeType::ELEMENT_NODE;
196
2.75M
                pCNode = new CElement(*this, m_Mutex, pNode);
197
2.75M
            break;
198
1.13M
            case XML_TEXT_NODE:
199
                // m_aNodeType = NodeType::TEXT_NODE;
200
1.13M
                pCNode = new CText(*this, m_Mutex, pNode);
201
1.13M
            break;
202
0
            case XML_CDATA_SECTION_NODE:
203
                // m_aNodeType = NodeType::CDATA_SECTION_NODE;
204
0
                pCNode = new CCDATASection(*this, m_Mutex, pNode);
205
0
            break;
206
0
            case XML_ENTITY_REF_NODE:
207
                // m_aNodeType = NodeType::ENTITY_REFERENCE_NODE;
208
0
                pCNode = new CEntityReference(*this, m_Mutex, pNode);
209
0
            break;
210
0
            case XML_ENTITY_NODE:
211
                // m_aNodeType = NodeType::ENTITY_NODE;
212
0
                pCNode = new CEntity(*this, m_Mutex, reinterpret_cast<xmlEntityPtr>(pNode));
213
0
            break;
214
0
            case XML_PI_NODE:
215
                // m_aNodeType = NodeType::PROCESSING_INSTRUCTION_NODE;
216
0
                pCNode = new CProcessingInstruction(*this, m_Mutex, pNode);
217
0
            break;
218
0
            case XML_COMMENT_NODE:
219
                // m_aNodeType = NodeType::COMMENT_NODE;
220
0
                pCNode = new CComment(*this, m_Mutex, pNode);
221
0
            break;
222
0
            case XML_DOCUMENT_NODE:
223
                // m_aNodeType = NodeType::DOCUMENT_NODE;
224
0
                OSL_ENSURE(false, "CDocument::GetCNode is not supposed to"
225
0
                        " create a CDocument!!!");
226
0
                pCNode = new CDocument(reinterpret_cast<xmlDocPtr>(pNode));
227
0
            break;
228
0
            case XML_DOCUMENT_TYPE_NODE:
229
0
            case XML_DTD_NODE:
230
                // m_aNodeType = NodeType::DOCUMENT_TYPE_NODE;
231
0
                pCNode = new CDocumentType(*this, m_Mutex, reinterpret_cast<xmlDtdPtr>(pNode));
232
0
            break;
233
0
            case XML_DOCUMENT_FRAG_NODE:
234
                // m_aNodeType = NodeType::DOCUMENT_FRAGMENT_NODE;
235
0
                pCNode = new CDocumentFragment(*this, m_Mutex, pNode);
236
0
            break;
237
0
            case XML_NOTATION_NODE:
238
                // m_aNodeType = NodeType::NOTATION_NODE;
239
0
                pCNode = new CNotation(*this, m_Mutex, reinterpret_cast<xmlNotationPtr>(pNode));
240
0
            break;
241
723k
            case XML_ATTRIBUTE_NODE:
242
                // m_aNodeType = NodeType::ATTRIBUTE_NODE;
243
723k
                pCNode = new CAttr(*this, m_Mutex, reinterpret_cast<xmlAttrPtr>(pNode));
244
723k
            break;
245
            // unsupported node types
246
0
            case XML_HTML_DOCUMENT_NODE:
247
0
            case XML_ELEMENT_DECL:
248
0
            case XML_ATTRIBUTE_DECL:
249
0
            case XML_ENTITY_DECL:
250
0
            case XML_NAMESPACE_DECL:
251
0
            default:
252
0
            break;
253
4.62M
        }
254
255
4.62M
        if (pCNode != nullptr) {
256
4.62M
            bool const bInserted = m_NodeMap.emplace(
257
4.62M
                        pNode,
258
4.62M
                        ::std::make_pair(pCNode.get(), pCNode.get())
259
4.62M
                ).second;
260
4.62M
            OSL_ASSERT(bInserted);
261
4.62M
            if (!bInserted) {
262
                // if insertion failed, delete new instance and return null
263
0
                return nullptr;
264
0
            }
265
4.62M
        }
266
267
4.62M
        OSL_ENSURE(pCNode.is(), "no node produced during CDocument::GetCNode!");
268
4.62M
        return pCNode;
269
4.62M
    }
270
271
272
    CDocument & CDocument::GetOwnerDocument()
273
278k
    {
274
278k
        return *this;
275
278k
    }
276
277
    void CDocument::saxify(const Reference< XDocumentHandler >& i_xHandler)
278
104
    {
279
104
        i_xHandler->startDocument();
280
104
        for (xmlNodePtr pChild = m_aNodePtr->children;
281
208
                        pChild != nullptr; pChild = pChild->next) {
282
104
            ::rtl::Reference<CNode> const pNode = GetCNode(pChild);
283
104
            OSL_ENSURE(pNode != nullptr, "CNode::get returned 0");
284
104
            pNode->saxify(i_xHandler);
285
104
        }
286
104
        i_xHandler->endDocument();
287
104
    }
288
289
    void CDocument::fastSaxify( Context& rContext )
290
2.94k
    {
291
2.94k
        rContext.mxDocHandler->startDocument();
292
2.94k
        for (xmlNodePtr pChild = m_aNodePtr->children;
293
5.88k
                        pChild != nullptr; pChild = pChild->next) {
294
2.94k
            ::rtl::Reference<CNode> const pNode = GetCNode(pChild);
295
2.94k
            OSL_ENSURE(pNode != nullptr, "CNode::get returned 0");
296
2.94k
            pNode->fastSaxify(rContext);
297
2.94k
        }
298
2.94k
        rContext.mxDocHandler->endDocument();
299
2.94k
    }
300
301
    bool CDocument::IsChildTypeAllowed(NodeType const nodeType, NodeType const*const pReplacedNodeType)
302
144k
    {
303
144k
        switch (nodeType) {
304
0
            case NodeType_PROCESSING_INSTRUCTION_NODE:
305
0
            case NodeType_COMMENT_NODE:
306
0
                return true;
307
144k
            case NodeType_ELEMENT_NODE:
308
                 // there may be only one!
309
144k
                return (pReplacedNodeType && *pReplacedNodeType == nodeType)
310
144k
                    || nullptr == lcl_getDocumentRootPtr(m_aDocPtr);
311
0
            case NodeType_DOCUMENT_TYPE_NODE:
312
                 // there may be only one!
313
0
                return (pReplacedNodeType && *pReplacedNodeType == nodeType)
314
0
                    || nullptr == lcl_getDocumentType(m_aDocPtr);
315
0
            default:
316
0
                return false;
317
144k
        }
318
144k
    }
319
320
321
    void SAL_CALL CDocument::addListener(const Reference< XStreamListener >& aListener )
322
0
    {
323
0
        ::osl::MutexGuard const g(m_Mutex);
324
325
0
        m_streamListeners.insert(aListener);
326
0
    }
327
328
    void SAL_CALL CDocument::removeListener(const Reference< XStreamListener >& aListener )
329
0
    {
330
0
        ::osl::MutexGuard const g(m_Mutex);
331
332
0
        m_streamListeners.erase(aListener);
333
0
    }
334
335
    namespace {
336
337
    // IO context functions for libxml2 interaction
338
    struct IOContext {
339
        Reference< XOutputStream > stream;
340
        bool allowClose;
341
    };
342
343
    }
344
345
    extern "C" {
346
    // write callback
347
    // int xmlOutputWriteCallback (void * context, const char * buffer, int len)
348
0
    static int writeCallback(void *context, const char* buffer, int len){
349
        // create a sequence and write it to the stream
350
0
        IOContext *pContext = static_cast<IOContext*>(context);
351
0
        Sequence<sal_Int8> bs(reinterpret_cast<const sal_Int8*>(buffer), len);
352
0
        pContext->stream->writeBytes(bs);
353
0
        return len;
354
0
    }
355
356
    // close callback
357
    //int xmlOutputCloseCallback (void * context)
358
    static int closeCallback(void *context)
359
0
    {
360
0
        IOContext *pContext = static_cast<IOContext*>(context);
361
0
        if (pContext->allowClose) {
362
0
            pContext->stream->closeOutput();
363
0
        }
364
0
        return 0;
365
0
    }
366
    } // extern "C"
367
368
    void SAL_CALL CDocument::start()
369
0
    {
370
0
        listenerlist_t streamListeners;
371
0
        {
372
0
            ::osl::MutexGuard const g(m_Mutex);
373
374
0
            if (! m_rOutputStream.is()) { throw RuntimeException(); }
375
0
            streamListeners = m_streamListeners;
376
0
        }
377
378
        // notify listeners about start
379
0
        for (const Reference< XStreamListener >& aListener : streamListeners) {
380
0
            aListener->started();
381
0
        }
382
383
0
        {
384
0
            ::osl::MutexGuard const g(m_Mutex);
385
386
            // check again! could have been reset...
387
0
            if (! m_rOutputStream.is()) { throw RuntimeException(); }
388
389
            // setup libxml IO and write data to output stream
390
0
            IOContext ioctx = {m_rOutputStream, false};
391
0
            xmlOutputBufferPtr pOut = xmlOutputBufferCreateIO(
392
0
                writeCallback, closeCallback, &ioctx, nullptr);
393
0
            xmlSaveFileTo(pOut, m_aNodePtr->doc, nullptr);
394
0
        }
395
396
        // call listeners
397
0
        for (const Reference< XStreamListener >& aListener : streamListeners) {
398
0
            aListener->closed();
399
0
        }
400
0
    }
401
402
    void SAL_CALL CDocument::terminate()
403
0
    {
404
        // not supported
405
0
    }
406
407
    void SAL_CALL CDocument::setOutputStream( const Reference< XOutputStream >& aStream )
408
0
    {
409
0
        ::osl::MutexGuard const g(m_Mutex);
410
411
0
        m_rOutputStream = aStream;
412
0
    }
413
414
    Reference< XOutputStream > SAL_CALL  CDocument::getOutputStream()
415
0
    {
416
0
        ::osl::MutexGuard const g(m_Mutex);
417
418
0
        return m_rOutputStream;
419
0
    }
420
421
    // Creates an Attr of the given name.
422
    Reference< XAttr > SAL_CALL CDocument::createAttribute(const OUString& name)
423
0
    {
424
0
        ::osl::MutexGuard const g(m_Mutex);
425
426
0
        OString o1 = OUStringToOString(name, RTL_TEXTENCODING_UTF8);
427
0
        xmlChar const *pName = reinterpret_cast<xmlChar const *>(o1.getStr());
428
0
        xmlAttrPtr const pAttr = xmlNewDocProp(m_aDocPtr, pName, nullptr);
429
0
        ::rtl::Reference< CAttr > const pCAttr(
430
0
            dynamic_cast< CAttr* >(GetCNode(
431
0
                    reinterpret_cast<xmlNodePtr>(pAttr)).get()));
432
0
        if (!pCAttr.is()) { throw RuntimeException(); }
433
0
        pCAttr->m_bUnlinked = true;
434
0
        return pCAttr;
435
0
    };
436
437
    // Creates an attribute of the given qualified name and namespace URI.
438
    Reference< XAttr > SAL_CALL CDocument::createAttributeNS(
439
            const OUString& ns, const OUString& qname)
440
0
    {
441
0
        ::osl::MutexGuard const g(m_Mutex);
442
443
        // libxml does not allow a NS definition to be attached to an
444
        // attribute node - which is a good thing, since namespaces are
445
        // only defined as parts of element nodes
446
        // thus the namespace data is stored in CAttr::m_pNamespace
447
0
        sal_Int32 i = qname.indexOf(':');
448
0
        OString oPrefix, oName, oUri;
449
0
        if (i != -1)
450
0
        {
451
0
            oPrefix = OUStringToOString(qname.subView(0, i), RTL_TEXTENCODING_UTF8);
452
0
            oName = OUStringToOString(qname.subView(i+1), RTL_TEXTENCODING_UTF8);
453
0
        }
454
0
        else
455
0
        {
456
0
            oName = OUStringToOString(qname, RTL_TEXTENCODING_UTF8);
457
0
        }
458
0
        oUri = OUStringToOString(ns, RTL_TEXTENCODING_UTF8);
459
0
        xmlAttrPtr const pAttr = xmlNewDocProp(m_aDocPtr,
460
0
                reinterpret_cast<xmlChar const*>(oName.getStr()), nullptr);
461
0
        ::rtl::Reference< CAttr > const pCAttr(
462
0
            dynamic_cast< CAttr* >(GetCNode(
463
0
                    reinterpret_cast<xmlNodePtr>(pAttr)).get()));
464
0
        if (!pCAttr.is()) { throw RuntimeException(); }
465
        // store the namespace data!
466
0
        pCAttr->m_oNamespace.emplace( oUri, oPrefix );
467
0
        pCAttr->m_bUnlinked = true;
468
469
0
        return pCAttr;
470
0
    };
471
472
    // Creates a CDATASection node whose value is the specified string.
473
    Reference< XCDATASection > SAL_CALL CDocument::createCDATASection(const OUString& data)
474
0
    {
475
0
        ::osl::MutexGuard const g(m_Mutex);
476
477
0
        OString const oData(
478
0
                OUStringToOString(data, RTL_TEXTENCODING_UTF8));
479
0
        xmlChar const*const pData =
480
0
            reinterpret_cast<xmlChar const*>(oData.getStr());
481
0
        xmlNodePtr const pText =
482
0
            xmlNewCDataBlock(m_aDocPtr, pData, oData.getLength());
483
0
        Reference< XCDATASection > const xRet(
484
0
            static_cast< XNode* >(GetCNode(pText).get()),
485
0
            UNO_QUERY_THROW);
486
0
        return xRet;
487
0
    }
488
489
    // Creates a Comment node given the specified string.
490
    Reference< XComment > SAL_CALL CDocument::createComment(const OUString& data)
491
0
    {
492
0
        ::osl::MutexGuard const g(m_Mutex);
493
494
0
        OString o1 = OUStringToOString(data, RTL_TEXTENCODING_UTF8);
495
0
        xmlChar const *pData = reinterpret_cast<xmlChar const *>(o1.getStr());
496
0
        xmlNodePtr pComment = xmlNewDocComment(m_aDocPtr, pData);
497
0
        Reference< XComment > const xRet(
498
0
            static_cast< XNode* >(GetCNode(pComment).get()),
499
0
            UNO_QUERY_THROW);
500
0
        return xRet;
501
0
    }
502
503
    //Creates an empty DocumentFragment object.
504
    Reference< XDocumentFragment > SAL_CALL CDocument::createDocumentFragment()
505
0
    {
506
0
        ::osl::MutexGuard const g(m_Mutex);
507
508
0
        xmlNodePtr pFrag = xmlNewDocFragment(m_aDocPtr);
509
0
        Reference< XDocumentFragment > const xRet(
510
0
            static_cast< XNode* >(GetCNode(pFrag).get()),
511
0
            UNO_QUERY_THROW);
512
0
        return xRet;
513
0
    }
514
515
    // Creates an element of the type specified.
516
    Reference< XElement > SAL_CALL CDocument::createElement(const OUString& tagName)
517
740k
    {
518
740k
        ::osl::MutexGuard const g(m_Mutex);
519
520
740k
        OString o1 = OUStringToOString(tagName, RTL_TEXTENCODING_UTF8);
521
740k
        xmlChar const *pName = reinterpret_cast<xmlChar const *>(o1.getStr());
522
740k
        xmlNodePtr const pNode = xmlNewDocNode(m_aDocPtr, nullptr, pName, nullptr);
523
740k
        Reference< XElement > const xRet(
524
740k
            static_cast< XNode* >(GetCNode(pNode).get()),
525
740k
            UNO_QUERY_THROW);
526
740k
        return xRet;
527
740k
    }
528
529
    // Creates an element of the given qualified name and namespace URI.
530
    Reference< XElement > SAL_CALL CDocument::createElementNS(
531
            const OUString& ns, const OUString& qname)
532
524k
    {
533
524k
        ::osl::MutexGuard const g(m_Mutex);
534
535
524k
        sal_Int32 i = qname.indexOf(':');
536
524k
        if (ns.isEmpty()) throw RuntimeException();
537
524k
        xmlChar const *pPrefix;
538
524k
        xmlChar const *pName;
539
524k
        OString o1, o2, o3;
540
524k
        if ( i != -1) {
541
469k
            o1 = OUStringToOString(qname.subView(0, i), RTL_TEXTENCODING_UTF8);
542
469k
            pPrefix = reinterpret_cast<xmlChar const *>(o1.getStr());
543
469k
            o2 = OUStringToOString(qname.subView(i+1), RTL_TEXTENCODING_UTF8);
544
469k
            pName = reinterpret_cast<xmlChar const *>(o2.getStr());
545
469k
        } else {
546
            // default prefix
547
54.7k
            pPrefix = reinterpret_cast<xmlChar const *>("");
548
54.7k
            o2 = OUStringToOString(qname, RTL_TEXTENCODING_UTF8);
549
54.7k
            pName = reinterpret_cast<xmlChar const *>(o2.getStr());
550
54.7k
        }
551
524k
        o3 = OUStringToOString(ns, RTL_TEXTENCODING_UTF8);
552
524k
        xmlChar const *pUri = reinterpret_cast<xmlChar const *>(o3.getStr());
553
554
        // xmlNsPtr aNsPtr = xmlNewReconciledNs?
555
        // xmlNsPtr aNsPtr = xmlNewGlobalNs?
556
524k
        xmlNodePtr const pNode = xmlNewDocNode(m_aDocPtr, nullptr, pName, nullptr);
557
524k
        xmlNsPtr const pNs = xmlNewNs(pNode, pUri, pPrefix);
558
524k
        xmlSetNs(pNode, pNs);
559
524k
        Reference< XElement > const xRet(
560
524k
            static_cast< XNode* >(GetCNode(pNode).get()),
561
524k
            UNO_QUERY_THROW);
562
524k
        return xRet;
563
524k
    }
564
565
    //Creates an EntityReference object.
566
    Reference< XEntityReference > SAL_CALL CDocument::createEntityReference(const OUString& name)
567
0
    {
568
0
        ::osl::MutexGuard const g(m_Mutex);
569
570
0
        OString o1 = OUStringToOString(name, RTL_TEXTENCODING_UTF8);
571
0
        xmlChar const *pName = reinterpret_cast<xmlChar const *>(o1.getStr());
572
0
        xmlNodePtr const pNode = xmlNewReference(m_aDocPtr, pName);
573
0
        Reference< XEntityReference > const xRet(
574
0
            static_cast< XNode* >(GetCNode(pNode).get()),
575
0
            UNO_QUERY_THROW);
576
0
        return xRet;
577
0
    }
578
579
    // Creates a ProcessingInstruction node given the specified name and
580
    // data strings.
581
    Reference< XProcessingInstruction > SAL_CALL CDocument::createProcessingInstruction(
582
            const OUString& target, const OUString& data)
583
0
    {
584
0
        ::osl::MutexGuard const g(m_Mutex);
585
586
0
        OString o1 = OUStringToOString(target, RTL_TEXTENCODING_UTF8);
587
0
        xmlChar const *pTarget = reinterpret_cast<xmlChar const *>(o1.getStr());
588
0
        OString o2 = OUStringToOString(data, RTL_TEXTENCODING_UTF8);
589
0
        xmlChar const *pData = reinterpret_cast<xmlChar const *>(o2.getStr());
590
0
        xmlNodePtr const pNode = xmlNewDocPI(m_aDocPtr, pTarget, pData);
591
0
        pNode->doc = m_aDocPtr;
592
0
        Reference< XProcessingInstruction > const xRet(
593
0
            static_cast< XNode* >(GetCNode(pNode).get()),
594
0
            UNO_QUERY_THROW);
595
0
        return xRet;
596
0
    }
597
598
    // Creates a Text node given the specified string.
599
    Reference< XText > SAL_CALL CDocument::createTextNode(const OUString& data)
600
393k
    {
601
393k
        ::osl::MutexGuard const g(m_Mutex);
602
603
393k
        OString o1 = OUStringToOString(data, RTL_TEXTENCODING_UTF8);
604
393k
        xmlChar const *pData = reinterpret_cast<xmlChar const *>(o1.getStr());
605
393k
        xmlNodePtr const pNode = xmlNewDocText(m_aDocPtr, pData);
606
393k
        Reference< XText > const xRet(
607
393k
            static_cast< XNode* >(GetCNode(pNode).get()),
608
393k
            UNO_QUERY_THROW);
609
393k
        return xRet;
610
393k
    }
611
612
    // The Document Type Declaration (see DocumentType) associated with this
613
    // document.
614
    Reference< XDocumentType > SAL_CALL CDocument::getDoctype()
615
0
    {
616
0
        ::osl::MutexGuard const g(m_Mutex);
617
618
0
        xmlNodePtr const pDocType(lcl_getDocumentType(m_aDocPtr));
619
0
        Reference< XDocumentType > const xRet(
620
0
            static_cast< XNode* >(GetCNode(pDocType).get()),
621
0
            UNO_QUERY);
622
0
        return xRet;
623
0
    }
624
625
    // This is a convenience attribute that allows direct access to the child
626
    // node that is the root element of the document.
627
    Reference< XElement > SAL_CALL CDocument::getDocumentElement()
628
0
    {
629
0
        ::osl::MutexGuard const g(m_Mutex);
630
631
0
        xmlNodePtr const pNode = lcl_getDocumentRootPtr(m_aDocPtr);
632
0
        if (!pNode) { return nullptr; }
633
0
        Reference< XElement > const xRet(
634
0
            static_cast< XNode* >(GetCNode(pNode).get()),
635
0
            UNO_QUERY);
636
0
        return xRet;
637
0
    }
638
639
    static xmlNodePtr
640
    lcl_search_element_by_id(const xmlNodePtr cur, const xmlChar* id)
641
0
    {
642
0
        if (cur == nullptr)
643
0
            return nullptr;
644
        // look in current node
645
0
        if (cur->type == XML_ELEMENT_NODE)
646
0
        {
647
0
            xmlAttrPtr a = cur->properties;
648
0
            while (a != nullptr)
649
0
            {
650
0
                if (a->atype == XML_ATTRIBUTE_ID) {
651
0
                    if (strcmp(reinterpret_cast<char*>(a->children->content), reinterpret_cast<char const *>(id)) == 0)
652
0
                        return cur;
653
0
                }
654
0
                a = a->next;
655
0
            }
656
0
        }
657
        // look in children
658
0
        xmlNodePtr result = lcl_search_element_by_id(cur->children, id);
659
0
        if (result != nullptr)
660
0
            return result;
661
0
        result = lcl_search_element_by_id(cur->next, id);
662
0
        return result;
663
0
    }
664
665
    // Returns the Element whose ID is given by elementId.
666
    Reference< XElement > SAL_CALL
667
    CDocument::getElementById(const OUString& elementId)
668
0
    {
669
0
        ::osl::MutexGuard const g(m_Mutex);
670
671
        // search the tree for an element with the given ID
672
0
        OString o1 = OUStringToOString(elementId, RTL_TEXTENCODING_UTF8);
673
0
        xmlChar const *pId = reinterpret_cast<xmlChar const *>(o1.getStr());
674
0
        xmlNodePtr const pStart = lcl_getDocumentRootPtr(m_aDocPtr);
675
0
        if (!pStart) { return nullptr; }
676
0
        xmlNodePtr const pNode = lcl_search_element_by_id(pStart, pId);
677
0
        Reference< XElement > const xRet(
678
0
            static_cast< XNode* >(GetCNode(pNode).get()),
679
0
            UNO_QUERY);
680
0
        return xRet;
681
0
    }
682
683
684
    Reference< XNodeList > SAL_CALL
685
    CDocument::getElementsByTagName(OUString const& rTagname)
686
0
    {
687
0
        ::osl::MutexGuard const g(m_Mutex);
688
689
0
        Reference< XNodeList > const xRet(
690
0
            new CElementList(GetDocumentElement(), m_Mutex, rTagname));
691
0
        return xRet;
692
0
    }
693
694
    Reference< XNodeList > SAL_CALL CDocument::getElementsByTagNameNS(
695
            OUString const& rNamespaceURI, OUString const& rLocalName)
696
0
    {
697
0
        ::osl::MutexGuard const g(m_Mutex);
698
699
0
        Reference< XNodeList > const xRet(
700
0
            new CElementList(GetDocumentElement(), m_Mutex,
701
0
                rLocalName, &rNamespaceURI));
702
0
        return xRet;
703
0
    }
704
705
    Reference< XDOMImplementation > SAL_CALL CDocument::getImplementation()
706
0
    {
707
        // does not need mutex currently
708
0
        return Reference< XDOMImplementation >(CDOMImplementation::get());
709
0
    }
710
711
    // helper function to recursively import siblings
712
    static void lcl_ImportSiblings(
713
        Reference< XDocument > const& xTargetDocument,
714
        Reference< XNode > const& xTargetParent,
715
        Reference< XNode > const& xChild)
716
0
    {
717
0
        Reference< XNode > xSibling = xChild;
718
0
        while (xSibling.is())
719
0
        {
720
0
            Reference< XNode > const xTmp(
721
0
                    xTargetDocument->importNode(xSibling, true));
722
0
            xTargetParent->appendChild(xTmp);
723
0
            xSibling = xSibling->getNextSibling();
724
0
        }
725
0
    }
726
727
    static Reference< XNode >
728
    lcl_ImportNode( Reference< XDocument > const& xDocument,
729
            Reference< XNode > const& xImportedNode, bool deep)
730
0
    {
731
0
        Reference< XNode > xNode;
732
0
        NodeType aNodeType = xImportedNode->getNodeType();
733
0
        switch (aNodeType)
734
0
        {
735
0
        case NodeType_ATTRIBUTE_NODE:
736
0
        {
737
0
            Reference< XAttr > const xAttr(xImportedNode, UNO_QUERY_THROW);
738
0
            Reference< XAttr > const xNew =
739
0
                xDocument->createAttribute(xAttr->getName());
740
0
            xNew->setValue(xAttr->getValue());
741
0
            xNode = xNew;
742
0
            break;
743
0
        }
744
0
        case NodeType_CDATA_SECTION_NODE:
745
0
        {
746
0
            Reference< XCDATASection > const xCData(xImportedNode,
747
0
                    UNO_QUERY_THROW);
748
0
            Reference< XCDATASection > const xNewCData =
749
0
                xDocument->createCDATASection(xCData->getData());
750
0
            xNode = xNewCData;
751
0
            break;
752
0
        }
753
0
        case NodeType_COMMENT_NODE:
754
0
        {
755
0
            Reference< XComment > const xComment(xImportedNode,
756
0
                    UNO_QUERY_THROW);
757
0
            Reference< XComment > const xNewComment =
758
0
                xDocument->createComment(xComment->getData());
759
0
            xNode = xNewComment;
760
0
            break;
761
0
        }
762
0
        case NodeType_DOCUMENT_FRAGMENT_NODE:
763
0
        {
764
0
            Reference< XDocumentFragment > const xFrag(xImportedNode,
765
0
                    UNO_QUERY_THROW);
766
0
            Reference< XDocumentFragment > const xNewFrag =
767
0
                xDocument->createDocumentFragment();
768
0
            xNode = xNewFrag;
769
0
            break;
770
0
        }
771
0
        case NodeType_ELEMENT_NODE:
772
0
        {
773
0
            Reference< XElement > const xElement(xImportedNode,
774
0
                    UNO_QUERY_THROW);
775
0
            OUString const aNsUri = xImportedNode->getNamespaceURI();
776
0
            OUString const aNsPrefix = xImportedNode->getPrefix();
777
0
            OUString aQName = xElement->getTagName();
778
0
            Reference< XElement > xNewElement;
779
0
            if (!aNsUri.isEmpty())
780
0
            {
781
0
                if (!aNsPrefix.isEmpty()) {
782
0
                    aQName = aNsPrefix + ":" + aQName;
783
0
                }
784
0
                xNewElement = xDocument->createElementNS(aNsUri, aQName);
785
0
            } else {
786
0
                xNewElement = xDocument->createElement(aQName);
787
0
            }
788
789
            // get attributes
790
0
            if (xElement->hasAttributes())
791
0
            {
792
0
                Reference< XNamedNodeMap > attribs = xElement->getAttributes();
793
0
                for (sal_Int32 i = 0; i < attribs->getLength(); i++)
794
0
                {
795
0
                    Reference< XAttr > const curAttr(attribs->item(i),
796
0
                            UNO_QUERY_THROW);
797
0
                    OUString const aAttrUri = curAttr->getNamespaceURI();
798
0
                    OUString const aAttrPrefix = curAttr->getPrefix();
799
0
                    OUString aAttrName = curAttr->getName();
800
0
                    OUString const sValue = curAttr->getValue();
801
0
                    if (!aAttrUri.isEmpty())
802
0
                    {
803
0
                        if (!aAttrPrefix.isEmpty()) {
804
0
                            aAttrName = aAttrPrefix + ":" + aAttrName;
805
0
                        }
806
0
                        xNewElement->setAttributeNS(
807
0
                                aAttrUri, aAttrName, sValue);
808
0
                    } else {
809
0
                        xNewElement->setAttribute(aAttrName, sValue);
810
0
                    }
811
0
                }
812
0
            }
813
0
            xNode = xNewElement;
814
0
            break;
815
0
        }
816
0
        case NodeType_ENTITY_REFERENCE_NODE:
817
0
        {
818
0
            Reference< XEntityReference > const xRef(xImportedNode,
819
0
                    UNO_QUERY_THROW);
820
0
            Reference< XEntityReference > const xNewRef(
821
0
                xDocument->createEntityReference(xRef->getNodeName()));
822
0
            xNode = xNewRef;
823
0
            break;
824
0
        }
825
0
        case NodeType_PROCESSING_INSTRUCTION_NODE:
826
0
        {
827
0
            Reference< XProcessingInstruction > const xPi(xImportedNode,
828
0
                    UNO_QUERY_THROW);
829
0
            Reference< XProcessingInstruction > const xNewPi(
830
0
                xDocument->createProcessingInstruction(
831
0
                    xPi->getTarget(), xPi->getData()));
832
0
            xNode = xNewPi;
833
0
            break;
834
0
        }
835
0
        case NodeType_TEXT_NODE:
836
0
        {
837
0
            Reference< XText > const xText(xImportedNode, UNO_QUERY_THROW);
838
0
            Reference< XText > const xNewText(
839
0
                xDocument->createTextNode(xText->getData()));
840
0
            xNode = xNewText;
841
0
            break;
842
0
        }
843
0
        case NodeType_ENTITY_NODE:
844
0
        case NodeType_DOCUMENT_NODE:
845
0
        case NodeType_DOCUMENT_TYPE_NODE:
846
0
        case NodeType_NOTATION_NODE:
847
0
        default:
848
            // can't be imported
849
0
            throw RuntimeException();
850
851
0
        }
852
0
        if (deep)
853
0
        {
854
            // get children and import them
855
0
            Reference< XNode > const xChild = xImportedNode->getFirstChild();
856
0
            if (xChild.is())
857
0
            {
858
0
                lcl_ImportSiblings(xDocument, xNode, xChild);
859
0
            }
860
0
        }
861
862
        /* DOMNodeInsertedIntoDocument
863
         * Fired when a node is being inserted into a document,
864
         * either through direct insertion of the Node or insertion of a
865
         * subtree in which it is contained. This event is dispatched after
866
         * the insertion has taken place. The target of this event is the node
867
         * being inserted. If the Node is being directly inserted the DOMNodeInserted
868
         * event will fire before the DOMNodeInsertedIntoDocument event.
869
         *   Bubbles: No
870
         *   Cancelable: No
871
         *   Context Info: None
872
         */
873
0
        if (xNode.is())
874
0
        {
875
0
            Reference< XDocumentEvent > const xDocevent(xDocument, UNO_QUERY);
876
0
            Reference< XMutationEvent > const event(xDocevent->createEvent(
877
0
                u"DOMNodeInsertedIntoDocument"_ustr), UNO_QUERY_THROW);
878
0
            event->initMutationEvent(
879
0
                u"DOMNodeInsertedIntoDocument"_ustr, true, false, Reference< XNode >(),
880
0
                OUString(), OUString(), OUString(), AttrChangeType(0) );
881
0
            Reference< XEventTarget > const xDocET(xDocument, UNO_QUERY);
882
0
            xDocET->dispatchEvent(event);
883
0
        }
884
885
0
        return xNode;
886
0
    }
887
888
    Reference< XNode > SAL_CALL CDocument::importNode(
889
            Reference< XNode > const& xImportedNode, sal_Bool deep)
890
0
    {
891
0
        if (!xImportedNode.is()) { throw RuntimeException(); }
892
893
        // NB: this whole operation inherently accesses 2 distinct documents.
894
        // The imported node could even be from a different DOM implementation,
895
        // so this implementation cannot make any assumptions about the
896
        // locking strategy of the imported node.
897
        // So the import takes no lock on this document;
898
        // it only calls UNO methods on this document that temporarily
899
        // lock the document, and UNO methods on the imported node that
900
        // may temporarily lock the other document.
901
        // As a consequence, the import is not atomic with regard to
902
        // concurrent modifications of either document, but it should not
903
        // deadlock.
904
        // To ensure that no members are accessed, the implementation is in
905
        // static non-member functions.
906
907
0
        Reference< XDocument > const xDocument(this);
908
        // already in doc?
909
0
        if (xImportedNode->getOwnerDocument() == xDocument) {
910
0
            return xImportedNode;
911
0
        }
912
913
0
        Reference< XNode > const xNode(
914
0
            lcl_ImportNode(xDocument, xImportedNode, deep) );
915
0
        return xNode;
916
0
    }
917
918
    OUString SAL_CALL CDocument::getNodeName()
919
0
    {
920
        // does not need mutex currently
921
0
        return u"#document"_ustr;
922
0
    }
923
924
    OUString SAL_CALL CDocument::getNodeValue()
925
0
    {
926
        // does not need mutex currently
927
0
        return OUString();
928
0
    }
929
930
    Reference< XNode > SAL_CALL CDocument::cloneNode(sal_Bool bDeep)
931
0
    {
932
0
        ::osl::MutexGuard const g(m_rMutex);
933
934
0
        OSL_ASSERT(nullptr != m_aNodePtr);
935
0
        if (nullptr == m_aNodePtr) {
936
0
            return nullptr;
937
0
        }
938
0
        xmlDocPtr const pClone(xmlCopyDoc(m_aDocPtr, bDeep ? 1 : 0));
939
0
        if (nullptr == pClone) { return nullptr; }
940
0
        Reference< XNode > const xRet(
941
0
            static_cast<CNode*>(CDocument::CreateCDocument(pClone).get()));
942
0
        return xRet;
943
0
    }
944
945
    Reference< XEvent > SAL_CALL CDocument::createEvent(const OUString& aType)
946
0
    {
947
        // does not need mutex currently
948
0
        rtl::Reference<events::CEvent> pEvent;
949
0
        if ( aType == "DOMSubtreeModified" || aType == "DOMNodeInserted" || aType == "DOMNodeRemoved"
950
0
          || aType == "DOMNodeRemovedFromDocument" || aType == "DOMNodeInsertedIntoDocument" || aType == "DOMAttrModified"
951
0
          || aType == "DOMCharacterDataModified")
952
0
        {
953
0
            pEvent = new events::CMutationEvent;
954
955
0
        } else if ( aType == "DOMFocusIn" || aType == "DOMFocusOut" || aType == "DOMActivate")
956
0
        {
957
0
            pEvent = new events::CUIEvent;
958
0
        } else if ( aType == "click"     || aType == "mousedown" || aType == "mouseup"
959
0
                 || aType == "mouseover" || aType == "mousemove" || aType == "mouseout" )
960
0
        {
961
0
            pEvent = new events::CMouseEvent;
962
0
        }
963
0
        else // generic event
964
0
        {
965
0
            pEvent = new events::CEvent;
966
0
        }
967
0
        return pEvent;
968
0
    }
969
970
    // css::xml::sax::XSAXSerializable
971
    void SAL_CALL CDocument::serialize(
972
            const Reference< XDocumentHandler >& i_xHandler,
973
            const Sequence< beans::StringPair >& i_rNamespaces)
974
104
    {
975
104
        ::osl::MutexGuard const g(m_Mutex);
976
977
        // add new namespaces to root node
978
104
        xmlNodePtr const pRoot = lcl_getDocumentRootPtr(m_aDocPtr);
979
104
        if (nullptr != pRoot) {
980
4.05k
            for (const beans::StringPair& rNsDef : i_rNamespaces) {
981
4.05k
                OString prefix = OUStringToOString(rNsDef.First,
982
4.05k
                                    RTL_TEXTENCODING_UTF8);
983
4.05k
                OString href   = OUStringToOString(rNsDef.Second,
984
4.05k
                                    RTL_TEXTENCODING_UTF8);
985
                // this will only add the ns if it does not exist already
986
4.05k
                xmlNewNs(pRoot, reinterpret_cast<const xmlChar*>(href.getStr()),
987
4.05k
                         reinterpret_cast<const xmlChar*>(prefix.getStr()));
988
4.05k
            }
989
            // eliminate duplicate namespace declarations
990
104
            nscleanup(pRoot->children, pRoot);
991
104
        }
992
104
        saxify(i_xHandler);
993
104
    }
994
995
    // css::xml::sax::XFastSAXSerializable
996
    void SAL_CALL CDocument::fastSerialize( const Reference< XFastDocumentHandler >& i_xHandler,
997
                                            const Reference< XFastTokenHandler >& i_xTokenHandler,
998
                                            const Sequence< beans::StringPair >& i_rNamespaces,
999
                                            const Sequence< beans::Pair< OUString, sal_Int32 > >& i_rRegisterNamespaces )
1000
2.94k
    {
1001
2.94k
        ::osl::MutexGuard const g(m_Mutex);
1002
1003
        // add new namespaces to root node
1004
2.94k
        xmlNodePtr const pRoot = lcl_getDocumentRootPtr(m_aDocPtr);
1005
2.94k
        if (nullptr != pRoot) {
1006
2.94k
            for (const beans::StringPair& rNsDef : i_rNamespaces) {
1007
0
                OString prefix = OUStringToOString(rNsDef.First,
1008
0
                                    RTL_TEXTENCODING_UTF8);
1009
0
                OString href   = OUStringToOString(rNsDef.Second,
1010
0
                                    RTL_TEXTENCODING_UTF8);
1011
                // this will only add the ns if it does not exist already
1012
0
                xmlNewNs(pRoot, reinterpret_cast<const xmlChar*>(href.getStr()),
1013
0
                         reinterpret_cast<const xmlChar*>(prefix.getStr()));
1014
0
            }
1015
            // eliminate duplicate namespace declarations
1016
2.94k
            nscleanup(pRoot->children, pRoot);
1017
2.94k
        }
1018
1019
2.94k
        Context aContext(i_xHandler,
1020
2.94k
                         dynamic_cast<sax_fastparser::FastTokenHandlerBase*>(i_xTokenHandler.get()));
1021
1022
        // register namespace ids
1023
2.94k
        for (const beans::Pair<OUString,sal_Int32>& rNs : i_rRegisterNamespaces)
1024
120k
        {
1025
120k
            OSL_ENSURE(rNs.Second >= FastToken::NAMESPACE,
1026
120k
                       "CDocument::fastSerialize(): invalid NS token id");
1027
120k
            aContext.maNamespaceMap[ rNs.First ] = rNs.Second;
1028
120k
        }
1029
1030
2.94k
        fastSaxify(aContext);
1031
2.94k
    }
1032
}
1033
1034
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */