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