1 // Copyright 2014 PDFium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com 6 7 #include "xfa/fxfa/parser/cxfa_document.h" 8 9 #include "core/fxcrt/fx_extension.h" 10 #include "fxjs/cfxjse_engine.h" 11 #include "xfa/fxfa/cxfa_ffnotify.h" 12 #include "xfa/fxfa/parser/cscript_datawindow.h" 13 #include "xfa/fxfa/parser/cscript_eventpseudomodel.h" 14 #include "xfa/fxfa/parser/cscript_hostpseudomodel.h" 15 #include "xfa/fxfa/parser/cscript_layoutpseudomodel.h" 16 #include "xfa/fxfa/parser/cscript_logpseudomodel.h" 17 #include "xfa/fxfa/parser/cscript_signaturepseudomodel.h" 18 #include "xfa/fxfa/parser/cxfa_datagroup.h" 19 #include "xfa/fxfa/parser/cxfa_document_parser.h" 20 #include "xfa/fxfa/parser/cxfa_interactive.h" 21 #include "xfa/fxfa/parser/cxfa_layoutprocessor.h" 22 #include "xfa/fxfa/parser/cxfa_localemgr.h" 23 #include "xfa/fxfa/parser/cxfa_node.h" 24 #include "xfa/fxfa/parser/cxfa_pdf.h" 25 #include "xfa/fxfa/parser/cxfa_present.h" 26 #include "xfa/fxfa/parser/cxfa_traversestrategy_xfanode.h" 27 #include "xfa/fxfa/parser/xfa_resolvenode_rs.h" 28 #include "xfa/fxfa/parser/xfa_utils.h" 29 30 namespace { 31 32 constexpr const wchar_t kTemplateNS[] = 33 L"http://www.xfa.org/schema/xfa-template/"; 34 35 void MergeNodeRecurse(CXFA_Document* pDocument, 36 CXFA_Node* pDestNodeParent, 37 CXFA_Node* pProtoNode) { 38 CXFA_Node* pExistingNode = nullptr; 39 for (CXFA_Node* pFormChild = pDestNodeParent->GetFirstChild(); pFormChild; 40 pFormChild = pFormChild->GetNextSibling()) { 41 if (pFormChild->GetElementType() == pProtoNode->GetElementType() && 42 pFormChild->GetNameHash() == pProtoNode->GetNameHash() && 43 pFormChild->IsUnusedNode()) { 44 pFormChild->ClearFlag(XFA_NodeFlag_UnusedNode); 45 pExistingNode = pFormChild; 46 break; 47 } 48 } 49 50 if (pExistingNode) { 51 pExistingNode->SetTemplateNode(pProtoNode); 52 for (CXFA_Node* pTemplateChild = pProtoNode->GetFirstChild(); 53 pTemplateChild; pTemplateChild = pTemplateChild->GetNextSibling()) { 54 MergeNodeRecurse(pDocument, pExistingNode, pTemplateChild); 55 } 56 return; 57 } 58 CXFA_Node* pNewNode = pProtoNode->Clone(true); 59 pNewNode->SetTemplateNode(pProtoNode); 60 pDestNodeParent->InsertChild(pNewNode, nullptr); 61 } 62 63 void MergeNode(CXFA_Document* pDocument, 64 CXFA_Node* pDestNode, 65 CXFA_Node* pProtoNode) { 66 { 67 CXFA_NodeIterator sIterator(pDestNode); 68 for (CXFA_Node* pNode = sIterator.GetCurrent(); pNode; 69 pNode = sIterator.MoveToNext()) { 70 pNode->SetFlag(XFA_NodeFlag_UnusedNode, true); 71 } 72 } 73 pDestNode->SetTemplateNode(pProtoNode); 74 for (CXFA_Node* pTemplateChild = pProtoNode->GetFirstChild(); pTemplateChild; 75 pTemplateChild = pTemplateChild->GetNextSibling()) { 76 MergeNodeRecurse(pDocument, pDestNode, pTemplateChild); 77 } 78 { 79 CXFA_NodeIterator sIterator(pDestNode); 80 for (CXFA_Node* pNode = sIterator.GetCurrent(); pNode; 81 pNode = sIterator.MoveToNext()) { 82 pNode->ClearFlag(XFA_NodeFlag_UnusedNode); 83 } 84 } 85 } 86 87 } // namespace 88 89 CXFA_Document::CXFA_Document(CXFA_DocumentParser* pParser) 90 : m_pParser(pParser), 91 m_pRootNode(nullptr), 92 m_eCurVersionMode(XFA_VERSION_DEFAULT), 93 m_dwDocFlags(0) { 94 ASSERT(m_pParser); 95 } 96 97 CXFA_Document::~CXFA_Document() { 98 // Remove all the bindings before freeing the node as the ownership is wonky. 99 if (m_pRootNode) 100 m_pRootNode->ReleaseBindingNodes(); 101 102 delete m_pRootNode; 103 104 for (CXFA_Node* pNode : m_PurgeNodes) 105 delete pNode; 106 m_PurgeNodes.clear(); 107 } 108 109 CXFA_LayoutProcessor* CXFA_Document::GetLayoutProcessor() { 110 if (!m_pLayoutProcessor) 111 m_pLayoutProcessor = pdfium::MakeUnique<CXFA_LayoutProcessor>(this); 112 return m_pLayoutProcessor.get(); 113 } 114 115 CXFA_LayoutProcessor* CXFA_Document::GetDocLayout() { 116 return GetLayoutProcessor(); 117 } 118 119 void CXFA_Document::ClearLayoutData() { 120 m_pLayoutProcessor.reset(); 121 m_pScriptContext.reset(); 122 m_pLocalMgr.reset(); 123 m_pScriptDataWindow.reset(); 124 m_pScriptEvent.reset(); 125 m_pScriptHost.reset(); 126 m_pScriptLog.reset(); 127 m_pScriptLayout.reset(); 128 m_pScriptSignature.reset(); 129 } 130 131 void CXFA_Document::SetRoot(CXFA_Node* pNewRoot) { 132 if (m_pRootNode) 133 AddPurgeNode(m_pRootNode); 134 135 m_pRootNode = pNewRoot; 136 RemovePurgeNode(pNewRoot); 137 } 138 139 CFX_XMLDoc* CXFA_Document::GetXMLDoc() const { 140 return m_pParser->GetXMLDoc(); 141 } 142 143 CXFA_FFNotify* CXFA_Document::GetNotify() const { 144 return m_pParser->GetNotify(); 145 } 146 147 CXFA_Object* CXFA_Document::GetXFAObject(XFA_HashCode dwNodeNameHash) { 148 switch (dwNodeNameHash) { 149 case XFA_HASHCODE_Data: { 150 CXFA_Node* pDatasetsNode = ToNode(GetXFAObject(XFA_HASHCODE_Datasets)); 151 if (!pDatasetsNode) 152 return nullptr; 153 154 for (CXFA_DataGroup* pDatasetsChild = 155 pDatasetsNode->GetFirstChildByClass<CXFA_DataGroup>( 156 XFA_Element::DataGroup); 157 pDatasetsChild; 158 pDatasetsChild = 159 pDatasetsChild->GetNextSameClassSibling<CXFA_DataGroup>( 160 XFA_Element::DataGroup)) { 161 if (pDatasetsChild->GetNameHash() != XFA_HASHCODE_Data) 162 continue; 163 164 Optional<WideString> namespaceURI = 165 pDatasetsChild->JSObject()->TryNamespace(); 166 if (!namespaceURI) 167 continue; 168 169 Optional<WideString> datasetsURI = 170 pDatasetsNode->JSObject()->TryNamespace(); 171 if (!datasetsURI) 172 continue; 173 if (*namespaceURI == *datasetsURI) 174 return pDatasetsChild; 175 } 176 return nullptr; 177 } 178 case XFA_HASHCODE_Record: { 179 CXFA_Node* pData = ToNode(GetXFAObject(XFA_HASHCODE_Data)); 180 return pData ? pData->GetFirstChildByClass<CXFA_DataGroup>( 181 XFA_Element::DataGroup) 182 : nullptr; 183 } 184 case XFA_HASHCODE_DataWindow: { 185 if (!m_pScriptDataWindow) 186 m_pScriptDataWindow = pdfium::MakeUnique<CScript_DataWindow>(this); 187 return m_pScriptDataWindow.get(); 188 } 189 case XFA_HASHCODE_Event: { 190 if (!m_pScriptEvent) 191 m_pScriptEvent = pdfium::MakeUnique<CScript_EventPseudoModel>(this); 192 return m_pScriptEvent.get(); 193 } 194 case XFA_HASHCODE_Host: { 195 if (!m_pScriptHost) 196 m_pScriptHost = pdfium::MakeUnique<CScript_HostPseudoModel>(this); 197 return m_pScriptHost.get(); 198 } 199 case XFA_HASHCODE_Log: { 200 if (!m_pScriptLog) 201 m_pScriptLog = pdfium::MakeUnique<CScript_LogPseudoModel>(this); 202 return m_pScriptLog.get(); 203 } 204 case XFA_HASHCODE_Signature: { 205 if (!m_pScriptSignature) 206 m_pScriptSignature = 207 pdfium::MakeUnique<CScript_SignaturePseudoModel>(this); 208 return m_pScriptSignature.get(); 209 } 210 case XFA_HASHCODE_Layout: { 211 if (!m_pScriptLayout) 212 m_pScriptLayout = pdfium::MakeUnique<CScript_LayoutPseudoModel>(this); 213 return m_pScriptLayout.get(); 214 } 215 default: 216 return m_pRootNode->GetFirstChildByName(dwNodeNameHash); 217 } 218 } 219 220 CXFA_Node* CXFA_Document::CreateNode(XFA_PacketType packet, 221 XFA_Element eElement) { 222 if (eElement == XFA_Element::Unknown) 223 return nullptr; 224 225 std::unique_ptr<CXFA_Node> pNode = CXFA_Node::Create(this, eElement, packet); 226 if (!pNode) 227 return nullptr; 228 229 // TODO(dsinclair): AddPrugeNode should take ownership of the pointer. 230 AddPurgeNode(pNode.get()); 231 return pNode.release(); 232 } 233 234 void CXFA_Document::AddPurgeNode(CXFA_Node* pNode) { 235 m_PurgeNodes.insert(pNode); 236 } 237 238 bool CXFA_Document::RemovePurgeNode(CXFA_Node* pNode) { 239 return !!m_PurgeNodes.erase(pNode); 240 } 241 242 void CXFA_Document::SetFlag(uint32_t dwFlag, bool bOn) { 243 if (bOn) 244 m_dwDocFlags |= dwFlag; 245 else 246 m_dwDocFlags &= ~dwFlag; 247 } 248 249 bool CXFA_Document::IsInteractive() { 250 if (m_dwDocFlags & XFA_DOCFLAG_HasInteractive) 251 return !!(m_dwDocFlags & XFA_DOCFLAG_Interactive); 252 253 CXFA_Node* pConfig = ToNode(GetXFAObject(XFA_HASHCODE_Config)); 254 if (!pConfig) 255 return false; 256 257 CXFA_Present* pPresent = 258 pConfig->GetFirstChildByClass<CXFA_Present>(XFA_Element::Present); 259 if (!pPresent) 260 return false; 261 262 CXFA_Pdf* pPDF = pPresent->GetFirstChildByClass<CXFA_Pdf>(XFA_Element::Pdf); 263 if (!pPDF) 264 return false; 265 266 CXFA_Interactive* pFormFiller = 267 pPDF->GetChild<CXFA_Interactive>(0, XFA_Element::Interactive, false); 268 if (pFormFiller) { 269 m_dwDocFlags |= XFA_DOCFLAG_HasInteractive; 270 271 WideString wsInteractive = pFormFiller->JSObject()->GetContent(false); 272 if (wsInteractive == L"1") { 273 m_dwDocFlags |= XFA_DOCFLAG_Interactive; 274 return true; 275 } 276 } 277 return false; 278 } 279 280 CXFA_LocaleMgr* CXFA_Document::GetLocalMgr() { 281 if (!m_pLocalMgr) { 282 m_pLocalMgr = pdfium::MakeUnique<CXFA_LocaleMgr>( 283 ToNode(GetXFAObject(XFA_HASHCODE_LocaleSet)), 284 GetNotify()->GetAppProvider()->GetLanguage()); 285 } 286 return m_pLocalMgr.get(); 287 } 288 289 CFXJSE_Engine* CXFA_Document::InitScriptContext(v8::Isolate* pIsolate) { 290 ASSERT(!m_pScriptContext); 291 m_pScriptContext = pdfium::MakeUnique<CFXJSE_Engine>(this, pIsolate); 292 return m_pScriptContext.get(); 293 } 294 295 // We have to call |InitScriptContext| before any calls to |GetScriptContext| 296 // or the context won't have an isolate set into it. 297 CFXJSE_Engine* CXFA_Document::GetScriptContext() { 298 ASSERT(m_pScriptContext); 299 return m_pScriptContext.get(); 300 } 301 302 XFA_VERSION CXFA_Document::RecognizeXFAVersionNumber( 303 const WideString& wsTemplateNS) { 304 WideStringView wsTemplateURIPrefix(kTemplateNS); 305 size_t nPrefixLength = wsTemplateURIPrefix.GetLength(); 306 if (WideStringView(wsTemplateNS.c_str(), wsTemplateNS.GetLength()) != 307 wsTemplateURIPrefix) { 308 return XFA_VERSION_UNKNOWN; 309 } 310 auto nDotPos = wsTemplateNS.Find('.', nPrefixLength); 311 if (!nDotPos.has_value()) 312 return XFA_VERSION_UNKNOWN; 313 314 int8_t iMajor = FXSYS_wtoi( 315 wsTemplateNS.Mid(nPrefixLength, nDotPos.value() - nPrefixLength).c_str()); 316 int8_t iMinor = 317 FXSYS_wtoi(wsTemplateNS 318 .Mid(nDotPos.value() + 1, 319 wsTemplateNS.GetLength() - nDotPos.value() - 2) 320 .c_str()); 321 XFA_VERSION eVersion = (XFA_VERSION)((int32_t)iMajor * 100 + iMinor); 322 if (eVersion < XFA_VERSION_MIN || eVersion > XFA_VERSION_MAX) 323 return XFA_VERSION_UNKNOWN; 324 325 m_eCurVersionMode = eVersion; 326 return eVersion; 327 } 328 329 CXFA_Node* CXFA_Document::GetNodeByID(CXFA_Node* pRoot, 330 const WideStringView& wsID) { 331 if (!pRoot || wsID.IsEmpty()) 332 return nullptr; 333 334 CXFA_NodeIterator sIterator(pRoot); 335 for (CXFA_Node* pNode = sIterator.GetCurrent(); pNode; 336 pNode = sIterator.MoveToNext()) { 337 WideString wsIDVal = pNode->JSObject()->GetCData(XFA_Attribute::Id); 338 if (!wsIDVal.IsEmpty() && wsIDVal == wsID) 339 return pNode; 340 } 341 return nullptr; 342 } 343 344 void CXFA_Document::DoProtoMerge() { 345 CXFA_Node* pTemplateRoot = ToNode(GetXFAObject(XFA_HASHCODE_Template)); 346 if (!pTemplateRoot) 347 return; 348 349 std::map<uint32_t, CXFA_Node*> mIDMap; 350 std::set<CXFA_Node*> sUseNodes; 351 CXFA_NodeIterator sIterator(pTemplateRoot); 352 for (CXFA_Node* pNode = sIterator.GetCurrent(); pNode; 353 pNode = sIterator.MoveToNext()) { 354 WideString wsIDVal = pNode->JSObject()->GetCData(XFA_Attribute::Id); 355 if (!wsIDVal.IsEmpty()) 356 mIDMap[FX_HashCode_GetW(wsIDVal.AsStringView(), false)] = pNode; 357 358 WideString wsUseVal = pNode->JSObject()->GetCData(XFA_Attribute::Use); 359 if (!wsUseVal.IsEmpty()) { 360 sUseNodes.insert(pNode); 361 } else { 362 wsUseVal = pNode->JSObject()->GetCData(XFA_Attribute::Usehref); 363 if (!wsUseVal.IsEmpty()) 364 sUseNodes.insert(pNode); 365 } 366 } 367 368 for (CXFA_Node* pUseHrefNode : sUseNodes) { 369 WideStringView wsURI; 370 WideStringView wsID; 371 WideStringView wsSOM; 372 373 WideString wsUseVal = 374 pUseHrefNode->JSObject()->GetCData(XFA_Attribute::Usehref); 375 if (!wsUseVal.IsEmpty()) { 376 auto uSharpPos = wsUseVal.Find('#'); 377 if (!uSharpPos.has_value()) { 378 wsURI = wsUseVal.AsStringView(); 379 } else { 380 wsURI = WideStringView(wsUseVal.c_str(), uSharpPos.value()); 381 size_t uLen = wsUseVal.GetLength(); 382 if (uLen >= uSharpPos.value() + 5 && 383 WideStringView(wsUseVal.c_str() + uSharpPos.value(), 5) == 384 L"#som(" && 385 wsUseVal[uLen - 1] == ')') { 386 wsSOM = WideStringView(wsUseVal.c_str() + uSharpPos.value() + 5, 387 uLen - 1 - uSharpPos.value() - 5); 388 } else { 389 wsID = WideStringView(wsUseVal.c_str() + uSharpPos.value() + 1, 390 uLen - uSharpPos.value() - 1); 391 } 392 } 393 } else { 394 wsUseVal = pUseHrefNode->JSObject()->GetCData(XFA_Attribute::Use); 395 if (!wsUseVal.IsEmpty()) { 396 if (wsUseVal[0] == '#') 397 wsID = WideStringView(wsUseVal.c_str() + 1, wsUseVal.GetLength() - 1); 398 else 399 wsSOM = WideStringView(wsUseVal.c_str(), wsUseVal.GetLength()); 400 } 401 } 402 403 if (!wsURI.IsEmpty() && wsURI != L".") 404 continue; 405 406 CXFA_Node* pProtoNode = nullptr; 407 if (!wsSOM.IsEmpty()) { 408 uint32_t dwFlag = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Attributes | 409 XFA_RESOLVENODE_Properties | XFA_RESOLVENODE_Parent | 410 XFA_RESOLVENODE_Siblings; 411 XFA_RESOLVENODE_RS resolveNodeRS; 412 int32_t iRet = m_pScriptContext->ResolveObjects( 413 pUseHrefNode, wsSOM, &resolveNodeRS, dwFlag, nullptr); 414 if (iRet > 0 && resolveNodeRS.objects.front()->IsNode()) 415 pProtoNode = resolveNodeRS.objects.front()->AsNode(); 416 } else if (!wsID.IsEmpty()) { 417 auto it = mIDMap.find(FX_HashCode_GetW(wsID, false)); 418 if (it == mIDMap.end()) 419 continue; 420 pProtoNode = it->second; 421 } 422 if (!pProtoNode) 423 continue; 424 425 MergeNode(this, pUseHrefNode, pProtoNode); 426 } 427 } 428