Home | History | Annotate | Download | only in parser
      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/cxfa_document.h"
      8 
      9 #include "core/fxcrt/fx_extension.h"
     10 #include "fxjs/cfxjse_engine.h"
     11 #include "xfa/fxfa/cxfa_ffnotify.h"
     12 #include "xfa/fxfa/parser/cscript_datawindow.h"
     13 #include "xfa/fxfa/parser/cscript_eventpseudomodel.h"
     14 #include "xfa/fxfa/parser/cscript_hostpseudomodel.h"
     15 #include "xfa/fxfa/parser/cscript_layoutpseudomodel.h"
     16 #include "xfa/fxfa/parser/cscript_logpseudomodel.h"
     17 #include "xfa/fxfa/parser/cscript_signaturepseudomodel.h"
     18 #include "xfa/fxfa/parser/cxfa_datagroup.h"
     19 #include "xfa/fxfa/parser/cxfa_document_parser.h"
     20 #include "xfa/fxfa/parser/cxfa_interactive.h"
     21 #include "xfa/fxfa/parser/cxfa_layoutprocessor.h"
     22 #include "xfa/fxfa/parser/cxfa_localemgr.h"
     23 #include "xfa/fxfa/parser/cxfa_node.h"
     24 #include "xfa/fxfa/parser/cxfa_pdf.h"
     25 #include "xfa/fxfa/parser/cxfa_present.h"
     26 #include "xfa/fxfa/parser/cxfa_traversestrategy_xfanode.h"
     27 #include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
     28 #include "xfa/fxfa/parser/xfa_utils.h"
     29 
     30 namespace {
     31 
     32 constexpr const wchar_t kTemplateNS[] =
     33     L"http://www.xfa.org/schema/xfa-template/";
     34 
     35 void MergeNodeRecurse(CXFA_Document* pDocument,
     36                       CXFA_Node* pDestNodeParent,
     37                       CXFA_Node* pProtoNode) {
     38   CXFA_Node* pExistingNode = nullptr;
     39   for (CXFA_Node* pFormChild = pDestNodeParent->GetFirstChild(); pFormChild;
     40        pFormChild = pFormChild->GetNextSibling()) {
     41     if (pFormChild->GetElementType() == pProtoNode->GetElementType() &&
     42         pFormChild->GetNameHash() == pProtoNode->GetNameHash() &&
     43         pFormChild->IsUnusedNode()) {
     44       pFormChild->ClearFlag(XFA_NodeFlag_UnusedNode);
     45       pExistingNode = pFormChild;
     46       break;
     47     }
     48   }
     49 
     50   if (pExistingNode) {
     51     pExistingNode->SetTemplateNode(pProtoNode);
     52     for (CXFA_Node* pTemplateChild = pProtoNode->GetFirstChild();
     53          pTemplateChild; pTemplateChild = pTemplateChild->GetNextSibling()) {
     54       MergeNodeRecurse(pDocument, pExistingNode, pTemplateChild);
     55     }
     56     return;
     57   }
     58   CXFA_Node* pNewNode = pProtoNode->Clone(true);
     59   pNewNode->SetTemplateNode(pProtoNode);
     60   pDestNodeParent->InsertChild(pNewNode, nullptr);
     61 }
     62 
     63 void MergeNode(CXFA_Document* pDocument,
     64                CXFA_Node* pDestNode,
     65                CXFA_Node* pProtoNode) {
     66   {
     67     CXFA_NodeIterator sIterator(pDestNode);
     68     for (CXFA_Node* pNode = sIterator.GetCurrent(); pNode;
     69          pNode = sIterator.MoveToNext()) {
     70       pNode->SetFlag(XFA_NodeFlag_UnusedNode, true);
     71     }
     72   }
     73   pDestNode->SetTemplateNode(pProtoNode);
     74   for (CXFA_Node* pTemplateChild = pProtoNode->GetFirstChild(); pTemplateChild;
     75        pTemplateChild = pTemplateChild->GetNextSibling()) {
     76     MergeNodeRecurse(pDocument, pDestNode, pTemplateChild);
     77   }
     78   {
     79     CXFA_NodeIterator sIterator(pDestNode);
     80     for (CXFA_Node* pNode = sIterator.GetCurrent(); pNode;
     81          pNode = sIterator.MoveToNext()) {
     82       pNode->ClearFlag(XFA_NodeFlag_UnusedNode);
     83     }
     84   }
     85 }
     86 
     87 }  // namespace
     88 
     89 CXFA_Document::CXFA_Document(CXFA_DocumentParser* pParser)
     90     : m_pParser(pParser),
     91       m_pRootNode(nullptr),
     92       m_eCurVersionMode(XFA_VERSION_DEFAULT),
     93       m_dwDocFlags(0) {
     94   ASSERT(m_pParser);
     95 }
     96 
     97 CXFA_Document::~CXFA_Document() {
     98   // Remove all the bindings before freeing the node as the ownership is wonky.
     99   if (m_pRootNode)
    100     m_pRootNode->ReleaseBindingNodes();
    101 
    102   delete m_pRootNode;
    103 
    104   for (CXFA_Node* pNode : m_PurgeNodes)
    105     delete pNode;
    106   m_PurgeNodes.clear();
    107 }
    108 
    109 CXFA_LayoutProcessor* CXFA_Document::GetLayoutProcessor() {
    110   if (!m_pLayoutProcessor)
    111     m_pLayoutProcessor = pdfium::MakeUnique<CXFA_LayoutProcessor>(this);
    112   return m_pLayoutProcessor.get();
    113 }
    114 
    115 CXFA_LayoutProcessor* CXFA_Document::GetDocLayout() {
    116   return GetLayoutProcessor();
    117 }
    118 
    119 void CXFA_Document::ClearLayoutData() {
    120   m_pLayoutProcessor.reset();
    121   m_pScriptContext.reset();
    122   m_pLocalMgr.reset();
    123   m_pScriptDataWindow.reset();
    124   m_pScriptEvent.reset();
    125   m_pScriptHost.reset();
    126   m_pScriptLog.reset();
    127   m_pScriptLayout.reset();
    128   m_pScriptSignature.reset();
    129 }
    130 
    131 void CXFA_Document::SetRoot(CXFA_Node* pNewRoot) {
    132   if (m_pRootNode)
    133     AddPurgeNode(m_pRootNode);
    134 
    135   m_pRootNode = pNewRoot;
    136   RemovePurgeNode(pNewRoot);
    137 }
    138 
    139 CFX_XMLDoc* CXFA_Document::GetXMLDoc() const {
    140   return m_pParser->GetXMLDoc();
    141 }
    142 
    143 CXFA_FFNotify* CXFA_Document::GetNotify() const {
    144   return m_pParser->GetNotify();
    145 }
    146 
    147 CXFA_Object* CXFA_Document::GetXFAObject(XFA_HashCode dwNodeNameHash) {
    148   switch (dwNodeNameHash) {
    149     case XFA_HASHCODE_Data: {
    150       CXFA_Node* pDatasetsNode = ToNode(GetXFAObject(XFA_HASHCODE_Datasets));
    151       if (!pDatasetsNode)
    152         return nullptr;
    153 
    154       for (CXFA_DataGroup* pDatasetsChild =
    155                pDatasetsNode->GetFirstChildByClass<CXFA_DataGroup>(
    156                    XFA_Element::DataGroup);
    157            pDatasetsChild;
    158            pDatasetsChild =
    159                pDatasetsChild->GetNextSameClassSibling<CXFA_DataGroup>(
    160                    XFA_Element::DataGroup)) {
    161         if (pDatasetsChild->GetNameHash() != XFA_HASHCODE_Data)
    162           continue;
    163 
    164         Optional<WideString> namespaceURI =
    165             pDatasetsChild->JSObject()->TryNamespace();
    166         if (!namespaceURI)
    167           continue;
    168 
    169         Optional<WideString> datasetsURI =
    170             pDatasetsNode->JSObject()->TryNamespace();
    171         if (!datasetsURI)
    172           continue;
    173         if (*namespaceURI == *datasetsURI)
    174           return pDatasetsChild;
    175       }
    176       return nullptr;
    177     }
    178     case XFA_HASHCODE_Record: {
    179       CXFA_Node* pData = ToNode(GetXFAObject(XFA_HASHCODE_Data));
    180       return pData ? pData->GetFirstChildByClass<CXFA_DataGroup>(
    181                          XFA_Element::DataGroup)
    182                    : nullptr;
    183     }
    184     case XFA_HASHCODE_DataWindow: {
    185       if (!m_pScriptDataWindow)
    186         m_pScriptDataWindow = pdfium::MakeUnique<CScript_DataWindow>(this);
    187       return m_pScriptDataWindow.get();
    188     }
    189     case XFA_HASHCODE_Event: {
    190       if (!m_pScriptEvent)
    191         m_pScriptEvent = pdfium::MakeUnique<CScript_EventPseudoModel>(this);
    192       return m_pScriptEvent.get();
    193     }
    194     case XFA_HASHCODE_Host: {
    195       if (!m_pScriptHost)
    196         m_pScriptHost = pdfium::MakeUnique<CScript_HostPseudoModel>(this);
    197       return m_pScriptHost.get();
    198     }
    199     case XFA_HASHCODE_Log: {
    200       if (!m_pScriptLog)
    201         m_pScriptLog = pdfium::MakeUnique<CScript_LogPseudoModel>(this);
    202       return m_pScriptLog.get();
    203     }
    204     case XFA_HASHCODE_Signature: {
    205       if (!m_pScriptSignature)
    206         m_pScriptSignature =
    207             pdfium::MakeUnique<CScript_SignaturePseudoModel>(this);
    208       return m_pScriptSignature.get();
    209     }
    210     case XFA_HASHCODE_Layout: {
    211       if (!m_pScriptLayout)
    212         m_pScriptLayout = pdfium::MakeUnique<CScript_LayoutPseudoModel>(this);
    213       return m_pScriptLayout.get();
    214     }
    215     default:
    216       return m_pRootNode->GetFirstChildByName(dwNodeNameHash);
    217   }
    218 }
    219 
    220 CXFA_Node* CXFA_Document::CreateNode(XFA_PacketType packet,
    221                                      XFA_Element eElement) {
    222   if (eElement == XFA_Element::Unknown)
    223     return nullptr;
    224 
    225   std::unique_ptr<CXFA_Node> pNode = CXFA_Node::Create(this, eElement, packet);
    226   if (!pNode)
    227     return nullptr;
    228 
    229   // TODO(dsinclair): AddPrugeNode should take ownership of the pointer.
    230   AddPurgeNode(pNode.get());
    231   return pNode.release();
    232 }
    233 
    234 void CXFA_Document::AddPurgeNode(CXFA_Node* pNode) {
    235   m_PurgeNodes.insert(pNode);
    236 }
    237 
    238 bool CXFA_Document::RemovePurgeNode(CXFA_Node* pNode) {
    239   return !!m_PurgeNodes.erase(pNode);
    240 }
    241 
    242 void CXFA_Document::SetFlag(uint32_t dwFlag, bool bOn) {
    243   if (bOn)
    244     m_dwDocFlags |= dwFlag;
    245   else
    246     m_dwDocFlags &= ~dwFlag;
    247 }
    248 
    249 bool CXFA_Document::IsInteractive() {
    250   if (m_dwDocFlags & XFA_DOCFLAG_HasInteractive)
    251     return !!(m_dwDocFlags & XFA_DOCFLAG_Interactive);
    252 
    253   CXFA_Node* pConfig = ToNode(GetXFAObject(XFA_HASHCODE_Config));
    254   if (!pConfig)
    255     return false;
    256 
    257   CXFA_Present* pPresent =
    258       pConfig->GetFirstChildByClass<CXFA_Present>(XFA_Element::Present);
    259   if (!pPresent)
    260     return false;
    261 
    262   CXFA_Pdf* pPDF = pPresent->GetFirstChildByClass<CXFA_Pdf>(XFA_Element::Pdf);
    263   if (!pPDF)
    264     return false;
    265 
    266   CXFA_Interactive* pFormFiller =
    267       pPDF->GetChild<CXFA_Interactive>(0, XFA_Element::Interactive, false);
    268   if (pFormFiller) {
    269     m_dwDocFlags |= XFA_DOCFLAG_HasInteractive;
    270 
    271     WideString wsInteractive = pFormFiller->JSObject()->GetContent(false);
    272     if (wsInteractive == L"1") {
    273       m_dwDocFlags |= XFA_DOCFLAG_Interactive;
    274       return true;
    275     }
    276   }
    277   return false;
    278 }
    279 
    280 CXFA_LocaleMgr* CXFA_Document::GetLocalMgr() {
    281   if (!m_pLocalMgr) {
    282     m_pLocalMgr = pdfium::MakeUnique<CXFA_LocaleMgr>(
    283         ToNode(GetXFAObject(XFA_HASHCODE_LocaleSet)),
    284         GetNotify()->GetAppProvider()->GetLanguage());
    285   }
    286   return m_pLocalMgr.get();
    287 }
    288 
    289 CFXJSE_Engine* CXFA_Document::InitScriptContext(v8::Isolate* pIsolate) {
    290   ASSERT(!m_pScriptContext);
    291   m_pScriptContext = pdfium::MakeUnique<CFXJSE_Engine>(this, pIsolate);
    292   return m_pScriptContext.get();
    293 }
    294 
    295 // We have to call |InitScriptContext| before any calls to |GetScriptContext|
    296 // or the context won't have an isolate set into it.
    297 CFXJSE_Engine* CXFA_Document::GetScriptContext() {
    298   ASSERT(m_pScriptContext);
    299   return m_pScriptContext.get();
    300 }
    301 
    302 XFA_VERSION CXFA_Document::RecognizeXFAVersionNumber(
    303     const WideString& wsTemplateNS) {
    304   WideStringView wsTemplateURIPrefix(kTemplateNS);
    305   size_t nPrefixLength = wsTemplateURIPrefix.GetLength();
    306   if (WideStringView(wsTemplateNS.c_str(), wsTemplateNS.GetLength()) !=
    307       wsTemplateURIPrefix) {
    308     return XFA_VERSION_UNKNOWN;
    309   }
    310   auto nDotPos = wsTemplateNS.Find('.', nPrefixLength);
    311   if (!nDotPos.has_value())
    312     return XFA_VERSION_UNKNOWN;
    313 
    314   int8_t iMajor = FXSYS_wtoi(
    315       wsTemplateNS.Mid(nPrefixLength, nDotPos.value() - nPrefixLength).c_str());
    316   int8_t iMinor =
    317       FXSYS_wtoi(wsTemplateNS
    318                      .Mid(nDotPos.value() + 1,
    319                           wsTemplateNS.GetLength() - nDotPos.value() - 2)
    320                      .c_str());
    321   XFA_VERSION eVersion = (XFA_VERSION)((int32_t)iMajor * 100 + iMinor);
    322   if (eVersion < XFA_VERSION_MIN || eVersion > XFA_VERSION_MAX)
    323     return XFA_VERSION_UNKNOWN;
    324 
    325   m_eCurVersionMode = eVersion;
    326   return eVersion;
    327 }
    328 
    329 CXFA_Node* CXFA_Document::GetNodeByID(CXFA_Node* pRoot,
    330                                       const WideStringView& wsID) {
    331   if (!pRoot || wsID.IsEmpty())
    332     return nullptr;
    333 
    334   CXFA_NodeIterator sIterator(pRoot);
    335   for (CXFA_Node* pNode = sIterator.GetCurrent(); pNode;
    336        pNode = sIterator.MoveToNext()) {
    337     WideString wsIDVal = pNode->JSObject()->GetCData(XFA_Attribute::Id);
    338     if (!wsIDVal.IsEmpty() && wsIDVal == wsID)
    339       return pNode;
    340   }
    341   return nullptr;
    342 }
    343 
    344 void CXFA_Document::DoProtoMerge() {
    345   CXFA_Node* pTemplateRoot = ToNode(GetXFAObject(XFA_HASHCODE_Template));
    346   if (!pTemplateRoot)
    347     return;
    348 
    349   std::map<uint32_t, CXFA_Node*> mIDMap;
    350   std::set<CXFA_Node*> sUseNodes;
    351   CXFA_NodeIterator sIterator(pTemplateRoot);
    352   for (CXFA_Node* pNode = sIterator.GetCurrent(); pNode;
    353        pNode = sIterator.MoveToNext()) {
    354     WideString wsIDVal = pNode->JSObject()->GetCData(XFA_Attribute::Id);
    355     if (!wsIDVal.IsEmpty())
    356       mIDMap[FX_HashCode_GetW(wsIDVal.AsStringView(), false)] = pNode;
    357 
    358     WideString wsUseVal = pNode->JSObject()->GetCData(XFA_Attribute::Use);
    359     if (!wsUseVal.IsEmpty()) {
    360       sUseNodes.insert(pNode);
    361     } else {
    362       wsUseVal = pNode->JSObject()->GetCData(XFA_Attribute::Usehref);
    363       if (!wsUseVal.IsEmpty())
    364         sUseNodes.insert(pNode);
    365     }
    366   }
    367 
    368   for (CXFA_Node* pUseHrefNode : sUseNodes) {
    369     WideStringView wsURI;
    370     WideStringView wsID;
    371     WideStringView wsSOM;
    372 
    373     WideString wsUseVal =
    374         pUseHrefNode->JSObject()->GetCData(XFA_Attribute::Usehref);
    375     if (!wsUseVal.IsEmpty()) {
    376       auto uSharpPos = wsUseVal.Find('#');
    377       if (!uSharpPos.has_value()) {
    378         wsURI = wsUseVal.AsStringView();
    379       } else {
    380         wsURI = WideStringView(wsUseVal.c_str(), uSharpPos.value());
    381         size_t uLen = wsUseVal.GetLength();
    382         if (uLen >= uSharpPos.value() + 5 &&
    383             WideStringView(wsUseVal.c_str() + uSharpPos.value(), 5) ==
    384                 L"#som(" &&
    385             wsUseVal[uLen - 1] == ')') {
    386           wsSOM = WideStringView(wsUseVal.c_str() + uSharpPos.value() + 5,
    387                                  uLen - 1 - uSharpPos.value() - 5);
    388         } else {
    389           wsID = WideStringView(wsUseVal.c_str() + uSharpPos.value() + 1,
    390                                 uLen - uSharpPos.value() - 1);
    391         }
    392       }
    393     } else {
    394       wsUseVal = pUseHrefNode->JSObject()->GetCData(XFA_Attribute::Use);
    395       if (!wsUseVal.IsEmpty()) {
    396         if (wsUseVal[0] == '#')
    397           wsID = WideStringView(wsUseVal.c_str() + 1, wsUseVal.GetLength() - 1);
    398         else
    399           wsSOM = WideStringView(wsUseVal.c_str(), wsUseVal.GetLength());
    400       }
    401     }
    402 
    403     if (!wsURI.IsEmpty() && wsURI != L".")
    404       continue;
    405 
    406     CXFA_Node* pProtoNode = nullptr;
    407     if (!wsSOM.IsEmpty()) {
    408       uint32_t dwFlag = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Attributes |
    409                         XFA_RESOLVENODE_Properties | XFA_RESOLVENODE_Parent |
    410                         XFA_RESOLVENODE_Siblings;
    411       XFA_RESOLVENODE_RS resolveNodeRS;
    412       int32_t iRet = m_pScriptContext->ResolveObjects(
    413           pUseHrefNode, wsSOM, &resolveNodeRS, dwFlag, nullptr);
    414       if (iRet > 0 && resolveNodeRS.objects.front()->IsNode())
    415         pProtoNode = resolveNodeRS.objects.front()->AsNode();
    416     } else if (!wsID.IsEmpty()) {
    417       auto it = mIDMap.find(FX_HashCode_GetW(wsID, false));
    418       if (it == mIDMap.end())
    419         continue;
    420       pProtoNode = it->second;
    421     }
    422     if (!pProtoNode)
    423       continue;
    424 
    425     MergeNode(this, pUseHrefNode, pProtoNode);
    426   }
    427 }
    428