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/xfa_utils.h"
8 
9 #include <algorithm>
10 #include <vector>
11 
12 #include "core/fxcrt/cfx_memorystream.h"
13 #include "core/fxcrt/cfx_widetextbuf.h"
14 #include "core/fxcrt/fx_codepage.h"
15 #include "core/fxcrt/fx_extension.h"
16 #include "core/fxcrt/xml/cfx_xmlchardata.h"
17 #include "core/fxcrt/xml/cfx_xmlelement.h"
18 #include "core/fxcrt/xml/cfx_xmlnode.h"
19 #include "core/fxcrt/xml/cfx_xmltext.h"
20 #include "fxjs/xfa/cjx_object.h"
21 #include "third_party/base/stl_util.h"
22 #include "xfa/fxfa/parser/cxfa_document.h"
23 #include "xfa/fxfa/parser/cxfa_localemgr.h"
24 #include "xfa/fxfa/parser/cxfa_localevalue.h"
25 #include "xfa/fxfa/parser/cxfa_measurement.h"
26 #include "xfa/fxfa/parser/cxfa_node.h"
27 #include "xfa/fxfa/parser/cxfa_ui.h"
28 #include "xfa/fxfa/parser/cxfa_value.h"
29 #include "xfa/fxfa/parser/xfa_basic_data.h"
30 
31 namespace {
32 
33 const char kFormNS[] = "http://www.xfa.org/schema/xfa-form/";
34 
ExportEncodeAttribute(const WideString & str)35 WideString ExportEncodeAttribute(const WideString& str) {
36   CFX_WideTextBuf textBuf;
37   int32_t iLen = str.GetLength();
38   for (int32_t i = 0; i < iLen; i++) {
39     switch (str[i]) {
40       case '&':
41         textBuf << "&amp;";
42         break;
43       case '<':
44         textBuf << "&lt;";
45         break;
46       case '>':
47         textBuf << "&gt;";
48         break;
49       case '\'':
50         textBuf << "&apos;";
51         break;
52       case '\"':
53         textBuf << "&quot;";
54         break;
55       default:
56         textBuf.AppendChar(str[i]);
57     }
58   }
59   return textBuf.MakeString();
60 }
61 
IsXMLValidChar(wchar_t ch)62 bool IsXMLValidChar(wchar_t ch) {
63   return ch == 0x09 || ch == 0x0A || ch == 0x0D ||
64          (ch >= 0x20 && ch <= 0xD7FF) || (ch >= 0xE000 && ch <= 0xFFFD);
65 }
66 
ExportEncodeContent(const WideString & str)67 WideString ExportEncodeContent(const WideString& str) {
68   CFX_WideTextBuf textBuf;
69   int32_t iLen = str.GetLength();
70   for (int32_t i = 0; i < iLen; i++) {
71     wchar_t ch = str[i];
72     if (!IsXMLValidChar(ch))
73       continue;
74 
75     if (ch == '&') {
76       textBuf << "&amp;";
77     } else if (ch == '<') {
78       textBuf << "&lt;";
79     } else if (ch == '>') {
80       textBuf << "&gt;";
81     } else if (ch == '\'') {
82       textBuf << "&apos;";
83     } else if (ch == '\"') {
84       textBuf << "&quot;";
85     } else if (ch == ' ') {
86       if (i && str[i - 1] != ' ') {
87         textBuf.AppendChar(' ');
88       } else {
89         textBuf << "&#x20;";
90       }
91     } else {
92       textBuf.AppendChar(str[i]);
93     }
94   }
95   return textBuf.MakeString();
96 }
97 
AttributeSaveInDataModel(CXFA_Node * pNode,XFA_Attribute eAttribute)98 bool AttributeSaveInDataModel(CXFA_Node* pNode, XFA_Attribute eAttribute) {
99   bool bSaveInDataModel = false;
100   if (pNode->GetElementType() != XFA_Element::Image)
101     return bSaveInDataModel;
102 
103   CXFA_Node* pValueNode = pNode->GetParent();
104   if (!pValueNode || pValueNode->GetElementType() != XFA_Element::Value)
105     return bSaveInDataModel;
106 
107   CXFA_Node* pFieldNode = pValueNode->GetParent();
108   if (pFieldNode && pFieldNode->GetBindData() &&
109       eAttribute == XFA_Attribute::Href) {
110     bSaveInDataModel = true;
111   }
112   return bSaveInDataModel;
113 }
114 
ContentNodeNeedtoExport(CXFA_Node * pContentNode)115 bool ContentNodeNeedtoExport(CXFA_Node* pContentNode) {
116   Optional<WideString> wsContent =
117       pContentNode->JSObject()->TryContent(false, false);
118   if (!wsContent)
119     return false;
120 
121   ASSERT(pContentNode->IsContentNode());
122   CXFA_Node* pParentNode = pContentNode->GetParent();
123   if (!pParentNode || pParentNode->GetElementType() != XFA_Element::Value)
124     return true;
125 
126   CXFA_Node* pGrandParentNode = pParentNode->GetParent();
127   if (!pGrandParentNode || !pGrandParentNode->IsContainerNode())
128     return true;
129   if (!pGrandParentNode->GetBindData())
130     return false;
131   if (pGrandParentNode->GetFFWidgetType() == XFA_FFWidgetType::kPasswordEdit)
132     return false;
133   return true;
134 }
135 
SaveAttribute(CXFA_Node * pNode,XFA_Attribute eName,const WideString & wsName,bool bProto,WideString & wsOutput)136 void SaveAttribute(CXFA_Node* pNode,
137                    XFA_Attribute eName,
138                    const WideString& wsName,
139                    bool bProto,
140                    WideString& wsOutput) {
141   if (!bProto && !pNode->JSObject()->HasAttribute(eName))
142     return;
143 
144   Optional<WideString> value = pNode->JSObject()->TryAttribute(eName, false);
145   if (!value)
146     return;
147 
148   wsOutput += L" ";
149   wsOutput += wsName;
150   wsOutput += L"=\"";
151   wsOutput += ExportEncodeAttribute(*value);
152   wsOutput += L"\"";
153 }
154 
RegenerateFormFile_Changed(CXFA_Node * pNode,CFX_WideTextBuf & buf,bool bSaveXML)155 void RegenerateFormFile_Changed(CXFA_Node* pNode,
156                                 CFX_WideTextBuf& buf,
157                                 bool bSaveXML) {
158   WideString wsAttrs;
159   for (size_t i = 0;; ++i) {
160     XFA_Attribute attr = pNode->GetAttribute(i);
161     if (attr == XFA_Attribute::Unknown)
162       break;
163 
164     if (attr == XFA_Attribute::Name ||
165         (AttributeSaveInDataModel(pNode, attr) && !bSaveXML)) {
166       continue;
167     }
168     WideString wsAttr;
169     SaveAttribute(pNode, attr, WideString::FromASCII(XFA_AttributeToName(attr)),
170                   bSaveXML, wsAttr);
171     wsAttrs += wsAttr;
172   }
173 
174   WideString wsChildren;
175   switch (pNode->GetObjectType()) {
176     case XFA_ObjectType::ContentNode: {
177       if (!bSaveXML && !ContentNodeNeedtoExport(pNode))
178         break;
179 
180       CXFA_Node* pRawValueNode = pNode->GetFirstChild();
181       while (pRawValueNode &&
182              pRawValueNode->GetElementType() != XFA_Element::SharpxHTML &&
183              pRawValueNode->GetElementType() != XFA_Element::Sharptext &&
184              pRawValueNode->GetElementType() != XFA_Element::Sharpxml) {
185         pRawValueNode = pRawValueNode->GetNextSibling();
186       }
187       if (!pRawValueNode)
188         break;
189 
190       Optional<WideString> contentType =
191           pNode->JSObject()->TryAttribute(XFA_Attribute::ContentType, false);
192       if (pRawValueNode->GetElementType() == XFA_Element::SharpxHTML &&
193           contentType.has_value() &&
194           contentType.value().EqualsASCII("text/html")) {
195         CFX_XMLNode* pExDataXML = pNode->GetXMLMappingNode();
196         if (!pExDataXML)
197           break;
198 
199         CFX_XMLNode* pRichTextXML = pExDataXML->GetFirstChild();
200         if (!pRichTextXML)
201           break;
202 
203         auto pMemStream = pdfium::MakeRetain<CFX_MemoryStream>();
204         pRichTextXML->Save(pMemStream);
205         wsChildren += WideString::FromUTF8(
206             ByteStringView(pMemStream->GetBuffer(), pMemStream->GetSize()));
207       } else if (pRawValueNode->GetElementType() == XFA_Element::Sharpxml &&
208                  contentType.has_value() &&
209                  contentType.value().EqualsASCII("text/xml")) {
210         Optional<WideString> rawValue = pRawValueNode->JSObject()->TryAttribute(
211             XFA_Attribute::Value, false);
212         if (!rawValue || rawValue->IsEmpty())
213           break;
214 
215         std::vector<WideString> wsSelTextArray =
216             fxcrt::Split(rawValue.value(), L'\n');
217 
218         CXFA_Node* pParentNode = pNode->GetParent();
219         CXFA_Node* pGrandparentNode = pParentNode->GetParent();
220         WideString bodyTagName =
221             pGrandparentNode->JSObject()->GetCData(XFA_Attribute::Name);
222         if (bodyTagName.IsEmpty())
223           bodyTagName = L"ListBox1";
224 
225         buf << "<";
226         buf << bodyTagName;
227         buf << " xmlns=\"\"\n>";
228         for (int32_t i = 0; i < pdfium::CollectionSize<int32_t>(wsSelTextArray);
229              i++) {
230           buf << "<value\n>";
231           buf << ExportEncodeContent(wsSelTextArray[i]);
232           buf << "</value\n>";
233         }
234         buf << "</";
235         buf << bodyTagName;
236         buf << "\n>";
237         wsChildren += buf.AsStringView();
238         buf.Clear();
239       } else {
240         WideString wsValue =
241             pRawValueNode->JSObject()->GetCData(XFA_Attribute::Value);
242         wsChildren += ExportEncodeContent(wsValue);
243       }
244       break;
245     }
246     case XFA_ObjectType::TextNode:
247     case XFA_ObjectType::NodeC:
248     case XFA_ObjectType::NodeV: {
249       WideString wsValue = pNode->JSObject()->GetCData(XFA_Attribute::Value);
250       wsChildren += ExportEncodeContent(wsValue);
251       break;
252     }
253     default:
254       if (pNode->GetElementType() == XFA_Element::Items) {
255         CXFA_Node* pTemplateNode = pNode->GetTemplateNodeIfExists();
256         if (!pTemplateNode ||
257             pTemplateNode->CountChildren(XFA_Element::Unknown, false) !=
258                 pNode->CountChildren(XFA_Element::Unknown, false)) {
259           bSaveXML = true;
260         }
261       }
262       CFX_WideTextBuf newBuf;
263       CXFA_Node* pChildNode = pNode->GetFirstChild();
264       while (pChildNode) {
265         RegenerateFormFile_Changed(pChildNode, newBuf, bSaveXML);
266         wsChildren += newBuf.AsStringView();
267         newBuf.Clear();
268         pChildNode = pChildNode->GetNextSibling();
269       }
270       if (!bSaveXML && !wsChildren.IsEmpty() &&
271           pNode->GetElementType() == XFA_Element::Items) {
272         wsChildren.clear();
273         bSaveXML = true;
274         CXFA_Node* pChild = pNode->GetFirstChild();
275         while (pChild) {
276           RegenerateFormFile_Changed(pChild, newBuf, bSaveXML);
277           wsChildren += newBuf.AsStringView();
278           newBuf.Clear();
279           pChild = pChild->GetNextSibling();
280         }
281       }
282       break;
283   }
284 
285   if (!wsChildren.IsEmpty() || !wsAttrs.IsEmpty() ||
286       pNode->JSObject()->HasAttribute(XFA_Attribute::Name)) {
287     WideString wsElement = WideString::FromASCII(pNode->GetClassName());
288     WideString wsName;
289     SaveAttribute(pNode, XFA_Attribute::Name, L"name", true, wsName);
290     buf << "<";
291     buf << wsElement;
292     buf << wsName;
293     buf << wsAttrs;
294     if (wsChildren.IsEmpty()) {
295       buf << "\n/>";
296     } else {
297       buf << "\n>";
298       buf << wsChildren;
299       buf << "</";
300       buf << wsElement;
301       buf << "\n>";
302     }
303   }
304 }
305 
RegenerateFormFile_Container(CXFA_Node * pNode,const RetainPtr<IFX_SeekableStream> & pStream,bool bSaveXML)306 void RegenerateFormFile_Container(CXFA_Node* pNode,
307                                   const RetainPtr<IFX_SeekableStream>& pStream,
308                                   bool bSaveXML) {
309   XFA_Element eType = pNode->GetElementType();
310   if (eType == XFA_Element::Field || eType == XFA_Element::Draw ||
311       !pNode->IsContainerNode()) {
312     CFX_WideTextBuf buf;
313     RegenerateFormFile_Changed(pNode, buf, bSaveXML);
314     size_t nLen = buf.GetLength();
315     if (nLen > 0)
316       pStream->WriteString(buf.MakeString().ToUTF8().AsStringView());
317     return;
318   }
319 
320   WideString wsElement = WideString::FromASCII(pNode->GetClassName());
321   pStream->WriteString("<");
322   pStream->WriteString(wsElement.ToUTF8().AsStringView());
323 
324   WideString wsOutput;
325   SaveAttribute(pNode, XFA_Attribute::Name, L"name", true, wsOutput);
326 
327   WideString wsAttrs;
328   for (size_t i = 0;; ++i) {
329     XFA_Attribute attr = pNode->GetAttribute(i);
330     if (attr == XFA_Attribute::Unknown)
331       break;
332     if (attr == XFA_Attribute::Name)
333       continue;
334 
335     WideString wsAttr;
336     SaveAttribute(pNode, attr, WideString::FromASCII(XFA_AttributeToName(attr)),
337                   false, wsAttr);
338     wsOutput += wsAttr;
339   }
340 
341   if (!wsOutput.IsEmpty())
342     pStream->WriteString(wsOutput.ToUTF8().AsStringView());
343 
344   CXFA_Node* pChildNode = pNode->GetFirstChild();
345   if (!pChildNode) {
346     pStream->WriteString(" />\n");
347     return;
348   }
349 
350   pStream->WriteString(">\n");
351   while (pChildNode) {
352     RegenerateFormFile_Container(pChildNode, pStream, bSaveXML);
353     pChildNode = pChildNode->GetNextSibling();
354   }
355   pStream->WriteString("</");
356   pStream->WriteString(wsElement.ToUTF8().AsStringView());
357   pStream->WriteString(">\n");
358 }
359 
RecognizeXFAVersionNumber(CXFA_Node * pTemplateRoot)360 WideString RecognizeXFAVersionNumber(CXFA_Node* pTemplateRoot) {
361   if (!pTemplateRoot)
362     return WideString();
363 
364   Optional<WideString> templateNS = pTemplateRoot->JSObject()->TryNamespace();
365   if (!templateNS)
366     return WideString();
367 
368   XFA_VERSION eVersion =
369       pTemplateRoot->GetDocument()->RecognizeXFAVersionNumber(*templateNS);
370   if (eVersion == XFA_VERSION_UNKNOWN)
371     eVersion = XFA_VERSION_DEFAULT;
372 
373   return WideString::Format(L"%i.%i", eVersion / 100, eVersion % 100);
374 }
375 
376 }  // namespace
377 
XFA_GetLocaleValue(CXFA_Node * pNode)378 CXFA_LocaleValue XFA_GetLocaleValue(CXFA_Node* pNode) {
379   CXFA_Value* pNodeValue =
380       pNode->GetChild<CXFA_Value>(0, XFA_Element::Value, false);
381   if (!pNodeValue)
382     return CXFA_LocaleValue();
383 
384   CXFA_Node* pValueChild = pNodeValue->GetFirstChild();
385   if (!pValueChild)
386     return CXFA_LocaleValue();
387 
388   int32_t iVTType = XFA_VT_NULL;
389   switch (pValueChild->GetElementType()) {
390     case XFA_Element::Decimal:
391       iVTType = XFA_VT_DECIMAL;
392       break;
393     case XFA_Element::Float:
394       iVTType = XFA_VT_FLOAT;
395       break;
396     case XFA_Element::Date:
397       iVTType = XFA_VT_DATE;
398       break;
399     case XFA_Element::Time:
400       iVTType = XFA_VT_TIME;
401       break;
402     case XFA_Element::DateTime:
403       iVTType = XFA_VT_DATETIME;
404       break;
405     case XFA_Element::Boolean:
406       iVTType = XFA_VT_BOOLEAN;
407       break;
408     case XFA_Element::Integer:
409       iVTType = XFA_VT_INTEGER;
410       break;
411     case XFA_Element::Text:
412       iVTType = XFA_VT_TEXT;
413       break;
414     default:
415       iVTType = XFA_VT_NULL;
416       break;
417   }
418   return CXFA_LocaleValue(iVTType, pNode->GetRawValue(),
419                           pNode->GetDocument()->GetLocaleMgr());
420 }
421 
XFA_FDEExtension_ResolveNamespaceQualifier(CFX_XMLElement * pNode,const WideString & wsQualifier,WideString * wsNamespaceURI)422 bool XFA_FDEExtension_ResolveNamespaceQualifier(CFX_XMLElement* pNode,
423                                                 const WideString& wsQualifier,
424                                                 WideString* wsNamespaceURI) {
425   if (!pNode)
426     return false;
427 
428   CFX_XMLNode* pFakeRoot = pNode->GetRoot();
429   WideString wsNSAttribute;
430   bool bRet = false;
431   if (wsQualifier.IsEmpty()) {
432     wsNSAttribute = L"xmlns";
433     bRet = true;
434   } else {
435     wsNSAttribute = L"xmlns:" + wsQualifier;
436   }
437   for (CFX_XMLNode* pParent = pNode; pParent != pFakeRoot;
438        pParent = pParent->GetParent()) {
439     CFX_XMLElement* pElement = ToXMLElement(pParent);
440     if (pElement && pElement->HasAttribute(wsNSAttribute)) {
441       *wsNamespaceURI = pElement->GetAttribute(wsNSAttribute);
442       return true;
443     }
444   }
445   wsNamespaceURI->clear();
446   return bRet;
447 }
448 
XFA_DataExporter_DealWithDataGroupNode(CXFA_Node * pDataNode)449 void XFA_DataExporter_DealWithDataGroupNode(CXFA_Node* pDataNode) {
450   if (!pDataNode || pDataNode->GetElementType() == XFA_Element::DataValue)
451     return;
452 
453   int32_t iChildNum = 0;
454   for (CXFA_Node* pChildNode = pDataNode->GetFirstChild(); pChildNode;
455        pChildNode = pChildNode->GetNextSibling()) {
456     iChildNum++;
457     XFA_DataExporter_DealWithDataGroupNode(pChildNode);
458   }
459 
460   if (pDataNode->GetElementType() != XFA_Element::DataGroup)
461     return;
462 
463   CFX_XMLElement* pElement = ToXMLElement(pDataNode->GetXMLMappingNode());
464   if (iChildNum > 0) {
465     if (pElement->HasAttribute(L"xfa:dataNode"))
466       pElement->RemoveAttribute(L"xfa:dataNode");
467     return;
468   }
469   pElement->SetAttribute(L"xfa:dataNode", L"dataGroup");
470 }
471 
XFA_DataExporter_RegenerateFormFile(CXFA_Node * pNode,const RetainPtr<IFX_SeekableStream> & pStream,bool bSaveXML)472 void XFA_DataExporter_RegenerateFormFile(
473     CXFA_Node* pNode,
474     const RetainPtr<IFX_SeekableStream>& pStream,
475     bool bSaveXML) {
476   if (pNode->IsModelNode()) {
477     pStream->WriteString("<form xmlns=\"");
478     pStream->WriteString(kFormNS);
479 
480     WideString wsVersionNumber = RecognizeXFAVersionNumber(
481         ToNode(pNode->GetDocument()->GetXFAObject(XFA_HASHCODE_Template)));
482     if (wsVersionNumber.IsEmpty())
483       wsVersionNumber = L"2.8";
484 
485     wsVersionNumber += L"/\"\n>";
486     pStream->WriteString(wsVersionNumber.ToUTF8().AsStringView());
487 
488     CXFA_Node* pChildNode = pNode->GetFirstChild();
489     while (pChildNode) {
490       RegenerateFormFile_Container(pChildNode, pStream, false);
491       pChildNode = pChildNode->GetNextSibling();
492     }
493     pStream->WriteString("</form\n>");
494   } else {
495     RegenerateFormFile_Container(pNode, pStream, bSaveXML);
496   }
497 }
498 
XFA_FieldIsMultiListBox(CXFA_Node * pFieldNode)499 bool XFA_FieldIsMultiListBox(CXFA_Node* pFieldNode) {
500   if (!pFieldNode)
501     return false;
502 
503   CXFA_Ui* pUIChild = pFieldNode->GetChild<CXFA_Ui>(0, XFA_Element::Ui, false);
504   if (!pUIChild)
505     return false;
506 
507   CXFA_Node* pFirstChild = pUIChild->GetFirstChild();
508   if (!pFirstChild ||
509       pFirstChild->GetElementType() != XFA_Element::ChoiceList) {
510     return false;
511   }
512 
513   return pFirstChild->JSObject()->GetEnum(XFA_Attribute::Open) ==
514          XFA_AttributeValue::MultiSelect;
515 }
516 
XFA_MapRotation(int32_t nRotation)517 int32_t XFA_MapRotation(int32_t nRotation) {
518   nRotation = nRotation % 360;
519   nRotation = nRotation < 0 ? nRotation + 360 : nRotation;
520   return nRotation;
521 }
522 
XFA_EventErrorAccumulate(XFA_EventError * pAcc,XFA_EventError eNew)523 void XFA_EventErrorAccumulate(XFA_EventError* pAcc, XFA_EventError eNew) {
524   if (*pAcc == XFA_EventError::kNotExist || eNew == XFA_EventError::kError)
525     *pAcc = eNew;
526 }
527