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