/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include "element.hxx" #include #include #include #include #include #include #include #include #include #include #include "attr.hxx" #include "elementlist.hxx" #include "attributesmap.hxx" #include "document.hxx" using namespace css::uno; using namespace css::xml::dom; using namespace css::xml::dom::events; using namespace css::xml::sax; namespace DOM { CElement::CElement(CDocument const& rDocument, ::osl::Mutex const& rMutex, xmlNodePtr const pNode) : CElement_Base(rDocument, rMutex, NodeType_ELEMENT_NODE, pNode) { } void CElement::saxify(const Reference< XDocumentHandler >& i_xHandler) { if (!i_xHandler.is()) throw RuntimeException(); rtl::Reference pAttrs = new comphelper::AttributeList(); // add namespace definitions to attributes for (xmlNsPtr pNs = m_aNodePtr->nsDef; pNs != nullptr; pNs = pNs->next) { const xmlChar *pPrefix = pNs->prefix ? pNs->prefix : reinterpret_cast(""); OUString prefix(reinterpret_cast(pPrefix), strlen(reinterpret_cast(pPrefix)), RTL_TEXTENCODING_UTF8); OUString name = (prefix.isEmpty()) ? OUString( "xmlns" ) : "xmlns:" + prefix; const xmlChar *pHref = pNs->href; OUString val(reinterpret_cast(pHref), strlen(reinterpret_cast(pHref)), RTL_TEXTENCODING_UTF8); pAttrs->AddAttribute(name, val); } // add attributes for (xmlAttrPtr pAttr = m_aNodePtr->properties; pAttr != nullptr; pAttr = pAttr->next) { ::rtl::Reference const pNode = GetOwnerDocument().GetCNode( reinterpret_cast(pAttr)); OSL_ENSURE(pNode != nullptr, "CNode::get returned 0"); OUString prefix = pNode->getPrefix(); OUString name = (prefix.isEmpty()) ? pNode->getLocalName() : prefix + ":" + pNode->getLocalName(); OUString val = pNode->getNodeValue(); pAttrs->AddAttribute(name, val); } OUString prefix = getPrefix(); OUString name = (prefix.isEmpty()) ? getLocalName() : prefix + ":" + getLocalName(); i_xHandler->startElement(name, pAttrs); // recurse for (xmlNodePtr pChild = m_aNodePtr->children; pChild != nullptr; pChild = pChild->next) { ::rtl::Reference const pNode( GetOwnerDocument().GetCNode(pChild)); OSL_ENSURE(pNode != nullptr, "CNode::get returned 0"); pNode->saxify(i_xHandler); } i_xHandler->endElement(name); } void CElement::fastSaxify( Context& i_rContext ) { if (!i_rContext.mxDocHandler.is()) throw RuntimeException(); pushContext(i_rContext); addNamespaces(i_rContext,m_aNodePtr); // add attributes i_rContext.mxAttribList->clear(); for (xmlAttrPtr pAttr = m_aNodePtr->properties; pAttr != nullptr; pAttr = pAttr->next) { ::rtl::Reference const pNode = GetOwnerDocument().GetCNode( reinterpret_cast(pAttr)); OSL_ENSURE(pNode != nullptr, "CNode::get returned 0"); const xmlChar* pName = pAttr->name; sal_Int32 nAttributeToken=FastToken::DONTKNOW; if( pAttr->ns && strlen(reinterpret_cast(pAttr->ns->prefix)) ) nAttributeToken = getTokenWithPrefix( i_rContext, reinterpret_cast(pAttr->ns->prefix), reinterpret_cast(pName) ); else nAttributeToken = getToken( i_rContext, reinterpret_cast(pName) ); if( nAttributeToken != FastToken::DONTKNOW ) i_rContext.mxAttribList->add( nAttributeToken, OUStringToOString(pNode->getNodeValue(), RTL_TEXTENCODING_UTF8)); } const xmlChar* pPrefix = (m_aNodePtr->ns && m_aNodePtr->ns->prefix) ? m_aNodePtr->ns->prefix : reinterpret_cast(""); const xmlChar* pName = m_aNodePtr->name; sal_Int32 nElementToken=FastToken::DONTKNOW; if( strlen(reinterpret_cast(pPrefix)) ) nElementToken = getTokenWithPrefix( i_rContext, reinterpret_cast(pPrefix), reinterpret_cast(pName) ); else nElementToken = getToken( i_rContext, reinterpret_cast(pName) ); Reference xParentHandler(i_rContext.mxCurrentHandler); try { Reference< XFastAttributeList > xAttr( i_rContext.mxAttribList ); if( nElementToken == FastToken::DONTKNOW ) { const OUString aNamespace; const OUString aElementName( reinterpret_cast(pPrefix), strlen(reinterpret_cast(pPrefix)), RTL_TEXTENCODING_UTF8 ); if( xParentHandler.is() ) i_rContext.mxCurrentHandler = xParentHandler->createUnknownChildContext( aNamespace, aElementName, xAttr ); else i_rContext.mxCurrentHandler = i_rContext.mxDocHandler->createUnknownChildContext( aNamespace, aElementName, xAttr ); if( i_rContext.mxCurrentHandler.is() ) i_rContext.mxCurrentHandler->startUnknownElement( aNamespace, aElementName, xAttr ); } else { if( xParentHandler.is() ) i_rContext.mxCurrentHandler = xParentHandler->createFastChildContext( nElementToken, xAttr ); else i_rContext.mxCurrentHandler = i_rContext.mxDocHandler->createFastChildContext( nElementToken, xAttr ); if( i_rContext.mxCurrentHandler.is() ) i_rContext.mxCurrentHandler->startFastElement( nElementToken, xAttr ); } } catch( Exception& ) {} // recurse for (xmlNodePtr pChild = m_aNodePtr->children; pChild != nullptr; pChild = pChild->next) { ::rtl::Reference const pNode( GetOwnerDocument().GetCNode(pChild)); OSL_ENSURE(pNode != nullptr, "CNode::get returned 0"); pNode->fastSaxify(i_rContext); } if( i_rContext.mxCurrentHandler.is() ) try { if( nElementToken != FastToken::DONTKNOW ) i_rContext.mxCurrentHandler->endFastElement( nElementToken ); else { const OUString aElementName( reinterpret_cast(pPrefix), strlen(reinterpret_cast(pPrefix)), RTL_TEXTENCODING_UTF8 ); i_rContext.mxCurrentHandler->endUnknownElement( "", aElementName ); } } catch( Exception& ) {} // restore after children have been processed i_rContext.mxCurrentHandler = xParentHandler; popContext(i_rContext); } bool CElement::IsChildTypeAllowed(NodeType const nodeType, NodeType const*const) { switch (nodeType) { case NodeType_ELEMENT_NODE: case NodeType_TEXT_NODE: case NodeType_COMMENT_NODE: case NodeType_PROCESSING_INSTRUCTION_NODE: case NodeType_CDATA_SECTION_NODE: case NodeType_ENTITY_REFERENCE_NODE: return true; case NodeType_ATTRIBUTE_NODE: /* this is not really allowed by the DOM spec, but this implementation has evidently supported it (by special case handling, so the attribute does not actually become a child) so allow it for backward compatibility */ return true; default: return false; } } /** Retrieves an attribute value by name. return empty string if attribute is not set */ OUString SAL_CALL CElement::getAttribute(OUString const& name) { ::osl::MutexGuard const g(m_rMutex); if (nullptr == m_aNodePtr) { return OUString(); } // search properties OString o1 = OUStringToOString(name, RTL_TEXTENCODING_UTF8); std::shared_ptr const pValue( xmlGetProp(m_aNodePtr, reinterpret_cast(o1.getStr())), xmlFree); OUString const ret( pValue ? OUString(reinterpret_cast(pValue.get()), strlen(reinterpret_cast(pValue.get())), RTL_TEXTENCODING_UTF8) : OUString() ); return ret; } /** Retrieves an attribute node by name. */ Reference< XAttr > SAL_CALL CElement::getAttributeNode(OUString const& name) { ::osl::MutexGuard const g(m_rMutex); if (nullptr == m_aNodePtr) { return nullptr; } OString o1 = OUStringToOString(name, RTL_TEXTENCODING_UTF8); xmlChar const*const pName = reinterpret_cast(o1.getStr()); xmlAttrPtr const pAttr = xmlHasProp(m_aNodePtr, pName); if (nullptr == pAttr) { return nullptr; } Reference< XAttr > const xRet( static_cast< XNode* >(GetOwnerDocument().GetCNode( reinterpret_cast(pAttr)).get()), UNO_QUERY_THROW); return xRet; } /** Retrieves an Attr node by local name and namespace URI. */ Reference< XAttr > SAL_CALL CElement::getAttributeNodeNS( const OUString& namespaceURI, const OUString& localName) { ::osl::MutexGuard const g(m_rMutex); if (nullptr == m_aNodePtr) { return nullptr; } OString o1 = OUStringToOString(localName, RTL_TEXTENCODING_UTF8); xmlChar const*const pName = reinterpret_cast(o1.getStr()); OString o2 = OUStringToOString(namespaceURI, RTL_TEXTENCODING_UTF8); xmlChar const*const pNS = reinterpret_cast(o2.getStr()); xmlAttrPtr const pAttr = xmlHasNsProp(m_aNodePtr, pName, pNS); if (nullptr == pAttr) { return nullptr; } Reference< XAttr > const xRet( static_cast< XNode* >(GetOwnerDocument().GetCNode( reinterpret_cast(pAttr)).get()), UNO_QUERY_THROW); return xRet; } /** Retrieves an attribute value by local name and namespace URI. return empty string if attribute is not set */ OUString SAL_CALL CElement::getAttributeNS( OUString const& namespaceURI, OUString const& localName) { ::osl::MutexGuard const g(m_rMutex); if (nullptr == m_aNodePtr) { return OUString(); } OString o1 = OUStringToOString(localName, RTL_TEXTENCODING_UTF8); xmlChar const*const pName = reinterpret_cast(o1.getStr()); OString o2 = OUStringToOString(namespaceURI, RTL_TEXTENCODING_UTF8); xmlChar const*const pNS = reinterpret_cast(o2.getStr()); std::shared_ptr const pValue( xmlGetNsProp(m_aNodePtr, pName, pNS), xmlFree); if (nullptr == pValue) { return OUString(); } OUString const ret(reinterpret_cast(pValue.get()), strlen(reinterpret_cast(pValue.get())), RTL_TEXTENCODING_UTF8); return ret; } /** Returns a NodeList of all descendant Elements with a given tag name, in the order in which they are encountered in a preorder traversal of this Element tree. */ Reference< XNodeList > SAL_CALL CElement::getElementsByTagName(OUString const& rLocalName) { ::osl::MutexGuard const g(m_rMutex); Reference< XNodeList > const xList( new CElementList(this, m_rMutex, rLocalName)); return xList; } /** Returns a NodeList of all the descendant Elements with a given local name and namespace URI in the order in which they are encountered in a preorder traversal of this Element tree. */ Reference< XNodeList > SAL_CALL CElement::getElementsByTagNameNS( OUString const& rNamespaceURI, OUString const& rLocalName) { ::osl::MutexGuard const g(m_rMutex); Reference< XNodeList > const xList( new CElementList(this, m_rMutex, rLocalName, &rNamespaceURI)); return xList; } /** The name of the element. */ OUString SAL_CALL CElement::getTagName() { ::osl::MutexGuard const g(m_rMutex); if (nullptr == m_aNodePtr) { return OUString(); } OUString const ret(reinterpret_cast(m_aNodePtr->name), strlen(reinterpret_cast(m_aNodePtr->name)), RTL_TEXTENCODING_UTF8); return ret; } /** Returns true when an attribute with a given name is specified on this element or has a default value, false otherwise. */ sal_Bool SAL_CALL CElement::hasAttribute(OUString const& name) { ::osl::MutexGuard const g(m_rMutex); OString o1 = OUStringToOString(name, RTL_TEXTENCODING_UTF8); xmlChar const *pName = reinterpret_cast(o1.getStr()); return (m_aNodePtr != nullptr && xmlHasProp(m_aNodePtr, pName) != nullptr); } /** Returns true when an attribute with a given local name and namespace URI is specified on this element or has a default value, false otherwise. */ sal_Bool SAL_CALL CElement::hasAttributeNS( OUString const& namespaceURI, OUString const& localName) { ::osl::MutexGuard const g(m_rMutex); OString o1 = OUStringToOString(localName, RTL_TEXTENCODING_UTF8); xmlChar const *pName = reinterpret_cast(o1.getStr()); OString o2 = OUStringToOString(namespaceURI, RTL_TEXTENCODING_UTF8); xmlChar const *pNs = reinterpret_cast(o2.getStr()); return (m_aNodePtr != nullptr && xmlHasNsProp(m_aNodePtr, pName, pNs) != nullptr); } /** Removes an attribute by name. */ void SAL_CALL CElement::removeAttribute(OUString const& name) { ::osl::MutexGuard const g(m_rMutex); if (nullptr == m_aNodePtr) { return; } OString o1 = OUStringToOString(name, RTL_TEXTENCODING_UTF8); xmlChar const*const pName = reinterpret_cast(o1.getStr()); xmlAttrPtr const pAttr = xmlHasProp(m_aNodePtr, pName); if (0 == xmlUnsetProp(m_aNodePtr, pName)) { ::rtl::Reference const pCNode(GetOwnerDocument().GetCNode( reinterpret_cast(pAttr), false)); if (pCNode.is()) { pCNode->invalidate(); // freed by xmlUnsetProp } } } /** Removes an attribute by local name and namespace URI. */ void SAL_CALL CElement::removeAttributeNS( OUString const& namespaceURI, OUString const& localName) { ::osl::MutexGuard const g(m_rMutex); if (nullptr == m_aNodePtr) { return; } OString o1 = OUStringToOString(localName, RTL_TEXTENCODING_UTF8); xmlChar const*const pName = reinterpret_cast(o1.getStr()); OString o2 = OUStringToOString(namespaceURI, RTL_TEXTENCODING_UTF8); xmlChar const*const pURI = reinterpret_cast(o2.getStr()); xmlNsPtr const pNs = xmlSearchNsByHref(m_aNodePtr->doc, m_aNodePtr, pURI); xmlAttrPtr const pAttr = xmlHasNsProp(m_aNodePtr, pName, pURI); if (0 == xmlUnsetNsProp(m_aNodePtr, pNs, pName)) { ::rtl::Reference const pCNode(GetOwnerDocument().GetCNode( reinterpret_cast(pAttr), false)); if (pCNode.is()) { pCNode->invalidate(); // freed by xmlUnsetNsProp } } } /** Removes the specified attribute node. */ Reference< XAttr > SAL_CALL CElement::removeAttributeNode(Reference< XAttr > const& oldAttr) { ::osl::MutexGuard const g(m_rMutex); if (nullptr == m_aNodePtr) { return nullptr; } ::rtl::Reference const pCNode( dynamic_cast(oldAttr.get())); if (!pCNode.is()) { throw RuntimeException(); } xmlNodePtr const pNode = pCNode->GetNodePtr(); xmlAttrPtr const pAttr = reinterpret_cast(pNode); if (!pAttr) { throw RuntimeException(); } if (pAttr->parent != m_aNodePtr) { DOMException e; e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR; throw e; } if (pAttr->doc != m_aNodePtr->doc) { DOMException e; e.Code = DOMExceptionType_WRONG_DOCUMENT_ERR; throw e; } Reference< XAttr > aAttr; if (!oldAttr->getNamespaceURI().isEmpty()) { OUStringBuffer qname(oldAttr->getPrefix()); if (!qname.isEmpty()) { qname.append(':'); } qname.append(oldAttr->getName()); aAttr = GetOwnerDocument().createAttributeNS( oldAttr->getNamespaceURI(), qname.makeStringAndClear()); } else { aAttr = GetOwnerDocument().createAttribute(oldAttr->getName()); } aAttr->setValue(oldAttr->getValue()); xmlRemoveProp(pAttr); pCNode->invalidate(); // freed by xmlRemoveProp return aAttr; } /** Adds a new attribute node. */ Reference< XAttr > CElement::setAttributeNode_Impl_Lock( Reference< XAttr > const& xNewAttr, bool const bNS) { if (xNewAttr->getOwnerDocument() != getOwnerDocument()) { DOMException e; e.Code = DOMExceptionType_WRONG_DOCUMENT_ERR; throw e; } ::osl::ClearableMutexGuard guard(m_rMutex); if (nullptr == m_aNodePtr) { throw RuntimeException(); } // get the implementation CAttr *const pCAttr = dynamic_cast(xNewAttr.get()); if (!pCAttr) { throw RuntimeException(); } xmlAttrPtr const pAttr = reinterpret_cast(pCAttr->GetNodePtr()); if (!pAttr) { throw RuntimeException(); } // check whether the attribute is not in use by another element if (pAttr->parent) { DOMException e; e.Code = DOMExceptionType_INUSE_ATTRIBUTE_ERR; throw e; } xmlAttrPtr res = nullptr; xmlChar const*const pContent( (pAttr->children) ? pAttr->children->content : nullptr); if (bNS) { xmlNsPtr const pNs( pCAttr->GetNamespace(m_aNodePtr) ); res = xmlNewNsProp(m_aNodePtr, pNs, pAttr->name, pContent); } else { res = xmlNewProp(m_aNodePtr, pAttr->name, pContent); } // get the new attr node Reference< XAttr > const xAttr( static_cast< XNode* >(GetOwnerDocument().GetCNode( reinterpret_cast(res)).get()), UNO_QUERY_THROW); // attribute addition event // dispatch DOMAttrModified event Reference< XDocumentEvent > docevent(getOwnerDocument(), UNO_QUERY); Reference< XMutationEvent > event(docevent->createEvent( "DOMAttrModified"), UNO_QUERY); event->initMutationEvent("DOMAttrModified", true, false, xAttr, OUString(), xAttr->getValue(), xAttr->getName(), AttrChangeType_ADDITION); guard.clear(); // release mutex before calling event handlers dispatchEvent(event); dispatchSubtreeModified(); return xAttr; } Reference< XAttr > CElement::setAttributeNode(const Reference< XAttr >& newAttr) { return setAttributeNode_Impl_Lock(newAttr, false); } /** Adds a new attribute. */ Reference< XAttr > CElement::setAttributeNodeNS(const Reference< XAttr >& newAttr) { return setAttributeNode_Impl_Lock(newAttr, true); } /** Adds a new attribute. */ void SAL_CALL CElement::setAttribute(OUString const& name, OUString const& value) { ::osl::ClearableMutexGuard guard(m_rMutex); OString o1 = OUStringToOString(name, RTL_TEXTENCODING_UTF8); xmlChar const *pName = reinterpret_cast(o1.getStr()); OString o2 = OUStringToOString(value, RTL_TEXTENCODING_UTF8); xmlChar const *pValue = reinterpret_cast(o2.getStr()); if (nullptr == m_aNodePtr) { throw RuntimeException(); } OUString oldValue; AttrChangeType aChangeType = AttrChangeType_MODIFICATION; std::shared_ptr const pOld( xmlGetProp(m_aNodePtr, pName), xmlFree); if (pOld == nullptr) { aChangeType = AttrChangeType_ADDITION; xmlNewProp(m_aNodePtr, pName, pValue); } else { oldValue = OUString(reinterpret_cast(pOld.get()), strlen(reinterpret_cast(pOld.get())), RTL_TEXTENCODING_UTF8); xmlSetProp(m_aNodePtr, pName, pValue); } // dispatch DOMAttrModified event Reference< XDocumentEvent > docevent(getOwnerDocument(), UNO_QUERY); Reference< XMutationEvent > event(docevent->createEvent( "DOMAttrModified"), UNO_QUERY); event->initMutationEvent("DOMAttrModified", true, false, getAttributeNode(name), oldValue, value, name, aChangeType); guard.clear(); // release mutex before calling event handlers dispatchEvent(event); dispatchSubtreeModified(); } /** Adds a new attribute. */ void SAL_CALL CElement::setAttributeNS(OUString const& namespaceURI, OUString const& qualifiedName, OUString const& value) { if (namespaceURI.isEmpty()) throw RuntimeException(); ::osl::ClearableMutexGuard guard(m_rMutex); OString o1, o2, o3, o4, o5; xmlChar const *pPrefix = nullptr; xmlChar const *pLName = nullptr; o1 = OUStringToOString(qualifiedName, RTL_TEXTENCODING_UTF8); xmlChar const *pQName = reinterpret_cast(o1.getStr()); sal_Int32 idx = qualifiedName.indexOf(':'); if (idx != -1) { o2 = OUStringToOString( qualifiedName.subView(0,idx), RTL_TEXTENCODING_UTF8); pPrefix = reinterpret_cast(o2.getStr()); o3 = OUStringToOString( qualifiedName.subView(idx+1), RTL_TEXTENCODING_UTF8); pLName = reinterpret_cast(o3.getStr()); } else { pPrefix = reinterpret_cast(""); pLName = pQName; } o4 = OUStringToOString(namespaceURI, RTL_TEXTENCODING_UTF8); o5 = OUStringToOString(value, RTL_TEXTENCODING_UTF8); xmlChar const *pURI= reinterpret_cast(o4.getStr()); xmlChar const *pValue = reinterpret_cast(o5.getStr()); if (nullptr == m_aNodePtr) { throw RuntimeException(); } //find the right namespace xmlNsPtr pNs = xmlSearchNs(m_aNodePtr->doc, m_aNodePtr, pPrefix); // if no namespace found, create a new one if (pNs == nullptr) { pNs = xmlNewNs(m_aNodePtr, pURI, pPrefix); } if (strcmp(reinterpret_cast(pNs->href), reinterpret_cast(pURI)) != 0) { // ambiguous ns prefix throw RuntimeException(); } // found namespace matches OUString oldValue; AttrChangeType aChangeType = AttrChangeType_MODIFICATION; std::shared_ptr const pOld( xmlGetNsProp(m_aNodePtr, pLName, pNs->href), xmlFree); if (pOld == nullptr) { aChangeType = AttrChangeType_ADDITION; xmlNewNsProp(m_aNodePtr, pNs, pLName, pValue); } else { oldValue = OUString(reinterpret_cast(pOld.get()), strlen(reinterpret_cast(pOld.get())), RTL_TEXTENCODING_UTF8); xmlSetNsProp(m_aNodePtr, pNs, pLName, pValue); } // dispatch DOMAttrModified event Reference< XDocumentEvent > docevent(getOwnerDocument(), UNO_QUERY); Reference< XMutationEvent > event(docevent->createEvent( "DOMAttrModified"), UNO_QUERY); event->initMutationEvent( "DOMAttrModified", true, false, getAttributeNodeNS(namespaceURI, OUString(reinterpret_cast(pLName), strlen(reinterpret_cast(pLName)), RTL_TEXTENCODING_UTF8)), oldValue, value, qualifiedName, aChangeType); guard.clear(); // release mutex before calling event handlers dispatchEvent(event); dispatchSubtreeModified(); } Reference< XNamedNodeMap > SAL_CALL CElement::getAttributes() { ::osl::MutexGuard const g(m_rMutex); Reference< XNamedNodeMap > const xMap( new CAttributesMap(this, m_rMutex)); return xMap; } OUString SAL_CALL CElement::getNodeName() { return getLocalName(); } OUString SAL_CALL CElement::getLocalName() { ::osl::MutexGuard const g(m_rMutex); OUString aName; if (m_aNodePtr != nullptr) { const xmlChar* pName = m_aNodePtr->name; aName = OUString(reinterpret_cast(pName), strlen(reinterpret_cast(pName)), RTL_TEXTENCODING_UTF8); } return aName; } OUString SAL_CALL CElement::getNodeValue() { return OUString(); } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */