• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "xfa/fxfa/parser/cxfa_document.h"
22 #include "xfa/fxfa/parser/cxfa_localemgr.h"
23 #include "xfa/fxfa/parser/cxfa_localevalue.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 constexpr const wchar_t kFormNS[] = L"http://www.xfa.org/schema/xfa-form/";
33 
34 const double fraction_scales[] = {0.1,
35                                   0.01,
36                                   0.001,
37                                   0.0001,
38                                   0.00001,
39                                   0.000001,
40                                   0.0000001,
41                                   0.00000001,
42                                   0.000000001,
43                                   0.0000000001,
44                                   0.00000000001,
45                                   0.000000000001,
46                                   0.0000000000001,
47                                   0.00000000000001,
48                                   0.000000000000001,
49                                   0.0000000000000001};
50 
ExportEncodeAttribute(const WideString & str)51 WideString ExportEncodeAttribute(const WideString& str) {
52   CFX_WideTextBuf textBuf;
53   int32_t iLen = str.GetLength();
54   for (int32_t i = 0; i < iLen; i++) {
55     switch (str[i]) {
56       case '&':
57         textBuf << L"&amp;";
58         break;
59       case '<':
60         textBuf << L"&lt;";
61         break;
62       case '>':
63         textBuf << L"&gt;";
64         break;
65       case '\'':
66         textBuf << L"&apos;";
67         break;
68       case '\"':
69         textBuf << L"&quot;";
70         break;
71       default:
72         textBuf.AppendChar(str[i]);
73     }
74   }
75   return textBuf.MakeString();
76 }
77 
IsXMLValidChar(wchar_t ch)78 bool IsXMLValidChar(wchar_t ch) {
79   return ch == 0x09 || ch == 0x0A || ch == 0x0D ||
80          (ch >= 0x20 && ch <= 0xD7FF) || (ch >= 0xE000 && ch <= 0xFFFD);
81 }
82 
ExportEncodeContent(const WideString & str)83 WideString ExportEncodeContent(const WideString& str) {
84   CFX_WideTextBuf textBuf;
85   int32_t iLen = str.GetLength();
86   for (int32_t i = 0; i < iLen; i++) {
87     wchar_t ch = str[i];
88     if (!IsXMLValidChar(ch))
89       continue;
90 
91     if (ch == '&') {
92       textBuf << L"&amp;";
93     } else if (ch == '<') {
94       textBuf << L"&lt;";
95     } else if (ch == '>') {
96       textBuf << L"&gt;";
97     } else if (ch == '\'') {
98       textBuf << L"&apos;";
99     } else if (ch == '\"') {
100       textBuf << L"&quot;";
101     } else if (ch == ' ') {
102       if (i && str[i - 1] != ' ') {
103         textBuf.AppendChar(' ');
104       } else {
105         textBuf << L"&#x20;";
106       }
107     } else {
108       textBuf.AppendChar(str[i]);
109     }
110   }
111   return textBuf.MakeString();
112 }
113 
AttributeSaveInDataModel(CXFA_Node * pNode,XFA_Attribute eAttribute)114 bool AttributeSaveInDataModel(CXFA_Node* pNode, XFA_Attribute eAttribute) {
115   bool bSaveInDataModel = false;
116   if (pNode->GetElementType() != XFA_Element::Image)
117     return bSaveInDataModel;
118 
119   CXFA_Node* pValueNode = pNode->GetParent();
120   if (!pValueNode || pValueNode->GetElementType() != XFA_Element::Value)
121     return bSaveInDataModel;
122 
123   CXFA_Node* pFieldNode = pValueNode->GetParent();
124   if (pFieldNode && pFieldNode->GetBindData() &&
125       eAttribute == XFA_Attribute::Href) {
126     bSaveInDataModel = true;
127   }
128   return bSaveInDataModel;
129 }
130 
ContentNodeNeedtoExport(CXFA_Node * pContentNode)131 bool ContentNodeNeedtoExport(CXFA_Node* pContentNode) {
132   Optional<WideString> wsContent =
133       pContentNode->JSObject()->TryContent(false, false);
134   if (!wsContent)
135     return false;
136 
137   ASSERT(pContentNode->IsContentNode());
138   CXFA_Node* pParentNode = pContentNode->GetParent();
139   if (!pParentNode || pParentNode->GetElementType() != XFA_Element::Value)
140     return true;
141 
142   CXFA_Node* pGrandParentNode = pParentNode->GetParent();
143   if (!pGrandParentNode || !pGrandParentNode->IsContainerNode())
144     return true;
145   if (pGrandParentNode->GetBindData())
146     return false;
147 
148   XFA_Element eUIType = pGrandParentNode->GetWidgetAcc()->GetUIType();
149   if (eUIType == XFA_Element::PasswordEdit)
150     return false;
151   return true;
152 }
153 
SaveAttribute(CXFA_Node * pNode,XFA_Attribute eName,const WideString & wsName,bool bProto,WideString & wsOutput)154 void SaveAttribute(CXFA_Node* pNode,
155                    XFA_Attribute eName,
156                    const WideString& wsName,
157                    bool bProto,
158                    WideString& wsOutput) {
159   if (!bProto && !pNode->JSObject()->HasAttribute(eName))
160     return;
161 
162   Optional<WideString> value = pNode->JSObject()->TryAttribute(eName, false);
163   if (!value)
164     return;
165 
166   wsOutput += L" ";
167   wsOutput += wsName;
168   wsOutput += L"=\"";
169   wsOutput += ExportEncodeAttribute(*value);
170   wsOutput += L"\"";
171 }
172 
RegenerateFormFile_Changed(CXFA_Node * pNode,CFX_WideTextBuf & buf,bool bSaveXML)173 void RegenerateFormFile_Changed(CXFA_Node* pNode,
174                                 CFX_WideTextBuf& buf,
175                                 bool bSaveXML) {
176   WideString wsAttrs;
177   for (size_t i = 0;; ++i) {
178     XFA_Attribute attr = pNode->GetAttribute(i);
179     if (attr == XFA_Attribute::Unknown)
180       break;
181 
182     if (attr == XFA_Attribute::Name ||
183         (AttributeSaveInDataModel(pNode, attr) && !bSaveXML)) {
184       continue;
185     }
186     WideString wsAttr;
187     SaveAttribute(pNode, attr, CXFA_Node::AttributeToName(attr), bSaveXML,
188                   wsAttr);
189     wsAttrs += wsAttr;
190   }
191 
192   WideString wsChildren;
193   switch (pNode->GetObjectType()) {
194     case XFA_ObjectType::ContentNode: {
195       if (!bSaveXML && !ContentNodeNeedtoExport(pNode))
196         break;
197 
198       CXFA_Node* pRawValueNode = pNode->GetFirstChild();
199       while (pRawValueNode &&
200              pRawValueNode->GetElementType() != XFA_Element::SharpxHTML &&
201              pRawValueNode->GetElementType() != XFA_Element::Sharptext &&
202              pRawValueNode->GetElementType() != XFA_Element::Sharpxml) {
203         pRawValueNode = pRawValueNode->GetNextSibling();
204       }
205       if (!pRawValueNode)
206         break;
207 
208       Optional<WideString> contentType =
209           pNode->JSObject()->TryAttribute(XFA_Attribute::ContentType, false);
210       if (pRawValueNode->GetElementType() == XFA_Element::SharpxHTML &&
211           (contentType && *contentType == L"text/html")) {
212         CFX_XMLNode* pExDataXML = pNode->GetXMLMappingNode();
213         if (!pExDataXML)
214           break;
215 
216         CFX_XMLNode* pRichTextXML =
217             pExDataXML->GetNodeItem(CFX_XMLNode::FirstChild);
218         if (!pRichTextXML)
219           break;
220 
221         auto pMemStream = pdfium::MakeRetain<CFX_MemoryStream>(true);
222         auto pTempStream =
223             pdfium::MakeRetain<CFX_SeekableStreamProxy>(pMemStream, true);
224 
225         pTempStream->SetCodePage(FX_CODEPAGE_UTF8);
226         pRichTextXML->SaveXMLNode(pTempStream);
227         wsChildren += WideString::FromUTF8(
228             ByteStringView(pMemStream->GetBuffer(), pMemStream->GetSize()));
229       } else if (pRawValueNode->GetElementType() == XFA_Element::Sharpxml &&
230                  (contentType && *contentType == L"text/xml")) {
231         Optional<WideString> rawValue = pRawValueNode->JSObject()->TryAttribute(
232             XFA_Attribute::Value, false);
233         if (!rawValue || rawValue->IsEmpty())
234           break;
235 
236         std::vector<WideString> wsSelTextArray;
237         size_t iStart = 0;
238         auto iEnd = rawValue->Find(L'\n', iStart);
239         iEnd = !iEnd.has_value() ? rawValue->GetLength() : iEnd;
240         while (iEnd.has_value() && iEnd >= iStart) {
241           wsSelTextArray.push_back(
242               rawValue->Mid(iStart, iEnd.value() - iStart));
243           iStart = iEnd.value() + 1;
244           if (iStart >= rawValue->GetLength())
245             break;
246           iEnd = rawValue->Find(L'\n', iStart);
247         }
248 
249         CXFA_Node* pParentNode = pNode->GetParent();
250         ASSERT(pParentNode);
251         CXFA_Node* pGrandparentNode = pParentNode->GetParent();
252         ASSERT(pGrandparentNode);
253         WideString bodyTagName;
254         bodyTagName =
255             pGrandparentNode->JSObject()->GetCData(XFA_Attribute::Name);
256         if (bodyTagName.IsEmpty())
257           bodyTagName = L"ListBox1";
258 
259         buf << L"<";
260         buf << bodyTagName;
261         buf << L" xmlns=\"\"\n>";
262         for (int32_t i = 0; i < pdfium::CollectionSize<int32_t>(wsSelTextArray);
263              i++) {
264           buf << L"<value\n>";
265           buf << ExportEncodeContent(wsSelTextArray[i]);
266           buf << L"</value\n>";
267         }
268         buf << L"</";
269         buf << bodyTagName;
270         buf << L"\n>";
271         wsChildren += buf.AsStringView();
272         buf.Clear();
273       } else {
274         WideString wsValue =
275             pRawValueNode->JSObject()->GetCData(XFA_Attribute::Value);
276         wsChildren += ExportEncodeContent(wsValue);
277       }
278       break;
279     }
280     case XFA_ObjectType::TextNode:
281     case XFA_ObjectType::NodeC:
282     case XFA_ObjectType::NodeV: {
283       WideString wsValue = pNode->JSObject()->GetCData(XFA_Attribute::Value);
284       wsChildren += ExportEncodeContent(wsValue);
285       break;
286     }
287     default:
288       if (pNode->GetElementType() == XFA_Element::Items) {
289         CXFA_Node* pTemplateNode = pNode->GetTemplateNodeIfExists();
290         if (!pTemplateNode ||
291             pTemplateNode->CountChildren(XFA_Element::Unknown, false) !=
292                 pNode->CountChildren(XFA_Element::Unknown, false)) {
293           bSaveXML = true;
294         }
295       }
296       CFX_WideTextBuf newBuf;
297       CXFA_Node* pChildNode = pNode->GetFirstChild();
298       while (pChildNode) {
299         RegenerateFormFile_Changed(pChildNode, newBuf, bSaveXML);
300         wsChildren += newBuf.AsStringView();
301         newBuf.Clear();
302         pChildNode = pChildNode->GetNextSibling();
303       }
304       if (!bSaveXML && !wsChildren.IsEmpty() &&
305           pNode->GetElementType() == XFA_Element::Items) {
306         wsChildren.clear();
307         bSaveXML = true;
308         CXFA_Node* pChild = pNode->GetFirstChild();
309         while (pChild) {
310           RegenerateFormFile_Changed(pChild, newBuf, bSaveXML);
311           wsChildren += newBuf.AsStringView();
312           newBuf.Clear();
313           pChild = pChild->GetNextSibling();
314         }
315       }
316       break;
317   }
318 
319   if (!wsChildren.IsEmpty() || !wsAttrs.IsEmpty() ||
320       pNode->JSObject()->HasAttribute(XFA_Attribute::Name)) {
321     WideStringView wsElement = pNode->GetClassName();
322     WideString wsName;
323     SaveAttribute(pNode, XFA_Attribute::Name, L"name", true, wsName);
324     buf << L"<";
325     buf << wsElement;
326     buf << wsName;
327     buf << wsAttrs;
328     if (wsChildren.IsEmpty()) {
329       buf << L"\n/>";
330     } else {
331       buf << L"\n>";
332       buf << wsChildren;
333       buf << L"</";
334       buf << wsElement;
335       buf << L"\n>";
336     }
337   }
338 }
339 
RegenerateFormFile_Container(CXFA_Node * pNode,const RetainPtr<CFX_SeekableStreamProxy> & pStream,bool bSaveXML)340 void RegenerateFormFile_Container(
341     CXFA_Node* pNode,
342     const RetainPtr<CFX_SeekableStreamProxy>& pStream,
343     bool bSaveXML) {
344   XFA_Element eType = pNode->GetElementType();
345   if (eType == XFA_Element::Field || eType == XFA_Element::Draw ||
346       !pNode->IsContainerNode()) {
347     CFX_WideTextBuf buf;
348     RegenerateFormFile_Changed(pNode, buf, bSaveXML);
349     size_t nLen = buf.GetLength();
350     if (nLen > 0)
351       pStream->WriteString(buf.AsStringView());
352     return;
353   }
354 
355   WideStringView wsElement(pNode->GetClassName());
356   pStream->WriteString(L"<");
357   pStream->WriteString(wsElement);
358 
359   WideString wsOutput;
360   SaveAttribute(pNode, XFA_Attribute::Name, L"name", true, wsOutput);
361 
362   WideString wsAttrs;
363   for (size_t i = 0;; ++i) {
364     XFA_Attribute attr = pNode->GetAttribute(i);
365     if (attr == XFA_Attribute::Unknown)
366       break;
367     if (attr == XFA_Attribute::Name)
368       continue;
369 
370     WideString wsAttr;
371     SaveAttribute(pNode, attr, CXFA_Node::AttributeToName(attr), false, wsAttr);
372     wsOutput += wsAttr;
373   }
374 
375   if (!wsOutput.IsEmpty())
376     pStream->WriteString(wsOutput.AsStringView());
377 
378   CXFA_Node* pChildNode = pNode->GetFirstChild();
379   if (pChildNode) {
380     pStream->WriteString(L"\n>");
381     while (pChildNode) {
382       RegenerateFormFile_Container(pChildNode, pStream, bSaveXML);
383       pChildNode = pChildNode->GetNextSibling();
384     }
385     pStream->WriteString(L"</");
386     pStream->WriteString(wsElement);
387     pStream->WriteString(L"\n>");
388   } else {
389     pStream->WriteString(L"\n/>");
390   }
391 }
392 
RecognizeXFAVersionNumber(CXFA_Node * pTemplateRoot)393 WideString RecognizeXFAVersionNumber(CXFA_Node* pTemplateRoot) {
394   if (!pTemplateRoot)
395     return WideString();
396 
397   Optional<WideString> templateNS = pTemplateRoot->JSObject()->TryNamespace();
398   if (!templateNS)
399     return WideString();
400 
401   XFA_VERSION eVersion =
402       pTemplateRoot->GetDocument()->RecognizeXFAVersionNumber(*templateNS);
403   if (eVersion == XFA_VERSION_UNKNOWN)
404     eVersion = XFA_VERSION_DEFAULT;
405 
406   return WideString::Format(L"%i.%i", eVersion / 100, eVersion % 100);
407 }
408 
409 }  // namespace
410 
XFA_GetFractionalScale(uint32_t idx)411 double XFA_GetFractionalScale(uint32_t idx) {
412   return fraction_scales[idx];
413 }
414 
XFA_GetMaxFractionalScale()415 int XFA_GetMaxFractionalScale() {
416   return FX_ArraySize(fraction_scales);
417 }
418 
XFA_GetLocaleValue(CXFA_Node * pNode)419 CXFA_LocaleValue XFA_GetLocaleValue(CXFA_Node* pNode) {
420   CXFA_Value* pNodeValue =
421       pNode->GetChild<CXFA_Value>(0, XFA_Element::Value, false);
422   if (!pNodeValue)
423     return CXFA_LocaleValue();
424 
425   CXFA_Node* pValueChild = pNodeValue->GetFirstChild();
426   if (!pValueChild)
427     return CXFA_LocaleValue();
428 
429   int32_t iVTType = XFA_VT_NULL;
430   switch (pValueChild->GetElementType()) {
431     case XFA_Element::Decimal:
432       iVTType = XFA_VT_DECIMAL;
433       break;
434     case XFA_Element::Float:
435       iVTType = XFA_VT_FLOAT;
436       break;
437     case XFA_Element::Date:
438       iVTType = XFA_VT_DATE;
439       break;
440     case XFA_Element::Time:
441       iVTType = XFA_VT_TIME;
442       break;
443     case XFA_Element::DateTime:
444       iVTType = XFA_VT_DATETIME;
445       break;
446     case XFA_Element::Boolean:
447       iVTType = XFA_VT_BOOLEAN;
448       break;
449     case XFA_Element::Integer:
450       iVTType = XFA_VT_INTEGER;
451       break;
452     case XFA_Element::Text:
453       iVTType = XFA_VT_TEXT;
454       break;
455     default:
456       iVTType = XFA_VT_NULL;
457       break;
458   }
459   return CXFA_LocaleValue(iVTType, pNode->GetRawValue(),
460                           pNode->GetDocument()->GetLocalMgr());
461 }
462 
XFA_FDEExtension_ResolveNamespaceQualifier(CFX_XMLElement * pNode,const WideString & wsQualifier,WideString * wsNamespaceURI)463 bool XFA_FDEExtension_ResolveNamespaceQualifier(CFX_XMLElement* pNode,
464                                                 const WideString& wsQualifier,
465                                                 WideString* wsNamespaceURI) {
466   if (!pNode)
467     return false;
468 
469   CFX_XMLNode* pFakeRoot = pNode->GetNodeItem(CFX_XMLNode::Root);
470   WideString wsNSAttribute;
471   bool bRet = false;
472   if (wsQualifier.IsEmpty()) {
473     wsNSAttribute = L"xmlns";
474     bRet = true;
475   } else {
476     wsNSAttribute = L"xmlns:" + wsQualifier;
477   }
478   for (CFX_XMLNode* pParent = pNode; pParent != pFakeRoot;
479        pParent = pParent->GetNodeItem(CFX_XMLNode::Parent)) {
480     if (pParent->GetType() != FX_XMLNODE_Element)
481       continue;
482 
483     auto* pElement = static_cast<CFX_XMLElement*>(pParent);
484     if (pElement->HasAttribute(wsNSAttribute.c_str())) {
485       *wsNamespaceURI = pElement->GetString(wsNSAttribute.c_str());
486       return true;
487     }
488   }
489   wsNamespaceURI->clear();
490   return bRet;
491 }
492 
XFA_DataExporter_DealWithDataGroupNode(CXFA_Node * pDataNode)493 void XFA_DataExporter_DealWithDataGroupNode(CXFA_Node* pDataNode) {
494   if (!pDataNode || pDataNode->GetElementType() == XFA_Element::DataValue)
495     return;
496 
497   int32_t iChildNum = 0;
498   for (CXFA_Node* pChildNode = pDataNode->GetFirstChild(); pChildNode;
499        pChildNode = pChildNode->GetNextSibling()) {
500     iChildNum++;
501     XFA_DataExporter_DealWithDataGroupNode(pChildNode);
502   }
503 
504   if (pDataNode->GetElementType() != XFA_Element::DataGroup)
505     return;
506 
507   if (iChildNum > 0) {
508     CFX_XMLNode* pXMLNode = pDataNode->GetXMLMappingNode();
509     ASSERT(pXMLNode->GetType() == FX_XMLNODE_Element);
510     CFX_XMLElement* pXMLElement = static_cast<CFX_XMLElement*>(pXMLNode);
511     if (pXMLElement->HasAttribute(L"xfa:dataNode"))
512       pXMLElement->RemoveAttribute(L"xfa:dataNode");
513 
514     return;
515   }
516 
517   CFX_XMLNode* pXMLNode = pDataNode->GetXMLMappingNode();
518   ASSERT(pXMLNode->GetType() == FX_XMLNODE_Element);
519   static_cast<CFX_XMLElement*>(pXMLNode)->SetString(L"xfa:dataNode",
520                                                     L"dataGroup");
521 }
522 
XFA_DataExporter_RegenerateFormFile(CXFA_Node * pNode,const RetainPtr<CFX_SeekableStreamProxy> & pStream,const char * pChecksum,bool bSaveXML)523 void XFA_DataExporter_RegenerateFormFile(
524     CXFA_Node* pNode,
525     const RetainPtr<CFX_SeekableStreamProxy>& pStream,
526     const char* pChecksum,
527     bool bSaveXML) {
528   if (pNode->IsModelNode()) {
529     pStream->WriteString(L"<form");
530     if (pChecksum) {
531       WideString wsChecksum = WideString::FromUTF8(pChecksum);
532       pStream->WriteString(L" checksum=\"");
533       pStream->WriteString(wsChecksum.AsStringView());
534       pStream->WriteString(L"\"");
535     }
536     pStream->WriteString(L" xmlns=\"");
537     pStream->WriteString(WideStringView(kFormNS));
538 
539     WideString wsVersionNumber = RecognizeXFAVersionNumber(
540         ToNode(pNode->GetDocument()->GetXFAObject(XFA_HASHCODE_Template)));
541     if (wsVersionNumber.IsEmpty())
542       wsVersionNumber = L"2.8";
543 
544     wsVersionNumber += L"/\"\n>";
545     pStream->WriteString(wsVersionNumber.AsStringView());
546 
547     CXFA_Node* pChildNode = pNode->GetFirstChild();
548     while (pChildNode) {
549       RegenerateFormFile_Container(pChildNode, pStream, false);
550       pChildNode = pChildNode->GetNextSibling();
551     }
552     pStream->WriteString(L"</form\n>");
553   } else {
554     RegenerateFormFile_Container(pNode, pStream, bSaveXML);
555   }
556 }
557 
XFA_FieldIsMultiListBox(CXFA_Node * pFieldNode)558 bool XFA_FieldIsMultiListBox(CXFA_Node* pFieldNode) {
559   if (!pFieldNode)
560     return false;
561 
562   CXFA_Ui* pUIChild = pFieldNode->GetChild<CXFA_Ui>(0, XFA_Element::Ui, false);
563   if (!pUIChild)
564     return false;
565 
566   CXFA_Node* pFirstChild = pUIChild->GetFirstChild();
567   if (!pFirstChild ||
568       pFirstChild->GetElementType() != XFA_Element::ChoiceList) {
569     return false;
570   }
571 
572   return pFirstChild->JSObject()->GetEnum(XFA_Attribute::Open) ==
573          XFA_AttributeEnum::MultiSelect;
574 }
575 
XFA_MapRotation(int32_t nRotation)576 int32_t XFA_MapRotation(int32_t nRotation) {
577   nRotation = nRotation % 360;
578   nRotation = nRotation < 0 ? nRotation + 360 : nRotation;
579   return nRotation;
580 }
581 
XFA_GetScriptAttributeByName(XFA_Element eElement,const WideStringView & wsAttributeName)582 const XFA_SCRIPTATTRIBUTEINFO* XFA_GetScriptAttributeByName(
583     XFA_Element eElement,
584     const WideStringView& wsAttributeName) {
585   if (wsAttributeName.IsEmpty())
586     return nullptr;
587 
588   int32_t iElementIndex = static_cast<int32_t>(eElement);
589   while (iElementIndex != -1) {
590     const XFA_SCRIPTHIERARCHY* scriptIndex = g_XFAScriptIndex + iElementIndex;
591     int32_t icount = scriptIndex->wAttributeCount;
592     if (icount == 0) {
593       iElementIndex = scriptIndex->wParentIndex;
594       continue;
595     }
596     uint32_t uHash = FX_HashCode_GetW(wsAttributeName, false);
597     int32_t iStart = scriptIndex->wAttributeStart, iEnd = iStart + icount - 1;
598     do {
599       int32_t iMid = (iStart + iEnd) / 2;
600       const XFA_SCRIPTATTRIBUTEINFO* pInfo = g_SomAttributeData + iMid;
601       if (uHash == pInfo->uHash)
602         return pInfo;
603       if (uHash < pInfo->uHash)
604         iEnd = iMid - 1;
605       else
606         iStart = iMid + 1;
607     } while (iStart <= iEnd);
608     iElementIndex = scriptIndex->wParentIndex;
609   }
610   return nullptr;
611 }
612