• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
MergeNodeRecurse(CXFA_Document * pDocument,CXFA_Node * pDestNodeParent,CXFA_Node * pProtoNode)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 
MergeNode(CXFA_Document * pDocument,CXFA_Node * pDestNode,CXFA_Node * pProtoNode)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 
CXFA_Document(CXFA_DocumentParser * pParser)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 
~CXFA_Document()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 
GetLayoutProcessor()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 
GetDocLayout()115 CXFA_LayoutProcessor* CXFA_Document::GetDocLayout() {
116   return GetLayoutProcessor();
117 }
118 
ClearLayoutData()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 
SetRoot(CXFA_Node * pNewRoot)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 
GetXMLDoc() const139 CFX_XMLDoc* CXFA_Document::GetXMLDoc() const {
140   return m_pParser->GetXMLDoc();
141 }
142 
GetNotify() const143 CXFA_FFNotify* CXFA_Document::GetNotify() const {
144   return m_pParser->GetNotify();
145 }
146 
GetXFAObject(XFA_HashCode dwNodeNameHash)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 
CreateNode(XFA_PacketType packet,XFA_Element eElement)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 
AddPurgeNode(CXFA_Node * pNode)234 void CXFA_Document::AddPurgeNode(CXFA_Node* pNode) {
235   m_PurgeNodes.insert(pNode);
236 }
237 
RemovePurgeNode(CXFA_Node * pNode)238 bool CXFA_Document::RemovePurgeNode(CXFA_Node* pNode) {
239   return !!m_PurgeNodes.erase(pNode);
240 }
241 
SetFlag(uint32_t dwFlag,bool bOn)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 
IsInteractive()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 
GetLocalMgr()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 
InitScriptContext(v8::Isolate * pIsolate)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.
GetScriptContext()297 CFXJSE_Engine* CXFA_Document::GetScriptContext() {
298   ASSERT(m_pScriptContext);
299   return m_pScriptContext.get();
300 }
301 
RecognizeXFAVersionNumber(const WideString & wsTemplateNS)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 
GetNodeByID(CXFA_Node * pRoot,const WideStringView & wsID)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 
DoProtoMerge()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