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"&";
41 break;
42 case '<':
43 textBuf += L"<";
44 break;
45 case '>':
46 textBuf += L">";
47 break;
48 case '\'':
49 textBuf += L"'";
50 break;
51 case '\"':
52 textBuf += L""";
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 << "&";
76 } else if (ch == '<') {
77 textBuf << "<";
78 } else if (ch == '>') {
79 textBuf << ">";
80 } else if (ch == '\'') {
81 textBuf << "'";
82 } else if (ch == '\"') {
83 textBuf << """;
84 } else if (ch == ' ') {
85 if (i && str[i - 1] != ' ') {
86 textBuf.AppendChar(' ');
87 } else {
88 textBuf << " ";
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