| // Copyright 2016 PDFium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com | |
| #include "xfa/fxfa/parser/cxfa_document_builder.h" | |
| #include <utility> | |
| #include <vector> | |
| #include "core/fxcrt/autorestorer.h" | |
| #include "core/fxcrt/cfx_readonlymemorystream.h" | |
| #include "core/fxcrt/cfx_widetextbuf.h" | |
| #include "core/fxcrt/fx_codepage.h" | |
| #include "core/fxcrt/fx_extension.h" | |
| #include "core/fxcrt/xml/cfx_xmlchardata.h" | |
| #include "core/fxcrt/xml/cfx_xmldocument.h" | |
| #include "core/fxcrt/xml/cfx_xmlelement.h" | |
| #include "core/fxcrt/xml/cfx_xmlinstruction.h" | |
| #include "core/fxcrt/xml/cfx_xmlnode.h" | |
| #include "core/fxcrt/xml/cfx_xmltext.h" | |
| #include "fxjs/xfa/cjx_object.h" | |
| #include "third_party/base/check.h" | |
| #include "third_party/base/notreached.h" | |
| #include "third_party/base/optional.h" | |
| #include "xfa/fxfa/fxfa.h" | |
| #include "xfa/fxfa/parser/cxfa_document.h" | |
| #include "xfa/fxfa/parser/cxfa_node.h" | |
| #include "xfa/fxfa/parser/cxfa_subform.h" | |
| #include "xfa/fxfa/parser/cxfa_template.h" | |
| #include "xfa/fxfa/parser/xfa_basic_data.h" | |
| #include "xfa/fxfa/parser/xfa_utils.h" | |
| namespace { | |
| CFX_XMLNode* GetDocumentNode(CFX_XMLNode* pRootNode) { | |
| for (CFX_XMLNode* pXMLNode = pRootNode->GetFirstChild(); pXMLNode; | |
| pXMLNode = pXMLNode->GetNextSibling()) { | |
| if (pXMLNode->GetType() == CFX_XMLNode::Type::kElement) | |
| return pXMLNode; | |
| } | |
| return nullptr; | |
| } | |
| bool MatchNodeName(CFX_XMLNode* pNode, | |
| WideStringView wsLocalTagName, | |
| WideStringView wsNamespaceURIPrefix, | |
| uint32_t eMatchFlags = XFA_XDPPACKET_FLAGS_NOMATCH) { | |
| CFX_XMLElement* pElement = ToXMLElement(pNode); | |
| if (!pElement) | |
| return false; | |
| WideString wsNodeStr = pElement->GetLocalTagName(); | |
| if (wsNodeStr != wsLocalTagName) | |
| return false; | |
| wsNodeStr = pElement->GetNamespaceURI(); | |
| if (eMatchFlags & XFA_XDPPACKET_FLAGS_NOMATCH) | |
| return true; | |
| if (eMatchFlags & XFA_XDPPACKET_FLAGS_PREFIXMATCH) { | |
| return wsNodeStr.First(wsNamespaceURIPrefix.GetLength()) == | |
| wsNamespaceURIPrefix; | |
| } | |
| return wsNodeStr == wsNamespaceURIPrefix; | |
| } | |
| bool GetAttributeLocalName(WideStringView wsAttributeName, | |
| WideString& wsLocalAttrName) { | |
| WideString wsAttrName(wsAttributeName); | |
| auto pos = wsAttrName.Find(L':', 0); | |
| if (!pos.has_value()) { | |
| wsLocalAttrName = std::move(wsAttrName); | |
| return false; | |
| } | |
| wsLocalAttrName = wsAttrName.Last(wsAttrName.GetLength() - pos.value() - 1); | |
| return true; | |
| } | |
| bool ResolveAttribute(CFX_XMLElement* pElement, | |
| const WideString& wsAttrName, | |
| WideString& wsLocalAttrName, | |
| WideString& wsNamespaceURI) { | |
| WideString wsNSPrefix; | |
| if (GetAttributeLocalName(wsAttrName.AsStringView(), wsLocalAttrName)) { | |
| wsNSPrefix = wsAttrName.First(wsAttrName.GetLength() - | |
| wsLocalAttrName.GetLength() - 1); | |
| } | |
| if (wsLocalAttrName.EqualsASCII("xmlns") || wsNSPrefix.EqualsASCII("xmlns") || | |
| wsNSPrefix.EqualsASCII("xml")) { | |
| return false; | |
| } | |
| if (!XFA_FDEExtension_ResolveNamespaceQualifier(pElement, wsNSPrefix, | |
| &wsNamespaceURI)) { | |
| wsNamespaceURI.clear(); | |
| return false; | |
| } | |
| return true; | |
| } | |
| Optional<WideString> FindAttributeWithNS(CFX_XMLElement* pElement, | |
| WideStringView wsLocalAttributeName, | |
| WideStringView wsNamespaceURIPrefix) { | |
| WideString wsAttrNS; | |
| for (auto it : pElement->GetAttributes()) { | |
| auto pos = it.first.Find(L':', 0); | |
| WideString wsNSPrefix; | |
| if (!pos.has_value()) { | |
| if (wsLocalAttributeName != it.first) | |
| continue; | |
| } else { | |
| if (wsLocalAttributeName != | |
| it.first.Last(it.first.GetLength() - pos.value() - 1)) { | |
| continue; | |
| } | |
| wsNSPrefix = it.first.First(pos.value()); | |
| } | |
| if (!XFA_FDEExtension_ResolveNamespaceQualifier(pElement, wsNSPrefix, | |
| &wsAttrNS) || | |
| wsAttrNS != wsNamespaceURIPrefix) { | |
| continue; | |
| } | |
| return it.second; | |
| } | |
| return {}; | |
| } | |
| CFX_XMLNode* GetDataSetsFromXDP(CFX_XMLNode* pXMLDocumentNode) { | |
| XFA_PACKETINFO datasets_packet = | |
| XFA_GetPacketByIndex(XFA_PacketType::Datasets); | |
| if (MatchNodeName(pXMLDocumentNode, datasets_packet.name, datasets_packet.uri, | |
| datasets_packet.flags)) { | |
| return pXMLDocumentNode; | |
| } | |
| XFA_PACKETINFO xdp_packet = XFA_GetPacketByIndex(XFA_PacketType::Xdp); | |
| if (!MatchNodeName(pXMLDocumentNode, xdp_packet.name, xdp_packet.uri, | |
| xdp_packet.flags)) { | |
| return nullptr; | |
| } | |
| for (CFX_XMLNode* pDatasetsNode = pXMLDocumentNode->GetFirstChild(); | |
| pDatasetsNode; pDatasetsNode = pDatasetsNode->GetNextSibling()) { | |
| if (MatchNodeName(pDatasetsNode, datasets_packet.name, datasets_packet.uri, | |
| datasets_packet.flags)) { | |
| return pDatasetsNode; | |
| } | |
| } | |
| return nullptr; | |
| } | |
| bool IsStringAllWhitespace(WideString wsText) { | |
| wsText.TrimRight(L"\x20\x9\xD\xA"); | |
| return wsText.IsEmpty(); | |
| } | |
| void ConvertXMLToPlainText(CFX_XMLElement* pRootXMLNode, WideString& wsOutput) { | |
| for (CFX_XMLNode* pXMLChild = pRootXMLNode->GetFirstChild(); pXMLChild; | |
| pXMLChild = pXMLChild->GetNextSibling()) { | |
| switch (pXMLChild->GetType()) { | |
| case CFX_XMLNode::Type::kElement: { | |
| WideString wsTextData = ToXMLElement(pXMLChild)->GetTextData(); | |
| wsTextData += L"\n"; | |
| wsOutput += wsTextData; | |
| break; | |
| } | |
| case CFX_XMLNode::Type::kText: | |
| case CFX_XMLNode::Type::kCharData: { | |
| WideString wsText = ToXMLText(pXMLChild)->GetText(); | |
| if (IsStringAllWhitespace(wsText)) | |
| continue; | |
| wsOutput = std::move(wsText); | |
| break; | |
| } | |
| default: | |
| NOTREACHED(); | |
| break; | |
| } | |
| } | |
| } | |
| WideString GetPlainTextFromRichText(CFX_XMLNode* pXMLNode) { | |
| if (!pXMLNode) | |
| return WideString(); | |
| WideString wsPlainText; | |
| switch (pXMLNode->GetType()) { | |
| case CFX_XMLNode::Type::kElement: { | |
| CFX_XMLElement* pXMLElement = static_cast<CFX_XMLElement*>(pXMLNode); | |
| WideString wsTag = pXMLElement->GetLocalTagName(); | |
| uint32_t uTag = FX_HashCode_GetW(wsTag.AsStringView(), true); | |
| if (uTag == 0x0001f714) { | |
| wsPlainText += L"\n"; | |
| } else if (uTag == 0x00000070) { | |
| if (!wsPlainText.IsEmpty()) { | |
| wsPlainText += L"\n"; | |
| } | |
| } else if (uTag == 0xa48ac63) { | |
| if (!wsPlainText.IsEmpty() && wsPlainText.Back() != '\n') { | |
| wsPlainText += L"\n"; | |
| } | |
| } | |
| break; | |
| } | |
| case CFX_XMLNode::Type::kText: | |
| case CFX_XMLNode::Type::kCharData: { | |
| WideString wsContent = ToXMLText(pXMLNode)->GetText(); | |
| wsPlainText += wsContent; | |
| break; | |
| } | |
| default: | |
| break; | |
| } | |
| for (CFX_XMLNode* pChildXML = pXMLNode->GetFirstChild(); pChildXML; | |
| pChildXML = pChildXML->GetNextSibling()) { | |
| wsPlainText += GetPlainTextFromRichText(pChildXML); | |
| } | |
| return wsPlainText; | |
| } | |
| } // namespace | |
| bool XFA_RecognizeRichText(CFX_XMLElement* pRichTextXMLNode) { | |
| return pRichTextXMLNode && pRichTextXMLNode->GetNamespaceURI().EqualsASCII( | |
| "http://www.w3.org/1999/xhtml"); | |
| } | |
| CXFA_DocumentBuilder::CXFA_DocumentBuilder(CXFA_Document* pNodeFactory) | |
| : node_factory_(pNodeFactory) {} | |
| CXFA_DocumentBuilder::~CXFA_DocumentBuilder() = default; | |
| bool CXFA_DocumentBuilder::BuildDocument(CFX_XMLDocument* pXML, | |
| XFA_PacketType ePacketID) { | |
| CFX_XMLNode* root = Build(pXML); | |
| if (!root) | |
| return false; | |
| root_node_ = ParseAsXDPPacket(root, ePacketID); | |
| return !!root_node_; | |
| } | |
| CFX_XMLNode* CXFA_DocumentBuilder::Build(CFX_XMLDocument* pXML) { | |
| if (!pXML) | |
| return nullptr; | |
| xml_doc_ = pXML; | |
| xml_doc_->GetRoot()->InsertChildNode( | |
| xml_doc_->CreateNode<CFX_XMLInstruction>(L"xml"), 0); | |
| return GetDocumentNode(xml_doc_->GetRoot()); | |
| } | |
| void CXFA_DocumentBuilder::ConstructXFANode(CXFA_Node* pXFANode, | |
| CFX_XMLNode* pXMLNode) { | |
| XFA_PacketType ePacketID = pXFANode->GetPacketType(); | |
| if (ePacketID == XFA_PacketType::Datasets) { | |
| if (pXFANode->GetElementType() == XFA_Element::DataValue) { | |
| for (CFX_XMLNode* pXMLChild = pXMLNode->GetFirstChild(); pXMLChild; | |
| pXMLChild = pXMLChild->GetNextSibling()) { | |
| CFX_XMLNode::Type eNodeType = pXMLChild->GetType(); | |
| if (eNodeType == CFX_XMLNode::Type::kInstruction) | |
| continue; | |
| if (eNodeType == CFX_XMLNode::Type::kElement) { | |
| CXFA_Node* pXFAChild = node_factory_->CreateNode( | |
| XFA_PacketType::Datasets, XFA_Element::DataValue); | |
| if (!pXFAChild) | |
| return; | |
| CFX_XMLElement* child = static_cast<CFX_XMLElement*>(pXMLChild); | |
| WideString wsNodeStr = child->GetLocalTagName(); | |
| pXFAChild->JSObject()->SetCData(XFA_Attribute::Name, wsNodeStr); | |
| WideString wsChildValue = GetPlainTextFromRichText(child); | |
| if (!wsChildValue.IsEmpty()) | |
| pXFAChild->JSObject()->SetCData(XFA_Attribute::Value, wsChildValue); | |
| pXFANode->InsertChildAndNotify(pXFAChild, nullptr); | |
| pXFAChild->SetXMLMappingNode(pXMLChild); | |
| pXFAChild->SetFlag(XFA_NodeFlag_Initialized); | |
| break; | |
| } | |
| } | |
| root_node_ = pXFANode; | |
| } else { | |
| root_node_ = DataLoader(pXFANode, pXMLNode, true); | |
| } | |
| } else if (pXFANode->IsContentNode()) { | |
| ParseContentNode(pXFANode, pXMLNode, ePacketID); | |
| root_node_ = pXFANode; | |
| } else { | |
| root_node_ = NormalLoader(pXFANode, pXMLNode, ePacketID, true); | |
| } | |
| } | |
| CXFA_Node* CXFA_DocumentBuilder::GetRootNode() const { | |
| return root_node_; | |
| } | |
| CXFA_Node* CXFA_DocumentBuilder::ParseAsXDPPacket(CFX_XMLNode* pXMLDocumentNode, | |
| XFA_PacketType ePacketID) { | |
| switch (ePacketID) { | |
| case XFA_PacketType::Xdp: | |
| return ParseAsXDPPacket_XDP(pXMLDocumentNode); | |
| case XFA_PacketType::Config: | |
| return ParseAsXDPPacket_Config(pXMLDocumentNode); | |
| case XFA_PacketType::Template: | |
| return ParseAsXDPPacket_Template(pXMLDocumentNode); | |
| case XFA_PacketType::Form: | |
| return ParseAsXDPPacket_Form(pXMLDocumentNode); | |
| case XFA_PacketType::Datasets: | |
| return ParseAsXDPPacket_Data(pXMLDocumentNode); | |
| case XFA_PacketType::Xdc: | |
| return ParseAsXDPPacket_Xdc(pXMLDocumentNode); | |
| case XFA_PacketType::LocaleSet: | |
| return ParseAsXDPPacket_LocaleConnectionSourceSet( | |
| pXMLDocumentNode, XFA_PacketType::LocaleSet, XFA_Element::LocaleSet); | |
| case XFA_PacketType::ConnectionSet: | |
| return ParseAsXDPPacket_LocaleConnectionSourceSet( | |
| pXMLDocumentNode, XFA_PacketType::ConnectionSet, | |
| XFA_Element::ConnectionSet); | |
| case XFA_PacketType::SourceSet: | |
| return ParseAsXDPPacket_LocaleConnectionSourceSet( | |
| pXMLDocumentNode, XFA_PacketType::SourceSet, XFA_Element::SourceSet); | |
| default: | |
| return ParseAsXDPPacket_User(pXMLDocumentNode); | |
| } | |
| } | |
| CXFA_Node* CXFA_DocumentBuilder::ParseAsXDPPacket_XDP( | |
| CFX_XMLNode* pXMLDocumentNode) { | |
| XFA_PACKETINFO packet = XFA_GetPacketByIndex(XFA_PacketType::Xdp); | |
| if (!MatchNodeName(pXMLDocumentNode, packet.name, packet.uri, packet.flags)) | |
| return nullptr; | |
| CXFA_Node* pXFARootNode = | |
| node_factory_->CreateNode(XFA_PacketType::Xdp, XFA_Element::Xfa); | |
| if (!pXFARootNode) | |
| return nullptr; | |
| root_node_ = pXFARootNode; | |
| pXFARootNode->JSObject()->SetCData(XFA_Attribute::Name, L"xfa"); | |
| for (auto it : ToXMLElement(pXMLDocumentNode)->GetAttributes()) { | |
| if (it.first.EqualsASCII("uuid")) | |
| pXFARootNode->JSObject()->SetCData(XFA_Attribute::Uuid, it.second); | |
| else if (it.first.EqualsASCII("timeStamp")) | |
| pXFARootNode->JSObject()->SetCData(XFA_Attribute::TimeStamp, it.second); | |
| } | |
| CFX_XMLNode* pXMLConfigDOMRoot = nullptr; | |
| CXFA_Node* pXFAConfigDOMRoot = nullptr; | |
| XFA_PACKETINFO config_packet = XFA_GetPacketByIndex(XFA_PacketType::Config); | |
| for (CFX_XMLNode* pChildItem = pXMLDocumentNode->GetFirstChild(); pChildItem; | |
| pChildItem = pChildItem->GetNextSibling()) { | |
| if (!MatchNodeName(pChildItem, config_packet.name, config_packet.uri, | |
| config_packet.flags)) { | |
| continue; | |
| } | |
| // TODO(tsepez): make GetFirstChildByName() take a name. | |
| uint32_t hash = FX_HashCode_GetW(config_packet.name, false); | |
| if (pXFARootNode->GetFirstChildByName(hash)) | |
| return nullptr; | |
| pXMLConfigDOMRoot = pChildItem; | |
| pXFAConfigDOMRoot = ParseAsXDPPacket_Config(pXMLConfigDOMRoot); | |
| if (pXFAConfigDOMRoot) | |
| pXFARootNode->InsertChildAndNotify(pXFAConfigDOMRoot, nullptr); | |
| } | |
| CFX_XMLNode* pXMLDatasetsDOMRoot = nullptr; | |
| CFX_XMLNode* pXMLFormDOMRoot = nullptr; | |
| CFX_XMLNode* pXMLTemplateDOMRoot = nullptr; | |
| for (CFX_XMLNode* pChildItem = pXMLDocumentNode->GetFirstChild(); pChildItem; | |
| pChildItem = pChildItem->GetNextSibling()) { | |
| CFX_XMLElement* pElement = ToXMLElement(pChildItem); | |
| if (!pElement || pElement == pXMLConfigDOMRoot) | |
| continue; | |
| WideString wsPacketName = pElement->GetLocalTagName(); | |
| Optional<XFA_PACKETINFO> packet_info = | |
| XFA_GetPacketByName(wsPacketName.AsStringView()); | |
| if (packet_info.has_value() && packet_info.value().uri && | |
| !MatchNodeName(pElement, packet_info.value().name, | |
| packet_info.value().uri, packet_info.value().flags)) { | |
| packet_info = {}; | |
| } | |
| XFA_PacketType ePacket = XFA_PacketType::User; | |
| if (packet_info.has_value()) | |
| ePacket = packet_info.value().packet_type; | |
| if (ePacket == XFA_PacketType::Xdp) | |
| continue; | |
| if (ePacket == XFA_PacketType::Datasets) { | |
| if (pXMLDatasetsDOMRoot) | |
| return nullptr; | |
| pXMLDatasetsDOMRoot = pElement; | |
| } else if (ePacket == XFA_PacketType::Form) { | |
| if (pXMLFormDOMRoot) | |
| return nullptr; | |
| pXMLFormDOMRoot = pElement; | |
| } else if (ePacket == XFA_PacketType::Template) { | |
| // Found a duplicate template packet. | |
| if (pXMLTemplateDOMRoot) | |
| return nullptr; | |
| CXFA_Node* pPacketNode = ParseAsXDPPacket_Template(pElement); | |
| if (pPacketNode) { | |
| pXMLTemplateDOMRoot = pElement; | |
| pXFARootNode->InsertChildAndNotify(pPacketNode, nullptr); | |
| } | |
| } else { | |
| CXFA_Node* pPacketNode = ParseAsXDPPacket(pElement, ePacket); | |
| if (pPacketNode) { | |
| if (packet_info.has_value() && | |
| (packet_info.value().flags & XFA_XDPPACKET_FLAGS_SUPPORTONE) && | |
| pXFARootNode->GetFirstChildByName( | |
| FX_HashCode_GetW(packet_info.value().name, false))) { | |
| return nullptr; | |
| } | |
| pXFARootNode->InsertChildAndNotify(pPacketNode, nullptr); | |
| } | |
| } | |
| } | |
| // No template is found. | |
| if (!pXMLTemplateDOMRoot) | |
| return nullptr; | |
| if (pXMLDatasetsDOMRoot) { | |
| CXFA_Node* pPacketNode = | |
| ParseAsXDPPacket(pXMLDatasetsDOMRoot, XFA_PacketType::Datasets); | |
| if (pPacketNode) | |
| pXFARootNode->InsertChildAndNotify(pPacketNode, nullptr); | |
| } | |
| if (pXMLFormDOMRoot) { | |
| CXFA_Node* pPacketNode = | |
| ParseAsXDPPacket(pXMLFormDOMRoot, XFA_PacketType::Form); | |
| if (pPacketNode) | |
| pXFARootNode->InsertChildAndNotify(pPacketNode, nullptr); | |
| } | |
| pXFARootNode->SetXMLMappingNode(pXMLDocumentNode); | |
| return pXFARootNode; | |
| } | |
| CXFA_Node* CXFA_DocumentBuilder::ParseAsXDPPacket_Config( | |
| CFX_XMLNode* pXMLDocumentNode) { | |
| XFA_PACKETINFO packet = XFA_GetPacketByIndex(XFA_PacketType::Config); | |
| if (!MatchNodeName(pXMLDocumentNode, packet.name, packet.uri, packet.flags)) | |
| return nullptr; | |
| CXFA_Node* pNode = | |
| node_factory_->CreateNode(XFA_PacketType::Config, XFA_Element::Config); | |
| if (!pNode) | |
| return nullptr; | |
| pNode->JSObject()->SetCData(XFA_Attribute::Name, packet.name); | |
| if (!NormalLoader(pNode, pXMLDocumentNode, XFA_PacketType::Config, true)) | |
| return nullptr; | |
| pNode->SetXMLMappingNode(pXMLDocumentNode); | |
| return pNode; | |
| } | |
| CXFA_Node* CXFA_DocumentBuilder::ParseAsXDPPacket_Template( | |
| CFX_XMLNode* pXMLDocumentNode) { | |
| XFA_PACKETINFO packet = XFA_GetPacketByIndex(XFA_PacketType::Template); | |
| if (!MatchNodeName(pXMLDocumentNode, packet.name, packet.uri, packet.flags)) | |
| return nullptr; | |
| CXFA_Node* pNode = node_factory_->CreateNode(XFA_PacketType::Template, | |
| XFA_Element::Template); | |
| if (!pNode) | |
| return nullptr; | |
| pNode->JSObject()->SetCData(XFA_Attribute::Name, packet.name); | |
| CFX_XMLElement* pXMLDocumentElement = ToXMLElement(pXMLDocumentNode); | |
| WideString wsNamespaceURI = pXMLDocumentElement->GetNamespaceURI(); | |
| if (wsNamespaceURI.IsEmpty()) | |
| wsNamespaceURI = pXMLDocumentElement->GetAttribute(L"xmlns:xfa"); | |
| pNode->GetDocument()->RecognizeXFAVersionNumber(wsNamespaceURI); | |
| if (!NormalLoader(pNode, pXMLDocumentNode, XFA_PacketType::Template, true)) | |
| return nullptr; | |
| pNode->SetXMLMappingNode(pXMLDocumentNode); | |
| return pNode; | |
| } | |
| CXFA_Node* CXFA_DocumentBuilder::ParseAsXDPPacket_Form( | |
| CFX_XMLNode* pXMLDocumentNode) { | |
| XFA_PACKETINFO packet = XFA_GetPacketByIndex(XFA_PacketType::Form); | |
| if (!MatchNodeName(pXMLDocumentNode, packet.name, packet.uri, packet.flags)) | |
| return nullptr; | |
| CXFA_Node* pNode = | |
| node_factory_->CreateNode(XFA_PacketType::Form, XFA_Element::Form); | |
| if (!pNode) | |
| return nullptr; | |
| pNode->JSObject()->SetCData(XFA_Attribute::Name, packet.name); | |
| CXFA_Template* pTemplateRoot = | |
| root_node_->GetFirstChildByClass<CXFA_Template>(XFA_Element::Template); | |
| CXFA_Subform* pTemplateChosen = | |
| pTemplateRoot ? pTemplateRoot->GetFirstChildByClass<CXFA_Subform>( | |
| XFA_Element::Subform) | |
| : nullptr; | |
| bool bUseAttribute = true; | |
| if (pTemplateChosen && | |
| pTemplateChosen->JSObject()->GetEnum(XFA_Attribute::RestoreState) != | |
| XFA_AttributeValue::Auto) { | |
| bUseAttribute = false; | |
| } | |
| if (!NormalLoader(pNode, pXMLDocumentNode, XFA_PacketType::Form, | |
| bUseAttribute)) | |
| return nullptr; | |
| pNode->SetXMLMappingNode(pXMLDocumentNode); | |
| return pNode; | |
| } | |
| CXFA_Node* CXFA_DocumentBuilder::ParseAsXDPPacket_Data( | |
| CFX_XMLNode* pXMLDocumentNode) { | |
| XFA_PACKETINFO packet = XFA_GetPacketByIndex(XFA_PacketType::Datasets); | |
| CFX_XMLNode* pDatasetsXMLNode = GetDataSetsFromXDP(pXMLDocumentNode); | |
| if (pDatasetsXMLNode) { | |
| CXFA_Node* pNode = node_factory_->CreateNode(XFA_PacketType::Datasets, | |
| XFA_Element::DataModel); | |
| if (!pNode) | |
| return nullptr; | |
| pNode->JSObject()->SetCData(XFA_Attribute::Name, packet.name); | |
| if (!DataLoader(pNode, pDatasetsXMLNode, false)) | |
| return nullptr; | |
| pNode->SetXMLMappingNode(pDatasetsXMLNode); | |
| return pNode; | |
| } | |
| CFX_XMLNode* pDataXMLNode = nullptr; | |
| if (MatchNodeName(pXMLDocumentNode, L"data", packet.uri, packet.flags)) { | |
| ToXMLElement(pXMLDocumentNode)->RemoveAttribute(L"xmlns:xfa"); | |
| pDataXMLNode = pXMLDocumentNode; | |
| } else { | |
| auto* pDataElement = xml_doc_->CreateNode<CFX_XMLElement>(L"xfa:data"); | |
| pXMLDocumentNode->RemoveSelfIfParented(); | |
| CFX_XMLElement* pElement = ToXMLElement(pXMLDocumentNode); | |
| pElement->RemoveAttribute(L"xmlns:xfa"); | |
| // The node was either removed from the parent above, or already has no | |
| // parent so we can take ownership. | |
| pDataElement->AppendLastChild(pXMLDocumentNode); | |
| pDataXMLNode = pDataElement; | |
| } | |
| if (!pDataXMLNode) | |
| return nullptr; | |
| CXFA_Node* pNode = node_factory_->CreateNode(XFA_PacketType::Datasets, | |
| XFA_Element::DataGroup); | |
| if (!pNode) | |
| return nullptr; | |
| WideString wsLocalName = ToXMLElement(pDataXMLNode)->GetLocalTagName(); | |
| pNode->JSObject()->SetCData(XFA_Attribute::Name, wsLocalName); | |
| if (!DataLoader(pNode, pDataXMLNode, true)) | |
| return nullptr; | |
| pNode->SetXMLMappingNode(pDataXMLNode); | |
| return pNode; | |
| } | |
| CXFA_Node* CXFA_DocumentBuilder::ParseAsXDPPacket_LocaleConnectionSourceSet( | |
| CFX_XMLNode* pXMLDocumentNode, | |
| XFA_PacketType packet_type, | |
| XFA_Element element) { | |
| XFA_PACKETINFO packet = XFA_GetPacketByIndex(packet_type); | |
| if (!MatchNodeName(pXMLDocumentNode, packet.name, packet.uri, packet.flags)) | |
| return nullptr; | |
| CXFA_Node* pNode = node_factory_->CreateNode(packet_type, element); | |
| if (!pNode) | |
| return nullptr; | |
| pNode->JSObject()->SetCData(XFA_Attribute::Name, packet.name); | |
| if (!NormalLoader(pNode, pXMLDocumentNode, packet_type, true)) | |
| return nullptr; | |
| pNode->SetXMLMappingNode(pXMLDocumentNode); | |
| return pNode; | |
| } | |
| CXFA_Node* CXFA_DocumentBuilder::ParseAsXDPPacket_Xdc( | |
| CFX_XMLNode* pXMLDocumentNode) { | |
| XFA_PACKETINFO packet = XFA_GetPacketByIndex(XFA_PacketType::Xdc); | |
| if (!MatchNodeName(pXMLDocumentNode, packet.name, packet.uri, packet.flags)) | |
| return nullptr; | |
| CXFA_Node* pNode = | |
| node_factory_->CreateNode(XFA_PacketType::Xdc, XFA_Element::Xdc); | |
| if (!pNode) | |
| return nullptr; | |
| pNode->JSObject()->SetCData(XFA_Attribute::Name, packet.name); | |
| pNode->SetXMLMappingNode(pXMLDocumentNode); | |
| return pNode; | |
| } | |
| CXFA_Node* CXFA_DocumentBuilder::ParseAsXDPPacket_User( | |
| CFX_XMLNode* pXMLDocumentNode) { | |
| CXFA_Node* pNode = | |
| node_factory_->CreateNode(XFA_PacketType::Xdp, XFA_Element::Packet); | |
| if (!pNode) | |
| return nullptr; | |
| WideString wsName = ToXMLElement(pXMLDocumentNode)->GetLocalTagName(); | |
| pNode->JSObject()->SetCData(XFA_Attribute::Name, wsName); | |
| pNode->SetXMLMappingNode(pXMLDocumentNode); | |
| return pNode; | |
| } | |
| CXFA_Node* CXFA_DocumentBuilder::DataLoader(CXFA_Node* pXFANode, | |
| CFX_XMLNode* pXMLDoc, | |
| bool bDoTransform) { | |
| ParseDataGroup(pXFANode, pXMLDoc, XFA_PacketType::Datasets); | |
| return pXFANode; | |
| } | |
| CXFA_Node* CXFA_DocumentBuilder::NormalLoader(CXFA_Node* pXFANode, | |
| CFX_XMLNode* pXMLDoc, | |
| XFA_PacketType ePacketID, | |
| bool bUseAttribute) { | |
| constexpr size_t kMaxExecuteRecursion = 1000; | |
| if (execute_recursion_depth_ > kMaxExecuteRecursion) | |
| return nullptr; | |
| AutoRestorer<size_t> restorer(&execute_recursion_depth_); | |
| ++execute_recursion_depth_; | |
| bool bOneOfPropertyFound = false; | |
| for (CFX_XMLNode* pXMLChild = pXMLDoc->GetFirstChild(); pXMLChild; | |
| pXMLChild = pXMLChild->GetNextSibling()) { | |
| switch (pXMLChild->GetType()) { | |
| case CFX_XMLNode::Type::kElement: { | |
| CFX_XMLElement* pXMLElement = static_cast<CFX_XMLElement*>(pXMLChild); | |
| WideString wsTagName = pXMLElement->GetLocalTagName(); | |
| XFA_Element eType = XFA_GetElementByName(wsTagName.AsStringView()); | |
| if (eType == XFA_Element::Unknown) | |
| continue; | |
| if (pXFANode->HasPropertyFlags( | |
| eType, | |
| XFA_PROPERTYFLAG_OneOf | XFA_PROPERTYFLAG_DefaultOneOf)) { | |
| if (bOneOfPropertyFound) | |
| break; | |
| bOneOfPropertyFound = true; | |
| } | |
| CXFA_Node* pXFAChild = node_factory_->CreateNode(ePacketID, eType); | |
| if (!pXFAChild) | |
| return nullptr; | |
| if (ePacketID == XFA_PacketType::Config) { | |
| pXFAChild->JSObject()->SetAttributeByEnum(XFA_Attribute::Name, | |
| wsTagName, false); | |
| } | |
| bool IsNeedValue = true; | |
| for (auto it : pXMLElement->GetAttributes()) { | |
| WideString wsAttrName; | |
| GetAttributeLocalName(it.first.AsStringView(), wsAttrName); | |
| if (wsAttrName.EqualsASCII("nil") && it.second.EqualsASCII("true")) | |
| IsNeedValue = false; | |
| Optional<XFA_ATTRIBUTEINFO> attr = | |
| XFA_GetAttributeByName(wsAttrName.AsStringView()); | |
| if (!attr.has_value()) | |
| continue; | |
| if (!bUseAttribute && attr.value().attribute != XFA_Attribute::Name && | |
| attr.value().attribute != XFA_Attribute::Save) { | |
| continue; | |
| } | |
| pXFAChild->JSObject()->SetAttributeByEnum(attr.value().attribute, | |
| it.second, false); | |
| } | |
| pXFANode->InsertChildAndNotify(pXFAChild, nullptr); | |
| if (eType == XFA_Element::Validate || eType == XFA_Element::Locale) { | |
| if (ePacketID == XFA_PacketType::Config) | |
| ParseContentNode(pXFAChild, pXMLElement, ePacketID); | |
| else | |
| NormalLoader(pXFAChild, pXMLElement, ePacketID, bUseAttribute); | |
| break; | |
| } | |
| switch (pXFAChild->GetObjectType()) { | |
| case XFA_ObjectType::ContentNode: | |
| case XFA_ObjectType::TextNode: | |
| case XFA_ObjectType::NodeC: | |
| case XFA_ObjectType::NodeV: | |
| if (IsNeedValue) | |
| ParseContentNode(pXFAChild, pXMLElement, ePacketID); | |
| break; | |
| default: | |
| NormalLoader(pXFAChild, pXMLElement, ePacketID, bUseAttribute); | |
| break; | |
| } | |
| } break; | |
| case CFX_XMLNode::Type::kInstruction: | |
| ParseInstruction(pXFANode, ToXMLInstruction(pXMLChild), ePacketID); | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| return pXFANode; | |
| } | |
| void CXFA_DocumentBuilder::ParseContentNode(CXFA_Node* pXFANode, | |
| CFX_XMLNode* pXMLNode, | |
| XFA_PacketType ePacketID) { | |
| XFA_Element element = XFA_Element::Sharptext; | |
| if (pXFANode->GetElementType() == XFA_Element::ExData) { | |
| WideString wsContentType = | |
| pXFANode->JSObject()->GetCData(XFA_Attribute::ContentType); | |
| if (wsContentType.EqualsASCII("text/html")) | |
| element = XFA_Element::SharpxHTML; | |
| else if (wsContentType.EqualsASCII("text/xml")) | |
| element = XFA_Element::Sharpxml; | |
| } | |
| if (element == XFA_Element::SharpxHTML) | |
| pXFANode->SetXMLMappingNode(pXMLNode); | |
| WideString wsValue; | |
| for (CFX_XMLNode* pXMLChild = pXMLNode->GetFirstChild(); pXMLChild; | |
| pXMLChild = pXMLChild->GetNextSibling()) { | |
| CFX_XMLNode::Type eNodeType = pXMLChild->GetType(); | |
| if (eNodeType == CFX_XMLNode::Type::kInstruction) | |
| continue; | |
| CFX_XMLElement* pElement = ToXMLElement(pXMLChild); | |
| if (element == XFA_Element::SharpxHTML) { | |
| if (!pElement) | |
| break; | |
| if (XFA_RecognizeRichText(pElement)) | |
| wsValue += GetPlainTextFromRichText(pElement); | |
| } else if (element == XFA_Element::Sharpxml) { | |
| if (!pElement) | |
| break; | |
| ConvertXMLToPlainText(pElement, wsValue); | |
| } else { | |
| if (pElement) | |
| break; | |
| CFX_XMLText* pText = ToXMLText(pXMLChild); | |
| if (pText) | |
| wsValue = pText->GetText(); | |
| } | |
| break; | |
| } | |
| if (!wsValue.IsEmpty()) { | |
| if (pXFANode->IsContentNode()) { | |
| CXFA_Node* pContentRawDataNode = | |
| node_factory_->CreateNode(ePacketID, element); | |
| DCHECK(pContentRawDataNode); | |
| pContentRawDataNode->JSObject()->SetCData(XFA_Attribute::Value, wsValue); | |
| pXFANode->InsertChildAndNotify(pContentRawDataNode, nullptr); | |
| } else { | |
| pXFANode->JSObject()->SetCData(XFA_Attribute::Value, wsValue); | |
| } | |
| } | |
| } | |
| void CXFA_DocumentBuilder::ParseDataGroup(CXFA_Node* pXFANode, | |
| CFX_XMLNode* pXMLNode, | |
| XFA_PacketType ePacketID) { | |
| for (CFX_XMLNode* pXMLChild = pXMLNode->GetFirstChild(); pXMLChild; | |
| pXMLChild = pXMLChild->GetNextSibling()) { | |
| switch (pXMLChild->GetType()) { | |
| case CFX_XMLNode::Type::kElement: { | |
| CFX_XMLElement* pXMLElement = static_cast<CFX_XMLElement*>(pXMLChild); | |
| WideString wsNamespaceURI = pXMLElement->GetNamespaceURI(); | |
| if (wsNamespaceURI.EqualsASCII( | |
| "http://www.xfa.com/schema/xfa-package/") || | |
| wsNamespaceURI.EqualsASCII( | |
| "http://www.xfa.org/schema/xfa-package/") || | |
| wsNamespaceURI.EqualsASCII( | |
| "http://www.w3.org/2001/XMLSchema-instance")) { | |
| continue; | |
| } | |
| XFA_Element eNodeType = XFA_Element::DataModel; | |
| if (eNodeType == XFA_Element::DataModel) { | |
| Optional<WideString> wsDataNodeAttr = | |
| FindAttributeWithNS(pXMLElement, L"dataNode", | |
| L"http://www.xfa.org/schema/xfa-data/1.0/"); | |
| if (wsDataNodeAttr.has_value()) { | |
| if (wsDataNodeAttr.value().EqualsASCII("dataGroup")) | |
| eNodeType = XFA_Element::DataGroup; | |
| else if (wsDataNodeAttr.value().EqualsASCII("dataValue")) | |
| eNodeType = XFA_Element::DataValue; | |
| } | |
| } | |
| if (eNodeType == XFA_Element::DataModel) { | |
| Optional<WideString> wsContentType = | |
| FindAttributeWithNS(pXMLElement, L"contentType", | |
| L"http://www.xfa.org/schema/xfa-data/1.0/"); | |
| if (wsContentType.has_value() && !wsContentType.value().IsEmpty()) | |
| eNodeType = XFA_Element::DataValue; | |
| } | |
| if (eNodeType == XFA_Element::DataModel) { | |
| for (CFX_XMLNode* pXMLDataChild = pXMLElement->GetFirstChild(); | |
| pXMLDataChild; pXMLDataChild = pXMLDataChild->GetNextSibling()) { | |
| CFX_XMLElement* pElement = ToXMLElement(pXMLDataChild); | |
| if (pElement && !XFA_RecognizeRichText(pElement)) { | |
| eNodeType = XFA_Element::DataGroup; | |
| break; | |
| } | |
| } | |
| } | |
| if (eNodeType == XFA_Element::DataModel) | |
| eNodeType = XFA_Element::DataValue; | |
| CXFA_Node* pXFAChild = | |
| node_factory_->CreateNode(XFA_PacketType::Datasets, eNodeType); | |
| if (!pXFAChild) | |
| return; | |
| pXFAChild->JSObject()->SetCData(XFA_Attribute::Name, | |
| pXMLElement->GetLocalTagName()); | |
| bool bNeedValue = true; | |
| for (auto it : pXMLElement->GetAttributes()) { | |
| WideString wsName; | |
| WideString wsNS; | |
| if (!ResolveAttribute(pXMLElement, it.first, wsName, wsNS)) { | |
| continue; | |
| } | |
| if (wsName.EqualsASCII("nil") && it.second.EqualsASCII("true")) { | |
| bNeedValue = false; | |
| continue; | |
| } | |
| if (wsNS.EqualsASCII("http://www.xfa.com/schema/xfa-package/") || | |
| wsNS.EqualsASCII("http://www.xfa.org/schema/xfa-package/") || | |
| wsNS.EqualsASCII("http://www.w3.org/2001/XMLSchema-instance") || | |
| wsNS.EqualsASCII("http://www.xfa.org/schema/xfa-data/1.0/")) { | |
| continue; | |
| } | |
| CXFA_Node* pXFAMetaData = node_factory_->CreateNode( | |
| XFA_PacketType::Datasets, XFA_Element::DataValue); | |
| if (!pXFAMetaData) | |
| return; | |
| pXFAMetaData->JSObject()->SetCData(XFA_Attribute::Name, wsName); | |
| pXFAMetaData->JSObject()->SetCData(XFA_Attribute::QualifiedName, | |
| it.first); | |
| pXFAMetaData->JSObject()->SetCData(XFA_Attribute::Value, it.second); | |
| pXFAMetaData->JSObject()->SetEnum( | |
| XFA_Attribute::Contains, XFA_AttributeValue::MetaData, false); | |
| pXFAChild->InsertChildAndNotify(pXFAMetaData, nullptr); | |
| pXFAMetaData->SetXMLMappingNode(pXMLElement); | |
| pXFAMetaData->SetFlag(XFA_NodeFlag_Initialized); | |
| } | |
| if (!bNeedValue) | |
| pXMLElement->RemoveAttribute(L"xsi:nil"); | |
| pXFANode->InsertChildAndNotify(pXFAChild, nullptr); | |
| if (eNodeType == XFA_Element::DataGroup) | |
| ParseDataGroup(pXFAChild, pXMLElement, ePacketID); | |
| else if (bNeedValue) | |
| ParseDataValue(pXFAChild, pXMLChild, XFA_PacketType::Datasets); | |
| pXFAChild->SetXMLMappingNode(pXMLElement); | |
| pXFAChild->SetFlag(XFA_NodeFlag_Initialized); | |
| continue; | |
| } | |
| case CFX_XMLNode::Type::kCharData: | |
| case CFX_XMLNode::Type::kText: { | |
| CFX_XMLText* pXMLText = ToXMLText(pXMLChild); | |
| WideString wsText = pXMLText->GetText(); | |
| if (IsStringAllWhitespace(wsText)) | |
| continue; | |
| CXFA_Node* pXFAChild = node_factory_->CreateNode( | |
| XFA_PacketType::Datasets, XFA_Element::DataValue); | |
| if (!pXFAChild) | |
| return; | |
| pXFAChild->JSObject()->SetCData(XFA_Attribute::Value, wsText); | |
| pXFANode->InsertChildAndNotify(pXFAChild, nullptr); | |
| pXFAChild->SetXMLMappingNode(pXMLText); | |
| pXFAChild->SetFlag(XFA_NodeFlag_Initialized); | |
| continue; | |
| } | |
| default: | |
| continue; | |
| } | |
| } | |
| } | |
| void CXFA_DocumentBuilder::ParseDataValue(CXFA_Node* pXFANode, | |
| CFX_XMLNode* pXMLNode, | |
| XFA_PacketType ePacketID) { | |
| CFX_WideTextBuf wsValueTextBuf; | |
| CFX_WideTextBuf wsCurValueTextBuf; | |
| bool bMarkAsCompound = false; | |
| CFX_XMLNode* pXMLCurValueNode = nullptr; | |
| for (CFX_XMLNode* pXMLChild = pXMLNode->GetFirstChild(); pXMLChild; | |
| pXMLChild = pXMLChild->GetNextSibling()) { | |
| CFX_XMLNode::Type eNodeType = pXMLChild->GetType(); | |
| if (eNodeType == CFX_XMLNode::Type::kInstruction) | |
| continue; | |
| CFX_XMLText* pText = ToXMLText(pXMLChild); | |
| if (pText) { | |
| WideString wsText = pText->GetText(); | |
| if (!pXMLCurValueNode) | |
| pXMLCurValueNode = pXMLChild; | |
| wsCurValueTextBuf << wsText; | |
| continue; | |
| } | |
| if (XFA_RecognizeRichText(ToXMLElement(pXMLChild))) { | |
| WideString wsText = GetPlainTextFromRichText(ToXMLElement(pXMLChild)); | |
| if (!pXMLCurValueNode) | |
| pXMLCurValueNode = pXMLChild; | |
| wsCurValueTextBuf << wsText; | |
| continue; | |
| } | |
| bMarkAsCompound = true; | |
| if (pXMLCurValueNode) { | |
| WideString wsCurValue = wsCurValueTextBuf.MakeString(); | |
| if (!wsCurValue.IsEmpty()) { | |
| CXFA_Node* pXFAChild = | |
| node_factory_->CreateNode(ePacketID, XFA_Element::DataValue); | |
| if (!pXFAChild) | |
| return; | |
| pXFAChild->JSObject()->SetCData(XFA_Attribute::Name, WideString()); | |
| pXFAChild->JSObject()->SetCData(XFA_Attribute::Value, wsCurValue); | |
| pXFANode->InsertChildAndNotify(pXFAChild, nullptr); | |
| pXFAChild->SetXMLMappingNode(pXMLCurValueNode); | |
| pXFAChild->SetFlag(XFA_NodeFlag_Initialized); | |
| wsValueTextBuf << wsCurValue; | |
| wsCurValueTextBuf.Clear(); | |
| } | |
| pXMLCurValueNode = nullptr; | |
| } | |
| CXFA_Node* pXFAChild = | |
| node_factory_->CreateNode(ePacketID, XFA_Element::DataValue); | |
| if (!pXFAChild) | |
| return; | |
| WideString wsNodeStr = ToXMLElement(pXMLChild)->GetLocalTagName(); | |
| pXFAChild->JSObject()->SetCData(XFA_Attribute::Name, wsNodeStr); | |
| ParseDataValue(pXFAChild, pXMLChild, ePacketID); | |
| pXFANode->InsertChildAndNotify(pXFAChild, nullptr); | |
| pXFAChild->SetXMLMappingNode(pXMLChild); | |
| pXFAChild->SetFlag(XFA_NodeFlag_Initialized); | |
| WideString wsCurValue = | |
| pXFAChild->JSObject()->GetCData(XFA_Attribute::Value); | |
| wsValueTextBuf << wsCurValue; | |
| } | |
| if (pXMLCurValueNode) { | |
| WideString wsCurValue = wsCurValueTextBuf.MakeString(); | |
| if (!wsCurValue.IsEmpty()) { | |
| if (bMarkAsCompound) { | |
| CXFA_Node* pXFAChild = | |
| node_factory_->CreateNode(ePacketID, XFA_Element::DataValue); | |
| if (!pXFAChild) | |
| return; | |
| pXFAChild->JSObject()->SetCData(XFA_Attribute::Name, WideString()); | |
| pXFAChild->JSObject()->SetCData(XFA_Attribute::Value, wsCurValue); | |
| pXFANode->InsertChildAndNotify(pXFAChild, nullptr); | |
| pXFAChild->SetXMLMappingNode(pXMLCurValueNode); | |
| pXFAChild->SetFlag(XFA_NodeFlag_Initialized); | |
| } | |
| wsValueTextBuf << wsCurValue; | |
| wsCurValueTextBuf.Clear(); | |
| } | |
| pXMLCurValueNode = nullptr; | |
| } | |
| WideString wsNodeValue = wsValueTextBuf.MakeString(); | |
| pXFANode->JSObject()->SetCData(XFA_Attribute::Value, wsNodeValue); | |
| } | |
| void CXFA_DocumentBuilder::ParseInstruction(CXFA_Node* pXFANode, | |
| CFX_XMLInstruction* pXMLInstruction, | |
| XFA_PacketType ePacketID) { | |
| const std::vector<WideString>& target_data = pXMLInstruction->GetTargetData(); | |
| if (pXMLInstruction->IsOriginalXFAVersion()) { | |
| if (target_data.size() > 1 && | |
| (pXFANode->GetDocument()->RecognizeXFAVersionNumber(target_data[0]) != | |
| XFA_VERSION_UNKNOWN) && | |
| target_data[1].EqualsASCII("v2.7-scripting:1")) { | |
| pXFANode->GetDocument()->set_is_scripting(); | |
| } | |
| return; | |
| } | |
| if (pXMLInstruction->IsAcrobat()) { | |
| if (target_data.size() > 1 && target_data[0].EqualsASCII("JavaScript") && | |
| target_data[1].EqualsASCII("strictScoping")) { | |
| pXFANode->GetDocument()->set_is_strict_scoping(); | |
| } | |
| } | |
| } |