Merge to XFA: Use stdint.h types throughout PDFium.
[pdfium.git] / xfa / src / fxfa / src / parser / xfa_document_imp.cpp
1 // Copyright 2014 PDFium Authors. All rights reserved.\r
2 // Use of this source code is governed by a BSD-style license that can be\r
3 // found in the LICENSE file.\r
4 \r
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com\r
6 \r
7 #include "../../../foxitlib.h"\r
8 #include "../common/xfa_utils.h"\r
9 #include "../common/xfa_object.h"\r
10 #include "../common/xfa_document.h"\r
11 #include "../common/xfa_parser.h"\r
12 #include "../common/xfa_script.h"\r
13 #include "../common/xfa_docdata.h"\r
14 #include "../common/xfa_doclayout.h"\r
15 #include "../common/xfa_debug.h"\r
16 #include "../common/xfa_localemgr.h"\r
17 #include "../common/xfa_fm2jsapi.h"\r
18 #include "xfa_debug_parser.h"\r
19 #include "xfa_basic_imp.h"\r
20 #include "xfa_document_layout_imp.h"\r
21 #include "xfa_script_datawindow.h"\r
22 #include "xfa_script_eventpseudomodel.h"\r
23 #include "xfa_script_hostpseudomodel.h"\r
24 #include "xfa_script_logpseudomodel.h"\r
25 #include "xfa_script_layoutpseudomodel.h"\r
26 #include "xfa_script_signaturepseudomodel.h"\r
27 CXFA_Document::CXFA_Document(IXFA_DocParser *pParser)\r
28     : m_pParser(pParser)\r
29     , m_pLayoutProcessor(NULL)\r
30     , m_pRootNode(NULL)\r
31     , m_pScriptContext(NULL)\r
32     , m_pLocalMgr(NULL)\r
33     , m_pScriptDataWindow(NULL)\r
34     , m_pScriptEvent(NULL)\r
35     , m_pScriptHost(NULL)\r
36     , m_pScriptLog(NULL)\r
37     , m_pScriptLayout(NULL)\r
38     , m_pScriptSignature(NULL)\r
39     , m_dwDocFlags(0)\r
40     , m_eCurVersionMode(XFA_VERSION_DEFAULT)\r
41 {\r
42     ASSERT(m_pParser);\r
43 }\r
44 CXFA_Document::~CXFA_Document()\r
45 {\r
46     if (m_pRootNode) {\r
47         delete m_pRootNode;\r
48     }\r
49     PurgeNodes();\r
50 }\r
51 void CXFA_Document::ClearLayoutData()\r
52 {\r
53     if(m_pLayoutProcessor) {\r
54         delete m_pLayoutProcessor;\r
55         m_pLayoutProcessor = NULL;\r
56     }\r
57     if (m_pScriptContext) {\r
58         m_pScriptContext->Release();\r
59         m_pScriptContext = NULL;\r
60     }\r
61     if (m_pLocalMgr) {\r
62         delete m_pLocalMgr;\r
63         m_pLocalMgr = NULL;\r
64     }\r
65     if(m_pScriptDataWindow) {\r
66         delete m_pScriptDataWindow;\r
67         m_pScriptDataWindow = NULL;\r
68     }\r
69     if(m_pScriptEvent) {\r
70         delete m_pScriptEvent;\r
71         m_pScriptEvent = NULL;\r
72     }\r
73     if (m_pScriptHost) {\r
74         delete m_pScriptHost;\r
75         m_pScriptHost = NULL;\r
76     }\r
77     if (m_pScriptLog) {\r
78         delete m_pScriptLog;\r
79         m_pScriptLog = NULL;\r
80     }\r
81     if(m_pScriptLayout) {\r
82         delete m_pScriptLayout;\r
83         m_pScriptLayout = NULL;\r
84     }\r
85     if(m_pScriptSignature) {\r
86         delete m_pScriptSignature;\r
87         m_pScriptSignature = NULL;\r
88     }\r
89 }\r
90 void CXFA_Document::SetRoot(CXFA_Node* pNewRoot)\r
91 {\r
92     if(m_pRootNode) {\r
93         AddPurgeNode(m_pRootNode);\r
94     }\r
95     m_pRootNode = pNewRoot;\r
96     RemovePurgeNode(pNewRoot);\r
97 }\r
98 IXFA_Notify* CXFA_Document::GetNotify() const\r
99 {\r
100     return m_pParser->GetNotify();\r
101 }\r
102 CXFA_Object* CXFA_Document::GetXFANode(FX_WSTR wsNodeName)\r
103 {\r
104     return GetXFANode(FX_HashCode_String_GetW(wsNodeName.GetPtr(), wsNodeName.GetLength()));\r
105 }\r
106 CXFA_Object* CXFA_Document::GetXFANode(FX_DWORD dwNodeNameHash)\r
107 {\r
108     switch(dwNodeNameHash) {\r
109         case XFA_HASHCODE_Data: {\r
110                 CXFA_Node* pDatasetsNode = (CXFA_Node*)GetXFANode(XFA_HASHCODE_Datasets);\r
111                 if(!pDatasetsNode) {\r
112                     return NULL;\r
113                 }\r
114                 for (CXFA_Node* pDatasetsChild = pDatasetsNode->GetFirstChildByClass(XFA_ELEMENT_DataGroup); pDatasetsChild; pDatasetsChild = pDatasetsChild->GetNextSameClassSibling(XFA_ELEMENT_DataGroup)) {\r
115                     if(pDatasetsChild->GetNameHash() != XFA_HASHCODE_Data) {\r
116                         continue;\r
117                     }\r
118                     CFX_WideString wsNamespaceURI;\r
119                     if(!pDatasetsChild->TryNamespace(wsNamespaceURI)) {\r
120                         continue;\r
121                     }\r
122                     CFX_WideString wsDatasetsURI;\r
123                     if(!pDatasetsNode->TryNamespace(wsDatasetsURI)) {\r
124                         continue;\r
125                     }\r
126                     if(wsNamespaceURI == wsDatasetsURI) {\r
127                         return pDatasetsChild;\r
128                     }\r
129                 }\r
130             }\r
131             return NULL;\r
132         case XFA_HASHCODE_Record: {\r
133                 CXFA_Node* pData = (CXFA_Node*)GetXFANode(XFA_HASHCODE_Data);\r
134                 return pData ? pData->GetFirstChildByClass(XFA_ELEMENT_DataGroup) : NULL;\r
135             }\r
136         case XFA_HASHCODE_DataWindow: {\r
137                 if(m_pScriptDataWindow == NULL) {\r
138                     m_pScriptDataWindow = FX_NEW CScript_DataWindow(this);\r
139                 }\r
140                 return m_pScriptDataWindow;\r
141             }\r
142         case XFA_HASHCODE_Event: {\r
143                 if(m_pScriptEvent == NULL) {\r
144                     m_pScriptEvent = FX_NEW CScript_EventPseudoModel(this);\r
145                 }\r
146                 return m_pScriptEvent;\r
147             }\r
148         case XFA_HASHCODE_Host: {\r
149                 if(m_pScriptHost == NULL) {\r
150                     m_pScriptHost = FX_NEW CScript_HostPseudoModel(this);\r
151                 }\r
152                 return m_pScriptHost;\r
153             }\r
154         case XFA_HASHCODE_Log: {\r
155                 if (m_pScriptLog == NULL) {\r
156                     m_pScriptLog = FX_NEW CScript_LogPseudoModel(this);\r
157                 }\r
158                 return m_pScriptLog;\r
159             }\r
160         case XFA_HASHCODE_Signature: {\r
161                 if(m_pScriptSignature == NULL) {\r
162                     m_pScriptSignature = FX_NEW CScript_SignaturePseudoModel(this);\r
163                 }\r
164                 return m_pScriptSignature;\r
165             }\r
166         case XFA_HASHCODE_Layout: {\r
167                 if(m_pScriptLayout == NULL) {\r
168                     m_pScriptLayout = FX_NEW CScript_LayoutPseudoModel(this);\r
169                 }\r
170                 return m_pScriptLayout;\r
171             }\r
172         default:\r
173             return m_pRootNode->GetFirstChildByName(dwNodeNameHash);\r
174     }\r
175 }\r
176 CXFA_Node* CXFA_Document::CreateNode(FX_DWORD dwPacket, XFA_ELEMENT eElement)\r
177 {\r
178     XFA_LPCPACKETINFO pPacket = XFA_GetPacketByID(dwPacket);\r
179     return CreateNode(pPacket, eElement);\r
180 }\r
181 CXFA_Node* CXFA_Document::CreateNode(XFA_LPCPACKETINFO pPacket, XFA_ELEMENT eElement)\r
182 {\r
183     if (pPacket == NULL) {\r
184         return NULL;\r
185     }\r
186     XFA_LPCELEMENTINFO pElement = XFA_GetElementByID(eElement);\r
187     if (pElement && (pElement->dwPackets & pPacket->eName)) {\r
188         CXFA_Node* pNode = FX_NEW CXFA_Node(this, pPacket->eName, pElement->eName);\r
189         if(pNode) {\r
190             AddPurgeNode(pNode);\r
191         }\r
192         return pNode;\r
193     }\r
194     return NULL;\r
195 }\r
196 void CXFA_Document::AddPurgeNode(CXFA_Node *pNode)\r
197 {\r
198     m_rgPurgeNodes.Add(pNode);\r
199 }\r
200 FX_BOOL CXFA_Document::RemovePurgeNode(CXFA_Node *pNode)\r
201 {\r
202     return m_rgPurgeNodes.RemoveKey(pNode);\r
203 }\r
204 void CXFA_Document::PurgeNodes()\r
205 {\r
206     FX_POSITION psNode = m_rgPurgeNodes.GetStartPosition();\r
207     while(psNode) {\r
208         CXFA_Node* pNode;\r
209         m_rgPurgeNodes.GetNextAssoc(psNode, pNode);\r
210         delete pNode;\r
211     }\r
212     m_rgPurgeNodes.RemoveAll();\r
213 }\r
214 void CXFA_Document::SetFlag(FX_DWORD dwFlag, FX_BOOL bOn )\r
215 {\r
216     if (bOn) {\r
217         m_dwDocFlags |= dwFlag;\r
218     } else {\r
219         m_dwDocFlags &= ~dwFlag;\r
220     }\r
221 }\r
222 FX_BOOL CXFA_Document::IsInteractive()\r
223 {\r
224     if (m_dwDocFlags & XFA_DOCFLAG_HasInteractive) {\r
225         return m_dwDocFlags & XFA_DOCFLAG_Interactive;\r
226     }\r
227     CXFA_Node* pConfig = (CXFA_Node*)this->GetXFANode(XFA_HASHCODE_Config);\r
228     if (!pConfig) {\r
229         return FALSE;\r
230     }\r
231     CFX_WideString wsInteractive;\r
232     CXFA_Node* pPresent = pConfig->GetFirstChildByClass(XFA_ELEMENT_Present);\r
233     if (!pPresent) {\r
234         return FALSE;\r
235     }\r
236     CXFA_Node* pPDF = pPresent->GetFirstChildByClass(XFA_ELEMENT_Pdf);\r
237     if (!pPDF) {\r
238         return FALSE;\r
239     }\r
240     CXFA_Node* pInteractive = pPDF->GetChild(0, XFA_ELEMENT_Interactive);\r
241     if (pInteractive) {\r
242         m_dwDocFlags |= XFA_DOCFLAG_HasInteractive;\r
243         if (pInteractive->TryContent(wsInteractive) && wsInteractive == FX_WSTRC(L"1")) {\r
244             m_dwDocFlags |= XFA_DOCFLAG_Interactive;\r
245             return TRUE;\r
246         }\r
247     }\r
248     return FALSE;\r
249 }\r
250 CXFA_LocaleMgr* CXFA_Document::GetLocalMgr()\r
251 {\r
252     if (!m_pLocalMgr) {\r
253         CFX_WideString wsLanguage;\r
254         this->GetParser()->GetNotify()->GetAppProvider()->GetLanguage(wsLanguage);\r
255         m_pLocalMgr = FX_NEW CXFA_LocaleMgr((CXFA_Node*)this->GetXFANode(XFA_HASHCODE_LocaleSet), wsLanguage);\r
256     }\r
257     return m_pLocalMgr;\r
258 }\r
259 IXFA_ScriptContext* CXFA_Document::InitScriptContext(FXJSE_HRUNTIME hRuntime)\r
260 {\r
261     if (!m_pScriptContext) {\r
262         m_pScriptContext = XFA_ScriptContext_Create(this);\r
263     }\r
264     m_pScriptContext->Initialize(hRuntime);\r
265     return m_pScriptContext;\r
266 }\r
267 IXFA_ScriptContext*     CXFA_Document::GetScriptContext()\r
268 {\r
269     if (!m_pScriptContext) {\r
270         m_pScriptContext = XFA_ScriptContext_Create(this);\r
271     }\r
272     return m_pScriptContext;\r
273 }\r
274 XFA_VERSION CXFA_Document::RecognizeXFAVersionNumber(CFX_WideString& wsTemplateNS)\r
275 {\r
276     CFX_WideStringC wsTemplateURIPrefix = XFA_GetPacketByIndex(XFA_PACKET_Template)->pURI;\r
277     FX_STRSIZE  nPrefixLength = wsTemplateURIPrefix.GetLength();\r
278     if(CFX_WideStringC(wsTemplateNS, wsTemplateNS.GetLength()) != wsTemplateURIPrefix) {\r
279         return XFA_VERSION_UNKNOWN;\r
280     }\r
281     FX_STRSIZE  nDotPos = wsTemplateNS.Find('.', nPrefixLength);\r
282     if(nDotPos == (FX_STRSIZE)-1) {\r
283         return XFA_VERSION_UNKNOWN;\r
284     }\r
285     int8_t iMajor = FXSYS_wtoi(wsTemplateNS.Mid(nPrefixLength, nDotPos - nPrefixLength));\r
286     int8_t iMinor = FXSYS_wtoi(wsTemplateNS.Mid(nDotPos + 1, wsTemplateNS.GetLength() - nDotPos - 2));\r
287     XFA_VERSION eVersion = (XFA_VERSION)((int32_t)iMajor * 100 + iMinor);\r
288     if(eVersion < XFA_VERSION_MIN || eVersion > XFA_VERSION_MAX) {\r
289         return XFA_VERSION_UNKNOWN;\r
290     }\r
291     m_eCurVersionMode = eVersion;\r
292     return eVersion;\r
293 }\r
294 CXFA_Node* CXFA_Document::GetNodeByID(CXFA_Node* pRoot, FX_WSTR wsID)\r
295 {\r
296     if(!pRoot || wsID.IsEmpty()) {\r
297         return NULL;\r
298     }\r
299     CXFA_NodeIterator sIterator(pRoot);\r
300     for(CXFA_Node* pNode = sIterator.GetCurrent(); pNode; pNode = sIterator.MoveToNext()) {\r
301         CFX_WideStringC wsIDVal;\r
302         if(pNode->TryCData(XFA_ATTRIBUTE_Id, wsIDVal) && !wsIDVal.IsEmpty()) {\r
303             if(wsIDVal == wsID) {\r
304                 return pNode;\r
305             }\r
306         }\r
307     }\r
308     return NULL;\r
309 }\r
310 static void XFA_ProtoMerge_MergeNodeRecurse(CXFA_Document *pDocument, CXFA_Node *pDestNodeParent, CXFA_Node *pProtoNode)\r
311 {\r
312     CXFA_Node* pExistingNode = NULL;\r
313     for(CXFA_Node* pFormChild = pDestNodeParent->GetNodeItem(XFA_NODEITEM_FirstChild); pFormChild; pFormChild = pFormChild->GetNodeItem(XFA_NODEITEM_NextSibling)) {\r
314         if(pFormChild->GetClassID() == pProtoNode->GetClassID() && pFormChild->GetNameHash() == pProtoNode->GetNameHash() && pFormChild->HasFlag(XFA_NODEFLAG_UnusedNode)) {\r
315             pFormChild->SetFlag(XFA_NODEFLAG_UnusedNode, FALSE);\r
316             pExistingNode = pFormChild;\r
317             break;\r
318         }\r
319     }\r
320     if (pExistingNode) {\r
321         pExistingNode->SetTemplateNode(pProtoNode);\r
322         for (CXFA_Node *pTemplateChild = pProtoNode->GetNodeItem(XFA_NODEITEM_FirstChild); pTemplateChild; pTemplateChild = pTemplateChild->GetNodeItem(XFA_NODEITEM_NextSibling)) {\r
323             XFA_ProtoMerge_MergeNodeRecurse(pDocument, pExistingNode, pTemplateChild);\r
324         }\r
325         return;\r
326     }\r
327     CXFA_Node* pNewNode = pProtoNode->Clone(TRUE);\r
328     pNewNode->SetTemplateNode(pProtoNode);\r
329     pDestNodeParent->InsertChild(pNewNode, NULL);\r
330 }\r
331 static void XFA_ProtoMerge_MergeNode(CXFA_Document *pDocument, CXFA_Node *pDestNode, CXFA_Node *pProtoNode)\r
332 {\r
333     {\r
334         CXFA_NodeIterator sIterator(pDestNode);\r
335         for(CXFA_Node* pNode = sIterator.GetCurrent(); pNode; pNode = sIterator.MoveToNext()) {\r
336             pNode->SetFlag(XFA_NODEFLAG_UnusedNode);\r
337         }\r
338     }\r
339     pDestNode->SetTemplateNode(pProtoNode);\r
340     for (CXFA_Node *pTemplateChild = pProtoNode->GetNodeItem(XFA_NODEITEM_FirstChild); pTemplateChild; pTemplateChild = pTemplateChild->GetNodeItem(XFA_NODEITEM_NextSibling)) {\r
341         XFA_ProtoMerge_MergeNodeRecurse(pDocument, pDestNode, pTemplateChild);\r
342     }\r
343     {\r
344         CXFA_NodeIterator sIterator(pDestNode);\r
345         for(CXFA_Node* pNode = sIterator.GetCurrent(); pNode; pNode = sIterator.MoveToNext()) {\r
346             pNode->SetFlag(XFA_NODEFLAG_UnusedNode, FALSE);\r
347         }\r
348     }\r
349 }\r
350 void CXFA_Document::DoProtoMerge()\r
351 {\r
352     CXFA_Node* pTemplateRoot = (CXFA_Node*)GetXFANode(XFA_HASHCODE_Template);\r
353     if(!pTemplateRoot) {\r
354         return;\r
355     }\r
356     CFX_MapPtrTemplate<FX_DWORD, CXFA_Node*> mIDMap;\r
357     CXFA_NodeSet sUseNodes;\r
358     CXFA_NodeIterator sIterator(pTemplateRoot);\r
359     for(CXFA_Node* pNode = sIterator.GetCurrent(); pNode; pNode = sIterator.MoveToNext()) {\r
360         CFX_WideStringC wsIDVal;\r
361         if(pNode->TryCData(XFA_ATTRIBUTE_Id, wsIDVal) && !wsIDVal.IsEmpty()) {\r
362             mIDMap[FX_HashCode_String_GetW(wsIDVal.GetPtr(), wsIDVal.GetLength())] = pNode;\r
363         }\r
364         CFX_WideStringC wsUseVal;\r
365         if(pNode->TryCData(XFA_ATTRIBUTE_Use, wsUseVal) && !wsUseVal.IsEmpty()) {\r
366             sUseNodes.Add(pNode);\r
367         } else if (pNode->TryCData(XFA_ATTRIBUTE_Usehref, wsUseVal) && !wsUseVal.IsEmpty()) {\r
368             sUseNodes.Add(pNode);\r
369         }\r
370     }\r
371     FX_POSITION pos = sUseNodes.GetStartPosition();\r
372     while (pos) {\r
373         CXFA_Node* pUseHrefNode = NULL;\r
374         sUseNodes.GetNextAssoc(pos, pUseHrefNode);\r
375         CFX_WideString wsUseVal;\r
376         CFX_WideStringC wsURI, wsID, wsSOM;\r
377         if (pUseHrefNode->TryCData(XFA_ATTRIBUTE_Usehref, wsUseVal) && !wsUseVal.IsEmpty()) {\r
378             FX_STRSIZE uSharpPos = wsUseVal.Find('#');\r
379             if(uSharpPos < 0) {\r
380                 wsURI = wsUseVal;\r
381             } else {\r
382                 wsURI = CFX_WideStringC((FX_LPCWSTR)wsUseVal, uSharpPos);\r
383                 FX_STRSIZE uLen = wsUseVal.GetLength();\r
384                 if(uLen >= uSharpPos + 5 && CFX_WideStringC((FX_LPCWSTR)wsUseVal + uSharpPos, 5) == FX_WSTRC(L"#som(") && wsUseVal[uLen - 1] == ')') {\r
385                     wsSOM = CFX_WideStringC((FX_LPCWSTR)wsUseVal + uSharpPos + 5, uLen - 1 - uSharpPos - 5);\r
386                 } else {\r
387                     wsID = CFX_WideStringC((FX_LPCWSTR)wsUseVal + uSharpPos + 1, uLen - uSharpPos - 1);\r
388                 }\r
389             }\r
390         } else if(pUseHrefNode->TryCData(XFA_ATTRIBUTE_Use, wsUseVal) && !wsUseVal.IsEmpty()) {\r
391             if(wsUseVal[0] == '#') {\r
392                 wsID = CFX_WideStringC((FX_LPCWSTR)wsUseVal + 1, wsUseVal.GetLength() - 1);\r
393             } else {\r
394                 wsSOM = CFX_WideStringC((FX_LPCWSTR)wsUseVal, wsUseVal.GetLength());\r
395             }\r
396         }\r
397         if (!wsURI.IsEmpty() && wsURI != FX_WSTRC(L".")) {\r
398             continue;\r
399         }\r
400         CXFA_Node* pProtoNode = NULL;\r
401         if(!wsSOM.IsEmpty()) {\r
402             FX_DWORD dwFlag = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Attributes | XFA_RESOLVENODE_Properties | XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings;\r
403             XFA_RESOLVENODE_RS resoveNodeRS;\r
404             int32_t iRet = m_pScriptContext->ResolveObjects(pUseHrefNode, wsSOM, resoveNodeRS, dwFlag);\r
405             if(iRet > 0 && resoveNodeRS.nodes[0]->IsNode()) {\r
406                 pProtoNode = (CXFA_Node*)resoveNodeRS.nodes[0];\r
407             }\r
408         } else if(!wsID.IsEmpty()) {\r
409             if(!mIDMap.Lookup(FX_HashCode_String_GetW(wsID.GetPtr(), wsID.GetLength()), pProtoNode)) {\r
410                 continue;\r
411             }\r
412         }\r
413         if(!pProtoNode) {\r
414             continue;\r
415         }\r
416         XFA_ProtoMerge_MergeNode(this, pUseHrefNode, pProtoNode);\r
417     }\r
418 }\r