/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: */ |