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 "core/fxcrt/fx_ext.h"
8 #include "xfa/fxfa/app/xfa_ffnotify.h"
9 #include "xfa/fxfa/parser/cscript_datawindow.h"
10 #include "xfa/fxfa/parser/cscript_eventpseudomodel.h"
11 #include "xfa/fxfa/parser/cscript_hostpseudomodel.h"
12 #include "xfa/fxfa/parser/cscript_layoutpseudomodel.h"
13 #include "xfa/fxfa/parser/cscript_logpseudomodel.h"
14 #include "xfa/fxfa/parser/cscript_signaturepseudomodel.h"
15 #include "xfa/fxfa/parser/cxfa_document.h"
16 #include "xfa/fxfa/parser/cxfa_document_parser.h"
17 #include "xfa/fxfa/parser/cxfa_layoutprocessor.h"
18 #include "xfa/fxfa/parser/cxfa_scriptcontext.h"
19 #include "xfa/fxfa/parser/xfa_localemgr.h"
20 #include "xfa/fxfa/parser/xfa_object.h"
21 #include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
22 #include "xfa/fxfa/parser/xfa_utils.h"
23
24 namespace {
25
MergeNodeRecurse(CXFA_Document * pDocument,CXFA_Node * pDestNodeParent,CXFA_Node * pProtoNode)26 void MergeNodeRecurse(CXFA_Document* pDocument,
27 CXFA_Node* pDestNodeParent,
28 CXFA_Node* pProtoNode) {
29 CXFA_Node* pExistingNode = nullptr;
30 for (CXFA_Node* pFormChild =
31 pDestNodeParent->GetNodeItem(XFA_NODEITEM_FirstChild);
32 pFormChild;
33 pFormChild = pFormChild->GetNodeItem(XFA_NODEITEM_NextSibling)) {
34 if (pFormChild->GetElementType() == pProtoNode->GetElementType() &&
35 pFormChild->GetNameHash() == pProtoNode->GetNameHash() &&
36 pFormChild->IsUnusedNode()) {
37 pFormChild->ClearFlag(XFA_NodeFlag_UnusedNode);
38 pExistingNode = pFormChild;
39 break;
40 }
41 }
42
43 if (pExistingNode) {
44 pExistingNode->SetTemplateNode(pProtoNode);
45 for (CXFA_Node* pTemplateChild =
46 pProtoNode->GetNodeItem(XFA_NODEITEM_FirstChild);
47 pTemplateChild; pTemplateChild = pTemplateChild->GetNodeItem(
48 XFA_NODEITEM_NextSibling)) {
49 MergeNodeRecurse(pDocument, pExistingNode, pTemplateChild);
50 }
51 return;
52 }
53 CXFA_Node* pNewNode = pProtoNode->Clone(true);
54 pNewNode->SetTemplateNode(pProtoNode);
55 pDestNodeParent->InsertChild(pNewNode, nullptr);
56 }
57
MergeNode(CXFA_Document * pDocument,CXFA_Node * pDestNode,CXFA_Node * pProtoNode)58 void MergeNode(CXFA_Document* pDocument,
59 CXFA_Node* pDestNode,
60 CXFA_Node* pProtoNode) {
61 {
62 CXFA_NodeIterator sIterator(pDestNode);
63 for (CXFA_Node* pNode = sIterator.GetCurrent(); pNode;
64 pNode = sIterator.MoveToNext()) {
65 pNode->SetFlag(XFA_NodeFlag_UnusedNode, true);
66 }
67 }
68 pDestNode->SetTemplateNode(pProtoNode);
69 for (CXFA_Node* pTemplateChild =
70 pProtoNode->GetNodeItem(XFA_NODEITEM_FirstChild);
71 pTemplateChild;
72 pTemplateChild = pTemplateChild->GetNodeItem(XFA_NODEITEM_NextSibling)) {
73 MergeNodeRecurse(pDocument, pDestNode, pTemplateChild);
74 }
75 {
76 CXFA_NodeIterator sIterator(pDestNode);
77 for (CXFA_Node* pNode = sIterator.GetCurrent(); pNode;
78 pNode = sIterator.MoveToNext()) {
79 pNode->ClearFlag(XFA_NodeFlag_UnusedNode);
80 }
81 }
82 }
83
84 } // namespace
85
CXFA_Document(CXFA_DocumentParser * pParser)86 CXFA_Document::CXFA_Document(CXFA_DocumentParser* pParser)
87 : m_pParser(pParser),
88 m_pScriptContext(nullptr),
89 m_pLayoutProcessor(nullptr),
90 m_pRootNode(nullptr),
91 m_pLocalMgr(nullptr),
92 m_pScriptDataWindow(nullptr),
93 m_pScriptEvent(nullptr),
94 m_pScriptHost(nullptr),
95 m_pScriptLog(nullptr),
96 m_pScriptLayout(nullptr),
97 m_pScriptSignature(nullptr),
98 m_eCurVersionMode(XFA_VERSION_DEFAULT),
99 m_dwDocFlags(0) {
100 ASSERT(m_pParser);
101 }
102
~CXFA_Document()103 CXFA_Document::~CXFA_Document() {
104 delete m_pRootNode;
105 PurgeNodes();
106 }
107
GetLayoutProcessor()108 CXFA_LayoutProcessor* CXFA_Document::GetLayoutProcessor() {
109 if (!m_pLayoutProcessor)
110 m_pLayoutProcessor = new CXFA_LayoutProcessor(this);
111 return m_pLayoutProcessor;
112 }
113
GetDocLayout()114 CXFA_LayoutProcessor* CXFA_Document::GetDocLayout() {
115 return GetLayoutProcessor();
116 }
117
ClearLayoutData()118 void CXFA_Document::ClearLayoutData() {
119 delete m_pLayoutProcessor;
120 m_pLayoutProcessor = nullptr;
121 delete m_pScriptContext;
122 m_pScriptContext = nullptr;
123 delete m_pLocalMgr;
124 m_pLocalMgr = nullptr;
125 delete m_pScriptDataWindow;
126 m_pScriptDataWindow = nullptr;
127 delete m_pScriptEvent;
128 m_pScriptEvent = nullptr;
129 delete m_pScriptHost;
130 m_pScriptHost = nullptr;
131 delete m_pScriptLog;
132 m_pScriptLog = nullptr;
133 delete m_pScriptLayout;
134 m_pScriptLayout = nullptr;
135 delete m_pScriptSignature;
136 m_pScriptSignature = nullptr;
137 }
138
SetRoot(CXFA_Node * pNewRoot)139 void CXFA_Document::SetRoot(CXFA_Node* pNewRoot) {
140 if (m_pRootNode)
141 AddPurgeNode(m_pRootNode);
142
143 m_pRootNode = pNewRoot;
144 RemovePurgeNode(pNewRoot);
145 }
146
GetXMLDoc() const147 CFDE_XMLDoc* CXFA_Document::GetXMLDoc() const {
148 return m_pParser->GetXMLDoc();
149 }
150
GetNotify() const151 CXFA_FFNotify* CXFA_Document::GetNotify() const {
152 return m_pParser->GetNotify();
153 }
154
GetXFAObject(XFA_HashCode dwNodeNameHash)155 CXFA_Object* CXFA_Document::GetXFAObject(XFA_HashCode dwNodeNameHash) {
156 switch (dwNodeNameHash) {
157 case XFA_HASHCODE_Data: {
158 CXFA_Node* pDatasetsNode = ToNode(GetXFAObject(XFA_HASHCODE_Datasets));
159 if (!pDatasetsNode)
160 return nullptr;
161
162 for (CXFA_Node* pDatasetsChild =
163 pDatasetsNode->GetFirstChildByClass(XFA_Element::DataGroup);
164 pDatasetsChild;
165 pDatasetsChild = pDatasetsChild->GetNextSameClassSibling(
166 XFA_Element::DataGroup)) {
167 if (pDatasetsChild->GetNameHash() != XFA_HASHCODE_Data)
168 continue;
169
170 CFX_WideString wsNamespaceURI;
171 if (!pDatasetsChild->TryNamespace(wsNamespaceURI))
172 continue;
173
174 CFX_WideString wsDatasetsURI;
175 if (!pDatasetsNode->TryNamespace(wsDatasetsURI))
176 continue;
177 if (wsNamespaceURI == wsDatasetsURI)
178 return pDatasetsChild;
179 }
180 return nullptr;
181 }
182 case XFA_HASHCODE_Record: {
183 CXFA_Node* pData = ToNode(GetXFAObject(XFA_HASHCODE_Data));
184 return pData ? pData->GetFirstChildByClass(XFA_Element::DataGroup)
185 : nullptr;
186 }
187 case XFA_HASHCODE_DataWindow: {
188 if (!m_pScriptDataWindow)
189 m_pScriptDataWindow = new CScript_DataWindow(this);
190 return m_pScriptDataWindow;
191 }
192 case XFA_HASHCODE_Event: {
193 if (!m_pScriptEvent)
194 m_pScriptEvent = new CScript_EventPseudoModel(this);
195 return m_pScriptEvent;
196 }
197 case XFA_HASHCODE_Host: {
198 if (!m_pScriptHost)
199 m_pScriptHost = new CScript_HostPseudoModel(this);
200 return m_pScriptHost;
201 }
202 case XFA_HASHCODE_Log: {
203 if (!m_pScriptLog)
204 m_pScriptLog = new CScript_LogPseudoModel(this);
205 return m_pScriptLog;
206 }
207 case XFA_HASHCODE_Signature: {
208 if (!m_pScriptSignature)
209 m_pScriptSignature = new CScript_SignaturePseudoModel(this);
210 return m_pScriptSignature;
211 }
212 case XFA_HASHCODE_Layout: {
213 if (!m_pScriptLayout)
214 m_pScriptLayout = new CScript_LayoutPseudoModel(this);
215 return m_pScriptLayout;
216 }
217 default:
218 return m_pRootNode->GetFirstChildByName(dwNodeNameHash);
219 }
220 }
221
CreateNode(uint32_t dwPacket,XFA_Element eElement)222 CXFA_Node* CXFA_Document::CreateNode(uint32_t dwPacket, XFA_Element eElement) {
223 return CreateNode(XFA_GetPacketByID(dwPacket), eElement);
224 }
225
CreateNode(const XFA_PACKETINFO * pPacket,XFA_Element eElement)226 CXFA_Node* CXFA_Document::CreateNode(const XFA_PACKETINFO* pPacket,
227 XFA_Element eElement) {
228 if (!pPacket)
229 return nullptr;
230
231 const XFA_ELEMENTINFO* pElement = XFA_GetElementByID(eElement);
232 if (pElement && (pElement->dwPackets & pPacket->eName)) {
233 CXFA_Node* pNode =
234 new CXFA_Node(this, pPacket->eName, pElement->eObjectType,
235 pElement->eName, pElement->pName);
236 AddPurgeNode(pNode);
237 return pNode;
238 }
239
240 return nullptr;
241 }
242
AddPurgeNode(CXFA_Node * pNode)243 void CXFA_Document::AddPurgeNode(CXFA_Node* pNode) {
244 m_PurgeNodes.insert(pNode);
245 }
246
RemovePurgeNode(CXFA_Node * pNode)247 bool CXFA_Document::RemovePurgeNode(CXFA_Node* pNode) {
248 return !!m_PurgeNodes.erase(pNode);
249 }
250
PurgeNodes()251 void CXFA_Document::PurgeNodes() {
252 for (CXFA_Node* pNode : m_PurgeNodes)
253 delete pNode;
254
255 m_PurgeNodes.clear();
256 }
257
SetFlag(uint32_t dwFlag,bool bOn)258 void CXFA_Document::SetFlag(uint32_t dwFlag, bool bOn) {
259 if (bOn)
260 m_dwDocFlags |= dwFlag;
261 else
262 m_dwDocFlags &= ~dwFlag;
263 }
264
IsInteractive()265 bool CXFA_Document::IsInteractive() {
266 if (m_dwDocFlags & XFA_DOCFLAG_HasInteractive)
267 return !!(m_dwDocFlags & XFA_DOCFLAG_Interactive);
268
269 CXFA_Node* pConfig = ToNode(GetXFAObject(XFA_HASHCODE_Config));
270 if (!pConfig)
271 return false;
272
273 CFX_WideString wsInteractive;
274 CXFA_Node* pPresent = pConfig->GetFirstChildByClass(XFA_Element::Present);
275 if (!pPresent)
276 return false;
277
278 CXFA_Node* pPDF = pPresent->GetFirstChildByClass(XFA_Element::Pdf);
279 if (!pPDF)
280 return false;
281
282 CXFA_Node* pFormFiller = pPDF->GetChild(0, XFA_Element::Interactive);
283 if (pFormFiller) {
284 m_dwDocFlags |= XFA_DOCFLAG_HasInteractive;
285 if (pFormFiller->TryContent(wsInteractive) && wsInteractive == L"1") {
286 m_dwDocFlags |= XFA_DOCFLAG_Interactive;
287 return true;
288 }
289 }
290 return false;
291 }
292
GetLocalMgr()293 CXFA_LocaleMgr* CXFA_Document::GetLocalMgr() {
294 if (!m_pLocalMgr) {
295 m_pLocalMgr =
296 new CXFA_LocaleMgr(ToNode(GetXFAObject(XFA_HASHCODE_LocaleSet)),
297 GetNotify()->GetAppProvider()->GetLanguage());
298 }
299 return m_pLocalMgr;
300 }
301
InitScriptContext(v8::Isolate * pIsolate)302 CXFA_ScriptContext* CXFA_Document::InitScriptContext(v8::Isolate* pIsolate) {
303 if (!m_pScriptContext)
304 m_pScriptContext = new CXFA_ScriptContext(this);
305 m_pScriptContext->Initialize(pIsolate);
306 return m_pScriptContext;
307 }
308
GetScriptContext()309 CXFA_ScriptContext* CXFA_Document::GetScriptContext() {
310 if (!m_pScriptContext)
311 m_pScriptContext = new CXFA_ScriptContext(this);
312 return m_pScriptContext;
313 }
314
RecognizeXFAVersionNumber(CFX_WideString & wsTemplateNS)315 XFA_VERSION CXFA_Document::RecognizeXFAVersionNumber(
316 CFX_WideString& wsTemplateNS) {
317 CFX_WideStringC wsTemplateURIPrefix =
318 XFA_GetPacketByIndex(XFA_PACKET_Template)->pURI;
319 FX_STRSIZE nPrefixLength = wsTemplateURIPrefix.GetLength();
320 if (CFX_WideStringC(wsTemplateNS.c_str(), wsTemplateNS.GetLength()) !=
321 wsTemplateURIPrefix) {
322 return XFA_VERSION_UNKNOWN;
323 }
324 FX_STRSIZE nDotPos = wsTemplateNS.Find('.', nPrefixLength);
325 if (nDotPos == (FX_STRSIZE)-1)
326 return XFA_VERSION_UNKNOWN;
327
328 int8_t iMajor = FXSYS_wtoi(
329 wsTemplateNS.Mid(nPrefixLength, nDotPos - nPrefixLength).c_str());
330 int8_t iMinor = FXSYS_wtoi(
331 wsTemplateNS.Mid(nDotPos + 1, wsTemplateNS.GetLength() - nDotPos - 2)
332 .c_str());
333 XFA_VERSION eVersion = (XFA_VERSION)((int32_t)iMajor * 100 + iMinor);
334 if (eVersion < XFA_VERSION_MIN || eVersion > XFA_VERSION_MAX)
335 return XFA_VERSION_UNKNOWN;
336
337 m_eCurVersionMode = eVersion;
338 return eVersion;
339 }
340
GetNodeByID(CXFA_Node * pRoot,const CFX_WideStringC & wsID)341 CXFA_Node* CXFA_Document::GetNodeByID(CXFA_Node* pRoot,
342 const CFX_WideStringC& wsID) {
343 if (!pRoot || wsID.IsEmpty())
344 return nullptr;
345
346 CXFA_NodeIterator sIterator(pRoot);
347 for (CXFA_Node* pNode = sIterator.GetCurrent(); pNode;
348 pNode = sIterator.MoveToNext()) {
349 CFX_WideStringC wsIDVal;
350 if (pNode->TryCData(XFA_ATTRIBUTE_Id, wsIDVal) && !wsIDVal.IsEmpty()) {
351 if (wsIDVal == wsID)
352 return pNode;
353 }
354 }
355 return nullptr;
356 }
357
DoProtoMerge()358 void CXFA_Document::DoProtoMerge() {
359 CXFA_Node* pTemplateRoot = ToNode(GetXFAObject(XFA_HASHCODE_Template));
360 if (!pTemplateRoot)
361 return;
362
363 std::map<uint32_t, CXFA_Node*> mIDMap;
364 CXFA_NodeSet sUseNodes;
365 CXFA_NodeIterator sIterator(pTemplateRoot);
366 for (CXFA_Node* pNode = sIterator.GetCurrent(); pNode;
367 pNode = sIterator.MoveToNext()) {
368 CFX_WideStringC wsIDVal;
369 if (pNode->TryCData(XFA_ATTRIBUTE_Id, wsIDVal) && !wsIDVal.IsEmpty()) {
370 mIDMap[FX_HashCode_GetW(wsIDVal, false)] = pNode;
371 }
372 CFX_WideStringC wsUseVal;
373 if (pNode->TryCData(XFA_ATTRIBUTE_Use, wsUseVal) && !wsUseVal.IsEmpty()) {
374 sUseNodes.insert(pNode);
375 } else if (pNode->TryCData(XFA_ATTRIBUTE_Usehref, wsUseVal) &&
376 !wsUseVal.IsEmpty()) {
377 sUseNodes.insert(pNode);
378 }
379 }
380
381 for (CXFA_Node* pUseHrefNode : sUseNodes) {
382 CFX_WideString wsUseVal;
383 CFX_WideStringC wsURI, wsID, wsSOM;
384 if (pUseHrefNode->TryCData(XFA_ATTRIBUTE_Usehref, wsUseVal) &&
385 !wsUseVal.IsEmpty()) {
386 FX_STRSIZE uSharpPos = wsUseVal.Find('#');
387 if (uSharpPos < 0) {
388 wsURI = wsUseVal.AsStringC();
389 } else {
390 wsURI = CFX_WideStringC(wsUseVal.c_str(), uSharpPos);
391 FX_STRSIZE uLen = wsUseVal.GetLength();
392 if (uLen >= uSharpPos + 5 &&
393 CFX_WideStringC(wsUseVal.c_str() + uSharpPos, 5) == L"#som(" &&
394 wsUseVal[uLen - 1] == ')') {
395 wsSOM = CFX_WideStringC(wsUseVal.c_str() + uSharpPos + 5,
396 uLen - 1 - uSharpPos - 5);
397 } else {
398 wsID = CFX_WideStringC(wsUseVal.c_str() + uSharpPos + 1,
399 uLen - uSharpPos - 1);
400 }
401 }
402 } else if (pUseHrefNode->TryCData(XFA_ATTRIBUTE_Use, wsUseVal) &&
403 !wsUseVal.IsEmpty()) {
404 if (wsUseVal[0] == '#')
405 wsID = CFX_WideStringC(wsUseVal.c_str() + 1, wsUseVal.GetLength() - 1);
406 else
407 wsSOM = CFX_WideStringC(wsUseVal.c_str(), wsUseVal.GetLength());
408 }
409
410 if (!wsURI.IsEmpty() && wsURI != L".")
411 continue;
412
413 CXFA_Node* pProtoNode = nullptr;
414 if (!wsSOM.IsEmpty()) {
415 uint32_t dwFlag = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Attributes |
416 XFA_RESOLVENODE_Properties | XFA_RESOLVENODE_Parent |
417 XFA_RESOLVENODE_Siblings;
418 XFA_RESOLVENODE_RS resoveNodeRS;
419 int32_t iRet = m_pScriptContext->ResolveObjects(pUseHrefNode, wsSOM,
420 resoveNodeRS, dwFlag);
421 if (iRet > 0 && resoveNodeRS.nodes[0]->IsNode())
422 pProtoNode = resoveNodeRS.nodes[0]->AsNode();
423 } else if (!wsID.IsEmpty()) {
424 auto it = mIDMap.find(FX_HashCode_GetW(wsID, false));
425 if (it == mIDMap.end())
426 continue;
427 pProtoNode = it->second;
428 }
429 if (!pProtoNode)
430 continue;
431
432 MergeNode(this, pUseHrefNode, pProtoNode);
433 }
434 }
435