blob: 346690b484d84b0dedf23b30d7260977805ca470 [file] [log] [blame]
// Copyright 2014 PDFium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
#include "core/fxcrt/include/fx_ext.h"
#include "xfa/fxfa/app/xfa_ffnotify.h"
#include "xfa/fxfa/parser/cscript_datawindow.h"
#include "xfa/fxfa/parser/cscript_eventpseudomodel.h"
#include "xfa/fxfa/parser/cscript_hostpseudomodel.h"
#include "xfa/fxfa/parser/cscript_layoutpseudomodel.h"
#include "xfa/fxfa/parser/cscript_logpseudomodel.h"
#include "xfa/fxfa/parser/cscript_signaturepseudomodel.h"
#include "xfa/fxfa/parser/cxfa_document.h"
#include "xfa/fxfa/parser/cxfa_document_parser.h"
#include "xfa/fxfa/parser/cxfa_layoutprocessor.h"
#include "xfa/fxfa/parser/cxfa_scriptcontext.h"
#include "xfa/fxfa/parser/xfa_localemgr.h"
#include "xfa/fxfa/parser/xfa_object.h"
#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
#include "xfa/fxfa/parser/xfa_utils.h"
namespace {
void MergeNodeRecurse(CXFA_Document* pDocument,
CXFA_Node* pDestNodeParent,
CXFA_Node* pProtoNode) {
CXFA_Node* pExistingNode = nullptr;
for (CXFA_Node* pFormChild =
pDestNodeParent->GetNodeItem(XFA_NODEITEM_FirstChild);
pFormChild;
pFormChild = pFormChild->GetNodeItem(XFA_NODEITEM_NextSibling)) {
if (pFormChild->GetElementType() == pProtoNode->GetElementType() &&
pFormChild->GetNameHash() == pProtoNode->GetNameHash() &&
pFormChild->IsUnusedNode()) {
pFormChild->ClearFlag(XFA_NodeFlag_UnusedNode);
pExistingNode = pFormChild;
break;
}
}
if (pExistingNode) {
pExistingNode->SetTemplateNode(pProtoNode);
for (CXFA_Node* pTemplateChild =
pProtoNode->GetNodeItem(XFA_NODEITEM_FirstChild);
pTemplateChild; pTemplateChild = pTemplateChild->GetNodeItem(
XFA_NODEITEM_NextSibling)) {
MergeNodeRecurse(pDocument, pExistingNode, pTemplateChild);
}
return;
}
CXFA_Node* pNewNode = pProtoNode->Clone(TRUE);
pNewNode->SetTemplateNode(pProtoNode);
pDestNodeParent->InsertChild(pNewNode, nullptr);
}
void MergeNode(CXFA_Document* pDocument,
CXFA_Node* pDestNode,
CXFA_Node* pProtoNode) {
{
CXFA_NodeIterator sIterator(pDestNode);
for (CXFA_Node* pNode = sIterator.GetCurrent(); pNode;
pNode = sIterator.MoveToNext()) {
pNode->SetFlag(XFA_NodeFlag_UnusedNode, true);
}
}
pDestNode->SetTemplateNode(pProtoNode);
for (CXFA_Node* pTemplateChild =
pProtoNode->GetNodeItem(XFA_NODEITEM_FirstChild);
pTemplateChild;
pTemplateChild = pTemplateChild->GetNodeItem(XFA_NODEITEM_NextSibling)) {
MergeNodeRecurse(pDocument, pDestNode, pTemplateChild);
}
{
CXFA_NodeIterator sIterator(pDestNode);
for (CXFA_Node* pNode = sIterator.GetCurrent(); pNode;
pNode = sIterator.MoveToNext()) {
pNode->ClearFlag(XFA_NodeFlag_UnusedNode);
}
}
}
} // namespace
CXFA_Document::CXFA_Document(CXFA_DocumentParser* pParser)
: m_pParser(pParser),
m_pScriptContext(nullptr),
m_pLayoutProcessor(nullptr),
m_pRootNode(nullptr),
m_pLocalMgr(nullptr),
m_pScriptDataWindow(nullptr),
m_pScriptEvent(nullptr),
m_pScriptHost(nullptr),
m_pScriptLog(nullptr),
m_pScriptLayout(nullptr),
m_pScriptSignature(nullptr),
m_eCurVersionMode(XFA_VERSION_DEFAULT),
m_dwDocFlags(0) {
ASSERT(m_pParser);
}
CXFA_Document::~CXFA_Document() {
delete m_pRootNode;
PurgeNodes();
}
void CXFA_Document::ClearLayoutData() {
delete m_pLayoutProcessor;
m_pLayoutProcessor = nullptr;
delete m_pScriptContext;
m_pScriptContext = nullptr;
delete m_pLocalMgr;
m_pLocalMgr = nullptr;
delete m_pScriptDataWindow;
m_pScriptDataWindow = nullptr;
delete m_pScriptEvent;
m_pScriptEvent = nullptr;
delete m_pScriptHost;
m_pScriptHost = nullptr;
delete m_pScriptLog;
m_pScriptLog = nullptr;
delete m_pScriptLayout;
m_pScriptLayout = nullptr;
delete m_pScriptSignature;
m_pScriptSignature = nullptr;
}
void CXFA_Document::SetRoot(CXFA_Node* pNewRoot) {
if (m_pRootNode)
AddPurgeNode(m_pRootNode);
m_pRootNode = pNewRoot;
RemovePurgeNode(pNewRoot);
}
CFDE_XMLDoc* CXFA_Document::GetXMLDoc() const {
return m_pParser->GetXMLDoc();
}
CXFA_FFNotify* CXFA_Document::GetNotify() const {
return m_pParser->GetNotify();
}
CXFA_Object* CXFA_Document::GetXFAObject(XFA_HashCode dwNodeNameHash) {
switch (dwNodeNameHash) {
case XFA_HASHCODE_Data: {
CXFA_Node* pDatasetsNode = ToNode(GetXFAObject(XFA_HASHCODE_Datasets));
if (!pDatasetsNode)
return nullptr;
for (CXFA_Node* pDatasetsChild =
pDatasetsNode->GetFirstChildByClass(XFA_Element::DataGroup);
pDatasetsChild;
pDatasetsChild = pDatasetsChild->GetNextSameClassSibling(
XFA_Element::DataGroup)) {
if (pDatasetsChild->GetNameHash() != XFA_HASHCODE_Data)
continue;
CFX_WideString wsNamespaceURI;
if (!pDatasetsChild->TryNamespace(wsNamespaceURI))
continue;
CFX_WideString wsDatasetsURI;
if (!pDatasetsNode->TryNamespace(wsDatasetsURI))
continue;
if (wsNamespaceURI == wsDatasetsURI)
return pDatasetsChild;
}
return nullptr;
}
case XFA_HASHCODE_Record: {
CXFA_Node* pData = ToNode(GetXFAObject(XFA_HASHCODE_Data));
return pData ? pData->GetFirstChildByClass(XFA_Element::DataGroup)
: nullptr;
}
case XFA_HASHCODE_DataWindow: {
if (!m_pScriptDataWindow)
m_pScriptDataWindow = new CScript_DataWindow(this);
return m_pScriptDataWindow;
}
case XFA_HASHCODE_Event: {
if (!m_pScriptEvent)
m_pScriptEvent = new CScript_EventPseudoModel(this);
return m_pScriptEvent;
}
case XFA_HASHCODE_Host: {
if (!m_pScriptHost)
m_pScriptHost = new CScript_HostPseudoModel(this);
return m_pScriptHost;
}
case XFA_HASHCODE_Log: {
if (!m_pScriptLog)
m_pScriptLog = new CScript_LogPseudoModel(this);
return m_pScriptLog;
}
case XFA_HASHCODE_Signature: {
if (!m_pScriptSignature)
m_pScriptSignature = new CScript_SignaturePseudoModel(this);
return m_pScriptSignature;
}
case XFA_HASHCODE_Layout: {
if (!m_pScriptLayout)
m_pScriptLayout = new CScript_LayoutPseudoModel(this);
return m_pScriptLayout;
}
default:
return m_pRootNode->GetFirstChildByName(dwNodeNameHash);
}
}
CXFA_Node* CXFA_Document::CreateNode(uint32_t dwPacket, XFA_Element eElement) {
return CreateNode(XFA_GetPacketByID(dwPacket), eElement);
}
CXFA_Node* CXFA_Document::CreateNode(const XFA_PACKETINFO* pPacket,
XFA_Element eElement) {
if (!pPacket)
return nullptr;
const XFA_ELEMENTINFO* pElement = XFA_GetElementByID(eElement);
if (pElement && (pElement->dwPackets & pPacket->eName)) {
CXFA_Node* pNode =
new CXFA_Node(this, pPacket->eName, pElement->eObjectType,
pElement->eName, pElement->pName);
AddPurgeNode(pNode);
return pNode;
}
return nullptr;
}
void CXFA_Document::AddPurgeNode(CXFA_Node* pNode) {
m_PurgeNodes.insert(pNode);
}
FX_BOOL CXFA_Document::RemovePurgeNode(CXFA_Node* pNode) {
return !!m_PurgeNodes.erase(pNode);
}
void CXFA_Document::PurgeNodes() {
for (CXFA_Node* pNode : m_PurgeNodes)
delete pNode;
m_PurgeNodes.clear();
}
void CXFA_Document::SetFlag(uint32_t dwFlag, FX_BOOL bOn) {
if (bOn)
m_dwDocFlags |= dwFlag;
else
m_dwDocFlags &= ~dwFlag;
}
FX_BOOL CXFA_Document::IsInteractive() {
if (m_dwDocFlags & XFA_DOCFLAG_HasInteractive)
return !!(m_dwDocFlags & XFA_DOCFLAG_Interactive);
CXFA_Node* pConfig = ToNode(GetXFAObject(XFA_HASHCODE_Config));
if (!pConfig)
return FALSE;
CFX_WideString wsInteractive;
CXFA_Node* pPresent = pConfig->GetFirstChildByClass(XFA_Element::Present);
if (!pPresent)
return FALSE;
CXFA_Node* pPDF = pPresent->GetFirstChildByClass(XFA_Element::Pdf);
if (!pPDF)
return FALSE;
CXFA_Node* pInteractive = pPDF->GetChild(0, XFA_Element::Interactive);
if (pInteractive) {
m_dwDocFlags |= XFA_DOCFLAG_HasInteractive;
if (pInteractive->TryContent(wsInteractive) &&
wsInteractive == FX_WSTRC(L"1")) {
m_dwDocFlags |= XFA_DOCFLAG_Interactive;
return TRUE;
}
}
return FALSE;
}
CXFA_LocaleMgr* CXFA_Document::GetLocalMgr() {
if (!m_pLocalMgr) {
CFX_WideString wsLanguage;
GetNotify()->GetAppProvider()->GetLanguage(wsLanguage);
m_pLocalMgr = new CXFA_LocaleMgr(
ToNode(GetXFAObject(XFA_HASHCODE_LocaleSet)), wsLanguage);
}
return m_pLocalMgr;
}
CXFA_ScriptContext* CXFA_Document::InitScriptContext(v8::Isolate* pIsolate) {
if (!m_pScriptContext)
m_pScriptContext = new CXFA_ScriptContext(this);
m_pScriptContext->Initialize(pIsolate);
return m_pScriptContext;
}
CXFA_ScriptContext* CXFA_Document::GetScriptContext() {
if (!m_pScriptContext)
m_pScriptContext = new CXFA_ScriptContext(this);
return m_pScriptContext;
}
XFA_VERSION CXFA_Document::RecognizeXFAVersionNumber(
CFX_WideString& wsTemplateNS) {
CFX_WideStringC wsTemplateURIPrefix =
XFA_GetPacketByIndex(XFA_PACKET_Template)->pURI;
FX_STRSIZE nPrefixLength = wsTemplateURIPrefix.GetLength();
if (CFX_WideStringC(wsTemplateNS.c_str(), wsTemplateNS.GetLength()) !=
wsTemplateURIPrefix) {
return XFA_VERSION_UNKNOWN;
}
FX_STRSIZE nDotPos = wsTemplateNS.Find('.', nPrefixLength);
if (nDotPos == (FX_STRSIZE)-1)
return XFA_VERSION_UNKNOWN;
int8_t iMajor = FXSYS_wtoi(
wsTemplateNS.Mid(nPrefixLength, nDotPos - nPrefixLength).c_str());
int8_t iMinor = FXSYS_wtoi(
wsTemplateNS.Mid(nDotPos + 1, wsTemplateNS.GetLength() - nDotPos - 2)
.c_str());
XFA_VERSION eVersion = (XFA_VERSION)((int32_t)iMajor * 100 + iMinor);
if (eVersion < XFA_VERSION_MIN || eVersion > XFA_VERSION_MAX)
return XFA_VERSION_UNKNOWN;
m_eCurVersionMode = eVersion;
return eVersion;
}
CXFA_Node* CXFA_Document::GetNodeByID(CXFA_Node* pRoot,
const CFX_WideStringC& wsID) {
if (!pRoot || wsID.IsEmpty())
return nullptr;
CXFA_NodeIterator sIterator(pRoot);
for (CXFA_Node* pNode = sIterator.GetCurrent(); pNode;
pNode = sIterator.MoveToNext()) {
CFX_WideStringC wsIDVal;
if (pNode->TryCData(XFA_ATTRIBUTE_Id, wsIDVal) && !wsIDVal.IsEmpty()) {
if (wsIDVal == wsID)
return pNode;
}
}
return nullptr;
}
void CXFA_Document::DoProtoMerge() {
CXFA_Node* pTemplateRoot = ToNode(GetXFAObject(XFA_HASHCODE_Template));
if (!pTemplateRoot)
return;
CFX_MapPtrTemplate<uint32_t, CXFA_Node*> mIDMap;
CXFA_NodeSet sUseNodes;
CXFA_NodeIterator sIterator(pTemplateRoot);
for (CXFA_Node* pNode = sIterator.GetCurrent(); pNode;
pNode = sIterator.MoveToNext()) {
CFX_WideStringC wsIDVal;
if (pNode->TryCData(XFA_ATTRIBUTE_Id, wsIDVal) && !wsIDVal.IsEmpty()) {
mIDMap[FX_HashCode_GetW(wsIDVal, false)] = pNode;
}
CFX_WideStringC wsUseVal;
if (pNode->TryCData(XFA_ATTRIBUTE_Use, wsUseVal) && !wsUseVal.IsEmpty()) {
sUseNodes.insert(pNode);
} else if (pNode->TryCData(XFA_ATTRIBUTE_Usehref, wsUseVal) &&
!wsUseVal.IsEmpty()) {
sUseNodes.insert(pNode);
}
}
for (CXFA_Node* pUseHrefNode : sUseNodes) {
CFX_WideString wsUseVal;
CFX_WideStringC wsURI, wsID, wsSOM;
if (pUseHrefNode->TryCData(XFA_ATTRIBUTE_Usehref, wsUseVal) &&
!wsUseVal.IsEmpty()) {
FX_STRSIZE uSharpPos = wsUseVal.Find('#');
if (uSharpPos < 0) {
wsURI = wsUseVal.AsStringC();
} else {
wsURI = CFX_WideStringC(wsUseVal.c_str(), uSharpPos);
FX_STRSIZE uLen = wsUseVal.GetLength();
if (uLen >= uSharpPos + 5 &&
CFX_WideStringC(wsUseVal.c_str() + uSharpPos, 5) ==
FX_WSTRC(L"#som(") &&
wsUseVal[uLen - 1] == ')') {
wsSOM = CFX_WideStringC(wsUseVal.c_str() + uSharpPos + 5,
uLen - 1 - uSharpPos - 5);
} else {
wsID = CFX_WideStringC(wsUseVal.c_str() + uSharpPos + 1,
uLen - uSharpPos - 1);
}
}
} else if (pUseHrefNode->TryCData(XFA_ATTRIBUTE_Use, wsUseVal) &&
!wsUseVal.IsEmpty()) {
if (wsUseVal[0] == '#')
wsID = CFX_WideStringC(wsUseVal.c_str() + 1, wsUseVal.GetLength() - 1);
else
wsSOM = CFX_WideStringC(wsUseVal.c_str(), wsUseVal.GetLength());
}
if (!wsURI.IsEmpty() && wsURI != FX_WSTRC(L"."))
continue;
CXFA_Node* pProtoNode = nullptr;
if (!wsSOM.IsEmpty()) {
uint32_t dwFlag = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Attributes |
XFA_RESOLVENODE_Properties | XFA_RESOLVENODE_Parent |
XFA_RESOLVENODE_Siblings;
XFA_RESOLVENODE_RS resoveNodeRS;
int32_t iRet = m_pScriptContext->ResolveObjects(pUseHrefNode, wsSOM,
resoveNodeRS, dwFlag);
if (iRet > 0 && resoveNodeRS.nodes[0]->IsNode()) {
pProtoNode = resoveNodeRS.nodes[0]->AsNode();
}
} else if (!wsID.IsEmpty()) {
if (!mIDMap.Lookup(FX_HashCode_GetW(wsID, false), pProtoNode)) {
continue;
}
}
if (!pProtoNode)
continue;
MergeNode(this, pUseHrefNode, pProtoNode);
}
}