You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1831 lines
68 KiB
1831 lines
68 KiB
// 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 "xfa/fxfa/parser/cxfa_document.h"
|
|
|
|
#include <set>
|
|
#include <utility>
|
|
|
|
#include "core/fxcrt/fx_extension.h"
|
|
#include "core/fxcrt/xml/cfx_xmldocument.h"
|
|
#include "fxjs/xfa/cfxjse_engine.h"
|
|
#include "fxjs/xfa/cjx_object.h"
|
|
#include "third_party/base/compiler_specific.h"
|
|
#include "third_party/base/ptr_util.h"
|
|
#include "third_party/base/stl_util.h"
|
|
#include "xfa/fxfa/cxfa_ffdoc.h"
|
|
#include "xfa/fxfa/cxfa_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_bind.h"
|
|
#include "xfa/fxfa/parser/cxfa_datagroup.h"
|
|
#include "xfa/fxfa/parser/cxfa_exdata.h"
|
|
#include "xfa/fxfa/parser/cxfa_form.h"
|
|
#include "xfa/fxfa/parser/cxfa_image.h"
|
|
#include "xfa/fxfa/parser/cxfa_interactive.h"
|
|
#include "xfa/fxfa/parser/cxfa_items.h"
|
|
#include "xfa/fxfa/parser/cxfa_localemgr.h"
|
|
#include "xfa/fxfa/parser/cxfa_node.h"
|
|
#include "xfa/fxfa/parser/cxfa_occur.h"
|
|
#include "xfa/fxfa/parser/cxfa_pageset.h"
|
|
#include "xfa/fxfa/parser/cxfa_pdf.h"
|
|
#include "xfa/fxfa/parser/cxfa_present.h"
|
|
#include "xfa/fxfa/parser/cxfa_subform.h"
|
|
#include "xfa/fxfa/parser/cxfa_template.h"
|
|
#include "xfa/fxfa/parser/cxfa_traversestrategy_xfacontainernode.h"
|
|
#include "xfa/fxfa/parser/cxfa_traversestrategy_xfanode.h"
|
|
#include "xfa/fxfa/parser/cxfa_value.h"
|
|
#include "xfa/fxfa/parser/xfa_document_datamerger_imp.h"
|
|
#include "xfa/fxfa/parser/xfa_resolvenode_rs.h"
|
|
#include "xfa/fxfa/parser/xfa_utils.h"
|
|
|
|
namespace {
|
|
|
|
const wchar_t kTemplateNS[] = L"http://www.xfa.org/schema/xfa-template/";
|
|
|
|
struct RecurseRecord {
|
|
CXFA_Node* pTemplateChild;
|
|
CXFA_Node* pDataChild;
|
|
};
|
|
|
|
class CXFA_TraverseStrategy_DDGroup {
|
|
public:
|
|
static CXFA_Node* GetFirstChild(CXFA_Node* pDDGroupNode) {
|
|
return pDDGroupNode->GetFirstChildByName(XFA_HASHCODE_Group);
|
|
}
|
|
static CXFA_Node* GetNextSibling(CXFA_Node* pDDGroupNode) {
|
|
return pDDGroupNode->GetNextSameNameSibling(XFA_HASHCODE_Group);
|
|
}
|
|
static CXFA_Node* GetParent(CXFA_Node* pDDGroupNode) {
|
|
return pDDGroupNode->GetParent();
|
|
}
|
|
};
|
|
|
|
void FormValueNode_MatchNoneCreateChild(CXFA_Node* pFormNode) {
|
|
ASSERT(pFormNode->IsWidgetReady());
|
|
// GetUIChildNode has the side effect of creating the UI child.
|
|
pFormNode->GetUIChildNode();
|
|
}
|
|
|
|
CXFA_Node* FormValueNode_CreateChild(CXFA_Node* pValueNode, XFA_Element iType) {
|
|
CXFA_Node* pChildNode = pValueNode->GetFirstChild();
|
|
if (!pChildNode) {
|
|
if (iType == XFA_Element::Unknown)
|
|
return nullptr;
|
|
|
|
pChildNode =
|
|
pValueNode->JSObject()->GetOrCreateProperty<CXFA_Node>(0, iType);
|
|
}
|
|
return pChildNode;
|
|
}
|
|
|
|
void FormValueNode_SetChildContent(CXFA_Node* pValueNode,
|
|
const WideString& wsContent,
|
|
XFA_Element iType) {
|
|
if (!pValueNode)
|
|
return;
|
|
|
|
ASSERT(pValueNode->GetPacketType() == XFA_PacketType::Form);
|
|
CXFA_Node* pChildNode = FormValueNode_CreateChild(pValueNode, iType);
|
|
if (!pChildNode)
|
|
return;
|
|
|
|
switch (pChildNode->GetObjectType()) {
|
|
case XFA_ObjectType::ContentNode: {
|
|
CXFA_Node* pContentRawDataNode = pChildNode->GetFirstChild();
|
|
if (!pContentRawDataNode) {
|
|
XFA_Element element = XFA_Element::Sharptext;
|
|
if (pChildNode->GetElementType() == XFA_Element::ExData) {
|
|
Optional<WideString> contentType =
|
|
pChildNode->JSObject()->TryAttribute(XFA_Attribute::ContentType,
|
|
false);
|
|
if (contentType.has_value()) {
|
|
if (contentType.value().EqualsASCII("text/html"))
|
|
element = XFA_Element::SharpxHTML;
|
|
else if (contentType.value().EqualsASCII("text/xml"))
|
|
element = XFA_Element::Sharpxml;
|
|
}
|
|
}
|
|
pContentRawDataNode = pChildNode->CreateSamePacketNode(element);
|
|
pChildNode->InsertChildAndNotify(pContentRawDataNode, nullptr);
|
|
}
|
|
pContentRawDataNode->JSObject()->SetCData(XFA_Attribute::Value, wsContent,
|
|
false, false);
|
|
break;
|
|
}
|
|
case XFA_ObjectType::NodeC:
|
|
case XFA_ObjectType::TextNode:
|
|
case XFA_ObjectType::NodeV: {
|
|
pChildNode->JSObject()->SetCData(XFA_Attribute::Value, wsContent, false,
|
|
false);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void MergeNodeRecurse(CXFA_Node* pDestNodeParent, CXFA_Node* pProtoNode) {
|
|
CXFA_Node* pExistingNode = nullptr;
|
|
for (CXFA_Node* pFormChild = pDestNodeParent->GetFirstChild(); pFormChild;
|
|
pFormChild = pFormChild->GetNextSibling()) {
|
|
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->GetFirstChild();
|
|
pTemplateChild; pTemplateChild = pTemplateChild->GetNextSibling()) {
|
|
MergeNodeRecurse(pExistingNode, pTemplateChild);
|
|
}
|
|
return;
|
|
}
|
|
CXFA_Node* pNewNode = pProtoNode->Clone(true);
|
|
pNewNode->SetTemplateNode(pProtoNode);
|
|
pDestNodeParent->InsertChildAndNotify(pNewNode, nullptr);
|
|
}
|
|
|
|
void MergeNode(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);
|
|
}
|
|
}
|
|
pDestNode->SetTemplateNode(pProtoNode);
|
|
for (CXFA_Node* pTemplateChild = pProtoNode->GetFirstChild(); pTemplateChild;
|
|
pTemplateChild = pTemplateChild->GetNextSibling()) {
|
|
MergeNodeRecurse(pDestNode, pTemplateChild);
|
|
}
|
|
{
|
|
CXFA_NodeIterator sIterator(pDestNode);
|
|
for (CXFA_Node* pNode = sIterator.GetCurrent(); pNode;
|
|
pNode = sIterator.MoveToNext()) {
|
|
pNode->ClearFlag(XFA_NodeFlag_UnusedNode);
|
|
}
|
|
}
|
|
}
|
|
|
|
CXFA_Node* CloneOrMergeInstanceManager(CXFA_Document* pDocument,
|
|
CXFA_Node* pFormParent,
|
|
CXFA_Node* pTemplateNode,
|
|
std::vector<CXFA_Node*>* subforms) {
|
|
WideString wsSubformName =
|
|
pTemplateNode->JSObject()->GetCData(XFA_Attribute::Name);
|
|
WideString wsInstMgrNodeName = L"_" + wsSubformName;
|
|
uint32_t dwInstNameHash =
|
|
FX_HashCode_GetW(wsInstMgrNodeName.AsStringView(), false);
|
|
CXFA_Node* pExistingNode = XFA_DataMerge_FindFormDOMInstance(
|
|
pDocument, XFA_Element::InstanceManager, dwInstNameHash, pFormParent);
|
|
if (pExistingNode) {
|
|
uint32_t dwNameHash = pTemplateNode->GetNameHash();
|
|
for (CXFA_Node* pNode = pExistingNode->GetNextSibling(); pNode;) {
|
|
XFA_Element eCurType = pNode->GetElementType();
|
|
if (eCurType == XFA_Element::InstanceManager)
|
|
break;
|
|
|
|
if ((eCurType != XFA_Element::Subform) &&
|
|
(eCurType != XFA_Element::SubformSet)) {
|
|
pNode = pNode->GetNextSibling();
|
|
continue;
|
|
}
|
|
if (dwNameHash != pNode->GetNameHash())
|
|
break;
|
|
|
|
CXFA_Node* pNextNode = pNode->GetNextSibling();
|
|
pFormParent->RemoveChildAndNotify(pNode, true);
|
|
subforms->push_back(pNode);
|
|
pNode = pNextNode;
|
|
}
|
|
pFormParent->RemoveChildAndNotify(pExistingNode, true);
|
|
pFormParent->InsertChildAndNotify(pExistingNode, nullptr);
|
|
pExistingNode->ClearFlag(XFA_NodeFlag_UnusedNode);
|
|
pExistingNode->SetTemplateNode(pTemplateNode);
|
|
return pExistingNode;
|
|
}
|
|
|
|
CXFA_Node* pNewNode =
|
|
pDocument->CreateNode(XFA_PacketType::Form, XFA_Element::InstanceManager);
|
|
wsInstMgrNodeName =
|
|
L"_" + pTemplateNode->JSObject()->GetCData(XFA_Attribute::Name);
|
|
pNewNode->JSObject()->SetCData(XFA_Attribute::Name, wsInstMgrNodeName, false,
|
|
false);
|
|
pFormParent->InsertChildAndNotify(pNewNode, nullptr);
|
|
pNewNode->SetTemplateNode(pTemplateNode);
|
|
return pNewNode;
|
|
}
|
|
|
|
void SortRecurseRecord(std::vector<RecurseRecord>* rgRecords,
|
|
CXFA_Node* pDataScope,
|
|
bool bChoiceMode) {
|
|
std::vector<RecurseRecord> rgResultRecord;
|
|
for (CXFA_Node* pNode = pDataScope->GetFirstChild(); pNode;
|
|
pNode = pNode->GetNextSibling()) {
|
|
auto it = std::find_if(rgRecords->begin(), rgRecords->end(),
|
|
[pNode](const RecurseRecord& record) {
|
|
return pNode == record.pDataChild;
|
|
});
|
|
if (it != rgRecords->end()) {
|
|
rgResultRecord.push_back(*it);
|
|
rgRecords->erase(it);
|
|
if (bChoiceMode)
|
|
break;
|
|
}
|
|
}
|
|
if (rgResultRecord.empty())
|
|
return;
|
|
|
|
if (!bChoiceMode) {
|
|
rgResultRecord.insert(rgResultRecord.end(), rgRecords->begin(),
|
|
rgRecords->end());
|
|
}
|
|
*rgRecords = rgResultRecord;
|
|
}
|
|
|
|
CXFA_Node* ScopeMatchGlobalBinding(CXFA_Node* pDataScope,
|
|
uint32_t dwNameHash,
|
|
XFA_Element eMatchDataNodeType,
|
|
bool bUpLevel) {
|
|
for (CXFA_Node *pCurDataScope = pDataScope, *pLastDataScope = nullptr;
|
|
pCurDataScope &&
|
|
pCurDataScope->GetPacketType() == XFA_PacketType::Datasets;
|
|
pLastDataScope = pCurDataScope,
|
|
pCurDataScope = pCurDataScope->GetParent()) {
|
|
for (CXFA_Node* pDataChild = pCurDataScope->GetFirstChildByName(dwNameHash);
|
|
pDataChild;
|
|
pDataChild = pDataChild->GetNextSameNameSibling(dwNameHash)) {
|
|
if (pDataChild == pLastDataScope ||
|
|
(eMatchDataNodeType != XFA_Element::DataModel &&
|
|
pDataChild->GetElementType() != eMatchDataNodeType) ||
|
|
pDataChild->HasBindItem()) {
|
|
continue;
|
|
}
|
|
return pDataChild;
|
|
}
|
|
|
|
for (CXFA_DataGroup* pDataChild =
|
|
pCurDataScope->GetFirstChildByClass<CXFA_DataGroup>(
|
|
XFA_Element::DataGroup);
|
|
pDataChild;
|
|
pDataChild = pDataChild->GetNextSameClassSibling<CXFA_DataGroup>(
|
|
XFA_Element::DataGroup)) {
|
|
CXFA_Node* pDataNode = ScopeMatchGlobalBinding(pDataChild, dwNameHash,
|
|
eMatchDataNodeType, false);
|
|
if (pDataNode)
|
|
return pDataNode;
|
|
}
|
|
if (!bUpLevel)
|
|
break;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
CXFA_Node* FindGlobalDataNode(CXFA_Document* pDocument,
|
|
const WideString& wsName,
|
|
CXFA_Node* pDataScope,
|
|
XFA_Element eMatchNodeType) {
|
|
if (wsName.IsEmpty())
|
|
return nullptr;
|
|
|
|
uint32_t dwNameHash = FX_HashCode_GetW(wsName.AsStringView(), false);
|
|
CXFA_Node* pBounded = pDocument->GetGlobalBinding(dwNameHash);
|
|
if (!pBounded) {
|
|
pBounded =
|
|
ScopeMatchGlobalBinding(pDataScope, dwNameHash, eMatchNodeType, true);
|
|
if (pBounded)
|
|
pDocument->RegisterGlobalBinding(dwNameHash, pBounded);
|
|
}
|
|
return pBounded;
|
|
}
|
|
|
|
CXFA_Node* FindOnceDataNode(const WideString& wsName,
|
|
CXFA_Node* pDataScope,
|
|
XFA_Element eMatchNodeType) {
|
|
if (wsName.IsEmpty())
|
|
return nullptr;
|
|
|
|
uint32_t dwNameHash = FX_HashCode_GetW(wsName.AsStringView(), false);
|
|
CXFA_Node* pLastDataScope = nullptr;
|
|
for (CXFA_Node* pCurDataScope = pDataScope;
|
|
pCurDataScope &&
|
|
pCurDataScope->GetPacketType() == XFA_PacketType::Datasets;
|
|
pCurDataScope = pCurDataScope->GetParent()) {
|
|
for (CXFA_Node* pDataChild = pCurDataScope->GetFirstChildByName(dwNameHash);
|
|
pDataChild;
|
|
pDataChild = pDataChild->GetNextSameNameSibling(dwNameHash)) {
|
|
if (pDataChild == pLastDataScope || pDataChild->HasBindItem() ||
|
|
(eMatchNodeType != XFA_Element::DataModel &&
|
|
pDataChild->GetElementType() != eMatchNodeType)) {
|
|
continue;
|
|
}
|
|
return pDataChild;
|
|
}
|
|
pLastDataScope = pCurDataScope;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
CXFA_Node* FindDataRefDataNode(CXFA_Document* pDocument,
|
|
const WideString& wsRef,
|
|
CXFA_Node* pDataScope,
|
|
XFA_Element eMatchNodeType,
|
|
CXFA_Node* pTemplateNode,
|
|
bool bForceBind,
|
|
bool bUpLevel) {
|
|
uint32_t dFlags = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_BindNew;
|
|
if (bUpLevel || !wsRef.EqualsASCII("name"))
|
|
dFlags |= (XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings);
|
|
|
|
XFA_RESOLVENODE_RS rs;
|
|
pDocument->GetScriptContext()->ResolveObjects(
|
|
pDataScope, wsRef.AsStringView(), &rs, dFlags, pTemplateNode);
|
|
if (rs.dwFlags == XFA_ResolveNode_RSType_CreateNodeAll ||
|
|
rs.dwFlags == XFA_ResolveNode_RSType_CreateNodeMidAll ||
|
|
rs.objects.size() > 1) {
|
|
return pDocument->GetNotBindNode(rs.objects);
|
|
}
|
|
|
|
if (rs.dwFlags == XFA_ResolveNode_RSType_CreateNodeOne) {
|
|
CXFA_Object* pObject =
|
|
!rs.objects.empty() ? rs.objects.front().Get() : nullptr;
|
|
CXFA_Node* pNode = ToNode(pObject);
|
|
return (bForceBind || !pNode || !pNode->HasBindItem()) ? pNode : nullptr;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
CXFA_Node* FindMatchingDataNode(
|
|
CXFA_Document* pDocument,
|
|
CXFA_Node* pTemplateNode,
|
|
CXFA_Node* pDataScope,
|
|
bool& bAccessedDataDOM,
|
|
bool bForceBind,
|
|
CXFA_NodeIteratorTemplate<CXFA_Node,
|
|
CXFA_TraverseStrategy_XFAContainerNode>*
|
|
pIterator,
|
|
bool& bSelfMatch,
|
|
XFA_AttributeValue& eBindMatch,
|
|
bool bUpLevel) {
|
|
CXFA_Node* pResult = nullptr;
|
|
CXFA_Node* pCurTemplateNode = pIterator->GetCurrent();
|
|
while (pCurTemplateNode) {
|
|
XFA_Element eMatchNodeType;
|
|
switch (pCurTemplateNode->GetElementType()) {
|
|
case XFA_Element::Subform:
|
|
eMatchNodeType = XFA_Element::DataGroup;
|
|
break;
|
|
case XFA_Element::Field: {
|
|
eMatchNodeType = XFA_FieldIsMultiListBox(pCurTemplateNode)
|
|
? XFA_Element::DataGroup
|
|
: XFA_Element::DataValue;
|
|
} break;
|
|
case XFA_Element::ExclGroup:
|
|
eMatchNodeType = XFA_Element::DataValue;
|
|
break;
|
|
default:
|
|
pCurTemplateNode = pIterator->MoveToNext();
|
|
continue;
|
|
}
|
|
|
|
CXFA_Occur* pTemplateNodeOccur =
|
|
pCurTemplateNode->GetFirstChildByClass<CXFA_Occur>(XFA_Element::Occur);
|
|
if (pTemplateNodeOccur) {
|
|
int32_t iMin;
|
|
int32_t iMax;
|
|
int32_t iInit;
|
|
std::tie(iMin, iMax, iInit) = pTemplateNodeOccur->GetOccurInfo();
|
|
if (iMax == 0) {
|
|
pCurTemplateNode = pIterator->MoveToNext();
|
|
continue;
|
|
}
|
|
}
|
|
|
|
CXFA_Bind* pTemplateNodeBind =
|
|
pCurTemplateNode->GetFirstChildByClass<CXFA_Bind>(XFA_Element::Bind);
|
|
XFA_AttributeValue eMatch =
|
|
pTemplateNodeBind
|
|
? pTemplateNodeBind->JSObject()->GetEnum(XFA_Attribute::Match)
|
|
: XFA_AttributeValue::Once;
|
|
eBindMatch = eMatch;
|
|
switch (eMatch) {
|
|
case XFA_AttributeValue::None:
|
|
pCurTemplateNode = pIterator->MoveToNext();
|
|
continue;
|
|
case XFA_AttributeValue::Global:
|
|
bAccessedDataDOM = true;
|
|
if (!bForceBind) {
|
|
pCurTemplateNode = pIterator->MoveToNext();
|
|
continue;
|
|
}
|
|
if (eMatchNodeType == XFA_Element::DataValue ||
|
|
(eMatchNodeType == XFA_Element::DataGroup &&
|
|
XFA_FieldIsMultiListBox(pTemplateNodeBind))) {
|
|
CXFA_Node* pGlobalBindNode = FindGlobalDataNode(
|
|
pDocument,
|
|
pCurTemplateNode->JSObject()->GetCData(XFA_Attribute::Name),
|
|
pDataScope, eMatchNodeType);
|
|
if (!pGlobalBindNode) {
|
|
pCurTemplateNode = pIterator->MoveToNext();
|
|
continue;
|
|
}
|
|
pResult = pGlobalBindNode;
|
|
break;
|
|
}
|
|
FALLTHROUGH;
|
|
case XFA_AttributeValue::Once: {
|
|
bAccessedDataDOM = true;
|
|
CXFA_Node* pOnceBindNode = FindOnceDataNode(
|
|
pCurTemplateNode->JSObject()->GetCData(XFA_Attribute::Name),
|
|
pDataScope, eMatchNodeType);
|
|
if (!pOnceBindNode) {
|
|
pCurTemplateNode = pIterator->MoveToNext();
|
|
continue;
|
|
}
|
|
pResult = pOnceBindNode;
|
|
break;
|
|
}
|
|
case XFA_AttributeValue::DataRef: {
|
|
bAccessedDataDOM = true;
|
|
CXFA_Node* pDataRefBindNode = FindDataRefDataNode(
|
|
pDocument,
|
|
pTemplateNodeBind->JSObject()->GetCData(XFA_Attribute::Ref),
|
|
pDataScope, eMatchNodeType, pTemplateNode, bForceBind, bUpLevel);
|
|
if (pDataRefBindNode &&
|
|
pDataRefBindNode->GetElementType() == eMatchNodeType) {
|
|
pResult = pDataRefBindNode;
|
|
}
|
|
if (!pResult) {
|
|
pCurTemplateNode = pIterator->SkipChildrenAndMoveToNext();
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
if (pCurTemplateNode == pTemplateNode && pResult)
|
|
bSelfMatch = true;
|
|
break;
|
|
}
|
|
return pResult;
|
|
}
|
|
|
|
void CreateDataBinding(CXFA_Node* pFormNode,
|
|
CXFA_Node* pDataNode,
|
|
bool bDataToForm) {
|
|
pFormNode->SetBindingNode(pDataNode);
|
|
pDataNode->AddBindItem(pFormNode);
|
|
XFA_Element eType = pFormNode->GetElementType();
|
|
if (eType != XFA_Element::Field && eType != XFA_Element::ExclGroup)
|
|
return;
|
|
|
|
ASSERT(pFormNode->IsWidgetReady());
|
|
auto* defValue = pFormNode->JSObject()->GetOrCreateProperty<CXFA_Value>(
|
|
0, XFA_Element::Value);
|
|
if (!bDataToForm) {
|
|
WideString wsValue;
|
|
switch (pFormNode->GetFFWidgetType()) {
|
|
case XFA_FFWidgetType::kImageEdit: {
|
|
CXFA_Image* image = defValue ? defValue->GetImageIfExists() : nullptr;
|
|
WideString wsContentType;
|
|
WideString wsHref;
|
|
if (image) {
|
|
wsValue = image->GetContent();
|
|
wsContentType = image->GetContentType();
|
|
wsHref = image->GetHref();
|
|
}
|
|
CFX_XMLElement* pXMLDataElement =
|
|
ToXMLElement(pDataNode->GetXMLMappingNode());
|
|
ASSERT(pXMLDataElement);
|
|
pDataNode->JSObject()->SetAttributeValue(
|
|
wsValue, pFormNode->GetFormatDataValue(wsValue), false, false);
|
|
pDataNode->JSObject()->SetCData(XFA_Attribute::ContentType,
|
|
wsContentType, false, false);
|
|
if (!wsHref.IsEmpty())
|
|
pXMLDataElement->SetAttribute(L"href", wsHref);
|
|
|
|
break;
|
|
}
|
|
case XFA_FFWidgetType::kChoiceList:
|
|
wsValue = defValue ? defValue->GetChildValueContent() : WideString();
|
|
if (pFormNode->IsChoiceListMultiSelect()) {
|
|
std::vector<WideString> wsSelTextArray =
|
|
pFormNode->GetSelectedItemsValue();
|
|
if (!wsSelTextArray.empty()) {
|
|
for (const auto& text : wsSelTextArray) {
|
|
CXFA_Node* pValue =
|
|
pDataNode->CreateSamePacketNode(XFA_Element::DataValue);
|
|
pValue->JSObject()->SetCData(XFA_Attribute::Name, L"value", false,
|
|
false);
|
|
pValue->CreateXMLMappingNode();
|
|
pDataNode->InsertChildAndNotify(pValue, nullptr);
|
|
pValue->JSObject()->SetCData(XFA_Attribute::Value, text, false,
|
|
false);
|
|
}
|
|
} else {
|
|
CFX_XMLElement* pElement =
|
|
ToXMLElement(pDataNode->GetXMLMappingNode());
|
|
pElement->SetAttribute(L"xfa:dataNode", L"dataGroup");
|
|
}
|
|
} else if (!wsValue.IsEmpty()) {
|
|
pDataNode->JSObject()->SetAttributeValue(
|
|
wsValue, pFormNode->GetFormatDataValue(wsValue), false, false);
|
|
}
|
|
break;
|
|
case XFA_FFWidgetType::kCheckButton:
|
|
wsValue = defValue ? defValue->GetChildValueContent() : WideString();
|
|
if (wsValue.IsEmpty())
|
|
break;
|
|
|
|
pDataNode->JSObject()->SetAttributeValue(
|
|
wsValue, pFormNode->GetFormatDataValue(wsValue), false, false);
|
|
break;
|
|
case XFA_FFWidgetType::kExclGroup: {
|
|
CXFA_Node* pChecked = nullptr;
|
|
CXFA_Node* pChild = pFormNode->GetFirstChild();
|
|
for (; pChild; pChild = pChild->GetNextSibling()) {
|
|
if (pChild->GetElementType() != XFA_Element::Field)
|
|
continue;
|
|
|
|
auto* pValue =
|
|
pChild->GetChild<CXFA_Value>(0, XFA_Element::Value, false);
|
|
if (!pValue)
|
|
continue;
|
|
|
|
wsValue = pValue->GetChildValueContent();
|
|
if (wsValue.IsEmpty())
|
|
continue;
|
|
|
|
CXFA_Items* pItems =
|
|
pChild->GetChild<CXFA_Items>(0, XFA_Element::Items, false);
|
|
if (!pItems)
|
|
continue;
|
|
|
|
CXFA_Node* pText = pItems->GetFirstChild();
|
|
if (!pText)
|
|
continue;
|
|
|
|
WideString wsContent = pText->JSObject()->GetContent(false);
|
|
if (wsContent == wsValue) {
|
|
pChecked = pChild;
|
|
pDataNode->JSObject()->SetAttributeValue(wsValue, wsValue, false,
|
|
false);
|
|
pFormNode->JSObject()->SetCData(XFA_Attribute::Value, wsContent,
|
|
false, false);
|
|
break;
|
|
}
|
|
}
|
|
if (!pChecked)
|
|
break;
|
|
|
|
pChild = pFormNode->GetFirstChild();
|
|
for (; pChild; pChild = pChild->GetNextSibling()) {
|
|
if (pChild == pChecked)
|
|
continue;
|
|
if (pChild->GetElementType() != XFA_Element::Field)
|
|
continue;
|
|
|
|
CXFA_Value* pValue =
|
|
pChild->JSObject()->GetOrCreateProperty<CXFA_Value>(
|
|
0, XFA_Element::Value);
|
|
CXFA_Items* pItems =
|
|
pChild->GetChild<CXFA_Items>(0, XFA_Element::Items, false);
|
|
CXFA_Node* pText = pItems ? pItems->GetFirstChild() : nullptr;
|
|
if (pText)
|
|
pText = pText->GetNextSibling();
|
|
|
|
WideString wsContent;
|
|
if (pText)
|
|
wsContent = pText->JSObject()->GetContent(false);
|
|
|
|
FormValueNode_SetChildContent(pValue, wsContent, XFA_Element::Text);
|
|
}
|
|
break;
|
|
}
|
|
case XFA_FFWidgetType::kNumericEdit: {
|
|
wsValue = defValue ? defValue->GetChildValueContent() : WideString();
|
|
if (wsValue.IsEmpty())
|
|
break;
|
|
|
|
wsValue = pFormNode->NormalizeNumStr(wsValue);
|
|
pDataNode->JSObject()->SetAttributeValue(
|
|
wsValue, pFormNode->GetFormatDataValue(wsValue), false, false);
|
|
CXFA_Value* pValue =
|
|
pFormNode->JSObject()->GetOrCreateProperty<CXFA_Value>(
|
|
0, XFA_Element::Value);
|
|
FormValueNode_SetChildContent(pValue, wsValue, XFA_Element::Float);
|
|
break;
|
|
}
|
|
default:
|
|
wsValue = defValue ? defValue->GetChildValueContent() : WideString();
|
|
if (wsValue.IsEmpty())
|
|
break;
|
|
|
|
pDataNode->JSObject()->SetAttributeValue(
|
|
wsValue, pFormNode->GetFormatDataValue(wsValue), false, false);
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
WideString wsXMLValue = pDataNode->JSObject()->GetContent(false);
|
|
WideString wsNormalizeValue = pFormNode->GetNormalizeDataValue(wsXMLValue);
|
|
|
|
pDataNode->JSObject()->SetAttributeValue(wsNormalizeValue, wsXMLValue, false,
|
|
false);
|
|
switch (pFormNode->GetFFWidgetType()) {
|
|
case XFA_FFWidgetType::kImageEdit: {
|
|
FormValueNode_SetChildContent(defValue, wsNormalizeValue,
|
|
XFA_Element::Image);
|
|
CXFA_Image* image = defValue ? defValue->GetImageIfExists() : nullptr;
|
|
if (image) {
|
|
CFX_XMLElement* pXMLDataElement =
|
|
ToXMLElement(pDataNode->GetXMLMappingNode());
|
|
WideString wsContentType =
|
|
pXMLDataElement->GetAttribute(L"xfa:contentType");
|
|
if (!wsContentType.IsEmpty()) {
|
|
pDataNode->JSObject()->SetCData(XFA_Attribute::ContentType,
|
|
wsContentType, false, false);
|
|
image->SetContentType(wsContentType);
|
|
}
|
|
|
|
WideString wsHref = pXMLDataElement->GetAttribute(L"href");
|
|
if (!wsHref.IsEmpty())
|
|
image->SetHref(wsHref);
|
|
}
|
|
break;
|
|
}
|
|
case XFA_FFWidgetType::kChoiceList:
|
|
if (pFormNode->IsChoiceListMultiSelect()) {
|
|
std::vector<CXFA_Node*> items = pDataNode->GetNodeListWithFilter(
|
|
XFA_NODEFILTER_Children | XFA_NODEFILTER_Properties);
|
|
if (!items.empty()) {
|
|
bool single = items.size() == 1;
|
|
wsNormalizeValue.clear();
|
|
|
|
for (CXFA_Node* pNode : items) {
|
|
WideString wsItem = pNode->JSObject()->GetContent(false);
|
|
if (single)
|
|
wsItem += L"\n";
|
|
|
|
wsNormalizeValue += wsItem;
|
|
}
|
|
CXFA_ExData* exData =
|
|
defValue ? defValue->GetExDataIfExists() : nullptr;
|
|
if (exData)
|
|
exData->SetContentType(single ? L"text/plain" : L"text/xml");
|
|
}
|
|
FormValueNode_SetChildContent(defValue, wsNormalizeValue,
|
|
XFA_Element::ExData);
|
|
} else {
|
|
FormValueNode_SetChildContent(defValue, wsNormalizeValue,
|
|
XFA_Element::Text);
|
|
}
|
|
break;
|
|
case XFA_FFWidgetType::kExclGroup: {
|
|
pFormNode->SetSelectedMemberByValue(wsNormalizeValue.AsStringView(),
|
|
false, false, false);
|
|
break;
|
|
}
|
|
case XFA_FFWidgetType::kDateTimeEdit:
|
|
FormValueNode_SetChildContent(defValue, wsNormalizeValue,
|
|
XFA_Element::DateTime);
|
|
break;
|
|
case XFA_FFWidgetType::kNumericEdit: {
|
|
WideString wsPicture =
|
|
pFormNode->GetPictureContent(XFA_VALUEPICTURE_DataBind);
|
|
if (wsPicture.IsEmpty())
|
|
wsNormalizeValue = pFormNode->NormalizeNumStr(wsNormalizeValue);
|
|
|
|
FormValueNode_SetChildContent(defValue, wsNormalizeValue,
|
|
XFA_Element::Float);
|
|
break;
|
|
}
|
|
default:
|
|
FormValueNode_SetChildContent(defValue, wsNormalizeValue,
|
|
XFA_Element::Text);
|
|
break;
|
|
}
|
|
}
|
|
|
|
CXFA_Node* MaybeCreateDataNode(CXFA_Document* pDocument,
|
|
CXFA_Node* pDataParent,
|
|
XFA_Element eNodeType,
|
|
const WideString& wsName) {
|
|
if (!pDataParent)
|
|
return nullptr;
|
|
|
|
CXFA_Node* pParentDDNode = pDataParent->GetDataDescriptionNode();
|
|
if (!pParentDDNode) {
|
|
CXFA_Node* pDataNode =
|
|
pDocument->CreateNode(XFA_PacketType::Datasets, eNodeType);
|
|
pDataNode->JSObject()->SetCData(XFA_Attribute::Name, wsName, false, false);
|
|
pDataNode->CreateXMLMappingNode();
|
|
pDataParent->InsertChildAndNotify(pDataNode, nullptr);
|
|
pDataNode->SetFlag(XFA_NodeFlag_Initialized);
|
|
return pDataNode;
|
|
}
|
|
|
|
CXFA_NodeIteratorTemplate<CXFA_Node, CXFA_TraverseStrategy_DDGroup> sIterator(
|
|
pParentDDNode);
|
|
for (CXFA_Node* pDDGroupNode = sIterator.GetCurrent(); pDDGroupNode;
|
|
pDDGroupNode = sIterator.MoveToNext()) {
|
|
if (pDDGroupNode != pParentDDNode) {
|
|
if (pDDGroupNode->GetElementType() != XFA_Element::DataGroup)
|
|
continue;
|
|
|
|
Optional<WideString> ns = pDDGroupNode->JSObject()->TryNamespace();
|
|
if (!ns.has_value() ||
|
|
!ns.value().EqualsASCII("http://ns.adobe.com/data-description/")) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
CXFA_Node* pDDNode =
|
|
pDDGroupNode->GetFirstChildByName(wsName.AsStringView());
|
|
if (!pDDNode)
|
|
continue;
|
|
if (pDDNode->GetElementType() != eNodeType)
|
|
break;
|
|
|
|
CXFA_Node* pDataNode =
|
|
pDocument->CreateNode(XFA_PacketType::Datasets, eNodeType);
|
|
pDataNode->JSObject()->SetCData(XFA_Attribute::Name, wsName, false, false);
|
|
pDataNode->CreateXMLMappingNode();
|
|
if (eNodeType == XFA_Element::DataValue &&
|
|
pDDNode->JSObject()->GetEnum(XFA_Attribute::Contains) ==
|
|
XFA_AttributeValue::MetaData) {
|
|
pDataNode->JSObject()->SetEnum(XFA_Attribute::Contains,
|
|
XFA_AttributeValue::MetaData, false);
|
|
}
|
|
pDataParent->InsertChildAndNotify(pDataNode, nullptr);
|
|
pDataNode->SetDataDescriptionNode(pDDNode);
|
|
pDataNode->SetFlag(XFA_NodeFlag_Initialized);
|
|
return pDataNode;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
CXFA_Node* CopyContainer_Field(CXFA_Document* pDocument,
|
|
CXFA_Node* pTemplateNode,
|
|
CXFA_Node* pFormNode,
|
|
CXFA_Node* pDataScope,
|
|
bool bDataMerge,
|
|
bool bUpLevel) {
|
|
CXFA_Node* pFieldNode = XFA_NodeMerge_CloneOrMergeContainer(
|
|
pDocument, pFormNode, pTemplateNode, false, nullptr);
|
|
ASSERT(pFieldNode);
|
|
for (CXFA_Node* pTemplateChildNode = pTemplateNode->GetFirstChild();
|
|
pTemplateChildNode;
|
|
pTemplateChildNode = pTemplateChildNode->GetNextSibling()) {
|
|
if (XFA_DataMerge_NeedGenerateForm(pTemplateChildNode, true)) {
|
|
XFA_NodeMerge_CloneOrMergeContainer(pDocument, pFieldNode,
|
|
pTemplateChildNode, true, nullptr);
|
|
} else if (pTemplateNode->GetElementType() == XFA_Element::ExclGroup &&
|
|
pTemplateChildNode->IsContainerNode()) {
|
|
if (pTemplateChildNode->GetElementType() == XFA_Element::Field) {
|
|
CopyContainer_Field(pDocument, pTemplateChildNode, pFieldNode, nullptr,
|
|
false, true);
|
|
}
|
|
}
|
|
}
|
|
if (bDataMerge) {
|
|
bool bAccessedDataDOM = false;
|
|
bool bSelfMatch = false;
|
|
XFA_AttributeValue eBindMatch;
|
|
CXFA_NodeIteratorTemplate<CXFA_Node, CXFA_TraverseStrategy_XFAContainerNode>
|
|
sNodeIter(pTemplateNode);
|
|
CXFA_Node* pDataNode = FindMatchingDataNode(
|
|
pDocument, pTemplateNode, pDataScope, bAccessedDataDOM, true,
|
|
&sNodeIter, bSelfMatch, eBindMatch, bUpLevel);
|
|
if (pDataNode)
|
|
CreateDataBinding(pFieldNode, pDataNode, true);
|
|
} else {
|
|
FormValueNode_MatchNoneCreateChild(pFieldNode);
|
|
}
|
|
return pFieldNode;
|
|
}
|
|
|
|
CXFA_Node* CopyContainer_SubformSet(CXFA_Document* pDocument,
|
|
CXFA_Node* pTemplateNode,
|
|
CXFA_Node* pFormParentNode,
|
|
CXFA_Node* pDataScope,
|
|
bool bOneInstance,
|
|
bool bDataMerge) {
|
|
XFA_Element eType = pTemplateNode->GetElementType();
|
|
CXFA_Node* pOccurNode = nullptr;
|
|
CXFA_Node* pFirstInstance = nullptr;
|
|
bool bUseInstanceManager =
|
|
pFormParentNode->GetElementType() != XFA_Element::Area;
|
|
CXFA_Node* pInstMgrNode = nullptr;
|
|
std::vector<CXFA_Node*> subformArray;
|
|
std::vector<CXFA_Node*>* pSearchArray = nullptr;
|
|
if (!bOneInstance &&
|
|
(eType == XFA_Element::SubformSet || eType == XFA_Element::Subform)) {
|
|
pInstMgrNode = bUseInstanceManager ? CloneOrMergeInstanceManager(
|
|
pDocument, pFormParentNode,
|
|
pTemplateNode, &subformArray)
|
|
: nullptr;
|
|
if (CXFA_Occur* pOccurTemplateNode =
|
|
pTemplateNode->GetFirstChildByClass<CXFA_Occur>(
|
|
XFA_Element::Occur)) {
|
|
pOccurNode = pInstMgrNode ? XFA_NodeMerge_CloneOrMergeContainer(
|
|
pDocument, pInstMgrNode,
|
|
pOccurTemplateNode, false, nullptr)
|
|
: pOccurTemplateNode;
|
|
} else if (pInstMgrNode) {
|
|
pOccurNode =
|
|
pInstMgrNode->GetFirstChildByClass<CXFA_Occur>(XFA_Element::Occur);
|
|
if (pOccurNode)
|
|
pOccurNode->ClearFlag(XFA_NodeFlag_UnusedNode);
|
|
}
|
|
if (pInstMgrNode) {
|
|
pInstMgrNode->SetFlagAndNotify(XFA_NodeFlag_Initialized);
|
|
pSearchArray = &subformArray;
|
|
if (pFormParentNode->GetElementType() == XFA_Element::PageArea) {
|
|
bOneInstance = true;
|
|
if (subformArray.empty())
|
|
pSearchArray = nullptr;
|
|
} else if (pTemplateNode->GetNameHash() == 0 && subformArray.empty()) {
|
|
pSearchArray = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
int32_t iMax = 1;
|
|
int32_t iInit = 1;
|
|
int32_t iMin = 1;
|
|
if (!bOneInstance && pOccurNode) {
|
|
std::tie(iMin, iMax, iInit) =
|
|
static_cast<CXFA_Occur*>(pOccurNode)->GetOccurInfo();
|
|
}
|
|
|
|
XFA_AttributeValue eRelation =
|
|
eType == XFA_Element::SubformSet
|
|
? pTemplateNode->JSObject()->GetEnum(XFA_Attribute::Relation)
|
|
: XFA_AttributeValue::Ordered;
|
|
int32_t iCurRepeatIndex = 0;
|
|
XFA_AttributeValue eParentBindMatch = XFA_AttributeValue::None;
|
|
if (bDataMerge) {
|
|
CXFA_NodeIteratorTemplate<CXFA_Node, CXFA_TraverseStrategy_XFAContainerNode>
|
|
sNodeIterator(pTemplateNode);
|
|
bool bAccessedDataDOM = false;
|
|
if (eType == XFA_Element::SubformSet || eType == XFA_Element::Area) {
|
|
sNodeIterator.MoveToNext();
|
|
} else {
|
|
std::map<CXFA_Node*, CXFA_Node*> subformMapArray;
|
|
std::vector<CXFA_Node*> nodeArray;
|
|
for (; iMax < 0 || iCurRepeatIndex < iMax; iCurRepeatIndex++) {
|
|
bool bSelfMatch = false;
|
|
XFA_AttributeValue eBindMatch = XFA_AttributeValue::None;
|
|
CXFA_Node* pDataNode = FindMatchingDataNode(
|
|
pDocument, pTemplateNode, pDataScope, bAccessedDataDOM, false,
|
|
&sNodeIterator, bSelfMatch, eBindMatch, true);
|
|
if (!pDataNode || sNodeIterator.GetCurrent() != pTemplateNode)
|
|
break;
|
|
|
|
eParentBindMatch = eBindMatch;
|
|
CXFA_Node* pSubformNode = XFA_NodeMerge_CloneOrMergeContainer(
|
|
pDocument, pFormParentNode, pTemplateNode, false, pSearchArray);
|
|
if (!pFirstInstance)
|
|
pFirstInstance = pSubformNode;
|
|
|
|
CreateDataBinding(pSubformNode, pDataNode, true);
|
|
ASSERT(pSubformNode);
|
|
subformMapArray[pSubformNode] = pDataNode;
|
|
nodeArray.push_back(pSubformNode);
|
|
}
|
|
|
|
for (CXFA_Node* pSubform : nodeArray) {
|
|
CXFA_Node* pDataNode = nullptr;
|
|
auto it = subformMapArray.find(pSubform);
|
|
if (it != subformMapArray.end())
|
|
pDataNode = it->second;
|
|
for (CXFA_Node* pTemplateChild = pTemplateNode->GetFirstChild();
|
|
pTemplateChild;
|
|
pTemplateChild = pTemplateChild->GetNextSibling()) {
|
|
if (XFA_DataMerge_NeedGenerateForm(pTemplateChild,
|
|
bUseInstanceManager)) {
|
|
XFA_NodeMerge_CloneOrMergeContainer(pDocument, pSubform,
|
|
pTemplateChild, true, nullptr);
|
|
} else if (pTemplateChild->IsContainerNode()) {
|
|
pDocument->DataMerge_CopyContainer(pTemplateChild, pSubform,
|
|
pDataNode, false, true, false);
|
|
}
|
|
}
|
|
}
|
|
subformMapArray.clear();
|
|
}
|
|
|
|
for (; iMax < 0 || iCurRepeatIndex < iMax; iCurRepeatIndex++) {
|
|
bool bSelfMatch = false;
|
|
XFA_AttributeValue eBindMatch = XFA_AttributeValue::None;
|
|
if (!FindMatchingDataNode(pDocument, pTemplateNode, pDataScope,
|
|
bAccessedDataDOM, false, &sNodeIterator,
|
|
bSelfMatch, eBindMatch, true)) {
|
|
break;
|
|
}
|
|
if (eBindMatch == XFA_AttributeValue::DataRef &&
|
|
eParentBindMatch == XFA_AttributeValue::DataRef) {
|
|
break;
|
|
}
|
|
|
|
if (eRelation == XFA_AttributeValue::Choice ||
|
|
eRelation == XFA_AttributeValue::Unordered) {
|
|
CXFA_Node* pSubformSetNode = XFA_NodeMerge_CloneOrMergeContainer(
|
|
pDocument, pFormParentNode, pTemplateNode, false, pSearchArray);
|
|
ASSERT(pSubformSetNode);
|
|
if (!pFirstInstance)
|
|
pFirstInstance = pSubformSetNode;
|
|
|
|
std::vector<RecurseRecord> rgItemMatchList;
|
|
std::vector<CXFA_Node*> rgItemUnmatchList;
|
|
for (CXFA_Node* pTemplateChild = pTemplateNode->GetFirstChild();
|
|
pTemplateChild;
|
|
pTemplateChild = pTemplateChild->GetNextSibling()) {
|
|
if (XFA_DataMerge_NeedGenerateForm(pTemplateChild,
|
|
bUseInstanceManager)) {
|
|
XFA_NodeMerge_CloneOrMergeContainer(pDocument, pSubformSetNode,
|
|
pTemplateChild, true, nullptr);
|
|
} else if (pTemplateChild->IsContainerNode()) {
|
|
bSelfMatch = false;
|
|
eBindMatch = XFA_AttributeValue::None;
|
|
if (eRelation != XFA_AttributeValue::Ordered) {
|
|
CXFA_NodeIteratorTemplate<CXFA_Node,
|
|
CXFA_TraverseStrategy_XFAContainerNode>
|
|
sChildIter(pTemplateChild);
|
|
CXFA_Node* pDataMatch = FindMatchingDataNode(
|
|
pDocument, pTemplateChild, pDataScope, bAccessedDataDOM,
|
|
false, &sChildIter, bSelfMatch, eBindMatch, true);
|
|
if (pDataMatch) {
|
|
RecurseRecord sNewRecord = {pTemplateChild, pDataMatch};
|
|
if (bSelfMatch)
|
|
rgItemMatchList.insert(rgItemMatchList.begin(), sNewRecord);
|
|
else
|
|
rgItemMatchList.push_back(sNewRecord);
|
|
} else {
|
|
rgItemUnmatchList.push_back(pTemplateChild);
|
|
}
|
|
} else {
|
|
rgItemUnmatchList.push_back(pTemplateChild);
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (eRelation) {
|
|
case XFA_AttributeValue::Choice: {
|
|
ASSERT(!rgItemMatchList.empty());
|
|
SortRecurseRecord(&rgItemMatchList, pDataScope, true);
|
|
pDocument->DataMerge_CopyContainer(
|
|
rgItemMatchList.front().pTemplateChild, pSubformSetNode,
|
|
pDataScope, false, true, true);
|
|
break;
|
|
}
|
|
case XFA_AttributeValue::Unordered: {
|
|
if (!rgItemMatchList.empty()) {
|
|
SortRecurseRecord(&rgItemMatchList, pDataScope, false);
|
|
for (const auto& matched : rgItemMatchList) {
|
|
pDocument->DataMerge_CopyContainer(matched.pTemplateChild,
|
|
pSubformSetNode, pDataScope,
|
|
false, true, true);
|
|
}
|
|
}
|
|
for (auto* unmatched : rgItemUnmatchList) {
|
|
pDocument->DataMerge_CopyContainer(unmatched, pSubformSetNode,
|
|
pDataScope, false, true, true);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
} else {
|
|
CXFA_Node* pSubformSetNode = XFA_NodeMerge_CloneOrMergeContainer(
|
|
pDocument, pFormParentNode, pTemplateNode, false, pSearchArray);
|
|
ASSERT(pSubformSetNode);
|
|
if (!pFirstInstance)
|
|
pFirstInstance = pSubformSetNode;
|
|
|
|
for (CXFA_Node* pTemplateChild = pTemplateNode->GetFirstChild();
|
|
pTemplateChild;
|
|
pTemplateChild = pTemplateChild->GetNextSibling()) {
|
|
if (XFA_DataMerge_NeedGenerateForm(pTemplateChild,
|
|
bUseInstanceManager)) {
|
|
XFA_NodeMerge_CloneOrMergeContainer(pDocument, pSubformSetNode,
|
|
pTemplateChild, true, nullptr);
|
|
} else if (pTemplateChild->IsContainerNode()) {
|
|
pDocument->DataMerge_CopyContainer(pTemplateChild, pSubformSetNode,
|
|
pDataScope, false, true, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (iCurRepeatIndex == 0 && !bAccessedDataDOM) {
|
|
int32_t iLimit = iMax;
|
|
if (pInstMgrNode && pTemplateNode->GetNameHash() == 0) {
|
|
iLimit = pdfium::CollectionSize<int32_t>(subformArray);
|
|
if (iLimit < iMin)
|
|
iLimit = iInit;
|
|
}
|
|
|
|
for (; (iLimit < 0 || iCurRepeatIndex < iLimit); iCurRepeatIndex++) {
|
|
if (pInstMgrNode) {
|
|
if (pSearchArray && pSearchArray->empty()) {
|
|
if (pTemplateNode->GetNameHash() != 0)
|
|
break;
|
|
pSearchArray = nullptr;
|
|
}
|
|
} else if (!XFA_DataMerge_FindFormDOMInstance(
|
|
pDocument, pTemplateNode->GetElementType(),
|
|
pTemplateNode->GetNameHash(), pFormParentNode)) {
|
|
break;
|
|
}
|
|
CXFA_Node* pSubformNode = XFA_NodeMerge_CloneOrMergeContainer(
|
|
pDocument, pFormParentNode, pTemplateNode, false, pSearchArray);
|
|
ASSERT(pSubformNode);
|
|
if (!pFirstInstance)
|
|
pFirstInstance = pSubformNode;
|
|
|
|
for (CXFA_Node* pTemplateChild = pTemplateNode->GetFirstChild();
|
|
pTemplateChild;
|
|
pTemplateChild = pTemplateChild->GetNextSibling()) {
|
|
if (XFA_DataMerge_NeedGenerateForm(pTemplateChild,
|
|
bUseInstanceManager)) {
|
|
XFA_NodeMerge_CloneOrMergeContainer(pDocument, pSubformNode,
|
|
pTemplateChild, true, nullptr);
|
|
} else if (pTemplateChild->IsContainerNode()) {
|
|
pDocument->DataMerge_CopyContainer(pTemplateChild, pSubformNode,
|
|
pDataScope, false, true, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int32_t iMinimalLimit = iCurRepeatIndex == 0 ? iInit : iMin;
|
|
for (; iCurRepeatIndex < iMinimalLimit; iCurRepeatIndex++) {
|
|
CXFA_Node* pSubformSetNode = XFA_NodeMerge_CloneOrMergeContainer(
|
|
pDocument, pFormParentNode, pTemplateNode, false, pSearchArray);
|
|
ASSERT(pSubformSetNode);
|
|
if (!pFirstInstance)
|
|
pFirstInstance = pSubformSetNode;
|
|
|
|
bool bFound = false;
|
|
for (CXFA_Node* pTemplateChild = pTemplateNode->GetFirstChild();
|
|
pTemplateChild; pTemplateChild = pTemplateChild->GetNextSibling()) {
|
|
if (XFA_DataMerge_NeedGenerateForm(pTemplateChild, bUseInstanceManager)) {
|
|
XFA_NodeMerge_CloneOrMergeContainer(pDocument, pSubformSetNode,
|
|
pTemplateChild, true, nullptr);
|
|
} else if (pTemplateChild->IsContainerNode()) {
|
|
if (bFound && eRelation == XFA_AttributeValue::Choice)
|
|
continue;
|
|
|
|
pDocument->DataMerge_CopyContainer(pTemplateChild, pSubformSetNode,
|
|
pDataScope, false, bDataMerge, true);
|
|
bFound = true;
|
|
}
|
|
}
|
|
}
|
|
return pFirstInstance;
|
|
}
|
|
|
|
void UpdateBindingRelations(CXFA_Document* pDocument,
|
|
CXFA_Node* pFormNode,
|
|
CXFA_Node* pDataScope,
|
|
bool bDataRef,
|
|
bool bParentDataRef) {
|
|
bool bMatchRef = true;
|
|
XFA_Element eType = pFormNode->GetElementType();
|
|
CXFA_Node* pDataNode = pFormNode->GetBindData();
|
|
if (eType == XFA_Element::Subform || eType == XFA_Element::ExclGroup ||
|
|
eType == XFA_Element::Field) {
|
|
CXFA_Node* pTemplateNode = pFormNode->GetTemplateNodeIfExists();
|
|
CXFA_Bind* pTemplateNodeBind =
|
|
pTemplateNode
|
|
? pTemplateNode->GetFirstChildByClass<CXFA_Bind>(XFA_Element::Bind)
|
|
: nullptr;
|
|
XFA_AttributeValue eMatch =
|
|
pTemplateNodeBind
|
|
? pTemplateNodeBind->JSObject()->GetEnum(XFA_Attribute::Match)
|
|
: XFA_AttributeValue::Once;
|
|
switch (eMatch) {
|
|
case XFA_AttributeValue::None:
|
|
if (!bDataRef || bParentDataRef)
|
|
FormValueNode_MatchNoneCreateChild(pFormNode);
|
|
break;
|
|
case XFA_AttributeValue::Once:
|
|
if (!bDataRef || bParentDataRef) {
|
|
if (!pDataNode) {
|
|
if (pFormNode->GetNameHash() != 0 &&
|
|
pFormNode->JSObject()->GetEnum(XFA_Attribute::Scope) !=
|
|
XFA_AttributeValue::None) {
|
|
XFA_Element eDataNodeType = (eType == XFA_Element::Subform ||
|
|
XFA_FieldIsMultiListBox(pFormNode))
|
|
? XFA_Element::DataGroup
|
|
: XFA_Element::DataValue;
|
|
pDataNode = MaybeCreateDataNode(
|
|
pDocument, pDataScope, eDataNodeType,
|
|
WideString(
|
|
pFormNode->JSObject()->GetCData(XFA_Attribute::Name)));
|
|
if (pDataNode)
|
|
CreateDataBinding(pFormNode, pDataNode, false);
|
|
}
|
|
if (!pDataNode)
|
|
FormValueNode_MatchNoneCreateChild(pFormNode);
|
|
|
|
} else {
|
|
CXFA_Node* pDataParent = pDataNode->GetParent();
|
|
if (pDataParent != pDataScope) {
|
|
ASSERT(pDataParent);
|
|
pDataParent->RemoveChildAndNotify(pDataNode, true);
|
|
pDataScope->InsertChildAndNotify(pDataNode, nullptr);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case XFA_AttributeValue::Global:
|
|
if (!bDataRef || bParentDataRef) {
|
|
uint32_t dwNameHash = pFormNode->GetNameHash();
|
|
if (dwNameHash != 0 && !pDataNode) {
|
|
pDataNode = pDocument->GetGlobalBinding(dwNameHash);
|
|
if (!pDataNode) {
|
|
XFA_Element eDataNodeType = (eType == XFA_Element::Subform ||
|
|
XFA_FieldIsMultiListBox(pFormNode))
|
|
? XFA_Element::DataGroup
|
|
: XFA_Element::DataValue;
|
|
CXFA_Node* pRecordNode =
|
|
ToNode(pDocument->GetXFAObject(XFA_HASHCODE_Record));
|
|
pDataNode = MaybeCreateDataNode(
|
|
pDocument, pRecordNode, eDataNodeType,
|
|
WideString(
|
|
pFormNode->JSObject()->GetCData(XFA_Attribute::Name)));
|
|
if (pDataNode) {
|
|
CreateDataBinding(pFormNode, pDataNode, false);
|
|
pDocument->RegisterGlobalBinding(pFormNode->GetNameHash(),
|
|
pDataNode);
|
|
}
|
|
} else {
|
|
CreateDataBinding(pFormNode, pDataNode, true);
|
|
}
|
|
}
|
|
if (!pDataNode)
|
|
FormValueNode_MatchNoneCreateChild(pFormNode);
|
|
}
|
|
break;
|
|
case XFA_AttributeValue::DataRef: {
|
|
bMatchRef = bDataRef;
|
|
bParentDataRef = true;
|
|
if (!pDataNode && bDataRef) {
|
|
WideString wsRef =
|
|
pTemplateNodeBind
|
|
? pTemplateNodeBind->JSObject()->GetCData(XFA_Attribute::Ref)
|
|
: WideString();
|
|
uint32_t dFlags =
|
|
XFA_RESOLVENODE_Children | XFA_RESOLVENODE_CreateNode;
|
|
XFA_RESOLVENODE_RS rs;
|
|
pDocument->GetScriptContext()->ResolveObjects(
|
|
pDataScope, wsRef.AsStringView(), &rs, dFlags, pTemplateNode);
|
|
CXFA_Object* pObject =
|
|
!rs.objects.empty() ? rs.objects.front().Get() : nullptr;
|
|
pDataNode = ToNode(pObject);
|
|
if (pDataNode) {
|
|
CreateDataBinding(pFormNode, pDataNode,
|
|
rs.dwFlags == XFA_ResolveNode_RSType_ExistNodes);
|
|
} else {
|
|
FormValueNode_MatchNoneCreateChild(pFormNode);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bMatchRef &&
|
|
(eType == XFA_Element::Subform || eType == XFA_Element::SubformSet ||
|
|
eType == XFA_Element::Area || eType == XFA_Element::PageArea ||
|
|
eType == XFA_Element::PageSet)) {
|
|
for (CXFA_Node* pFormChild = pFormNode->GetFirstChild(); pFormChild;
|
|
pFormChild = pFormChild->GetNextSibling()) {
|
|
if (!pFormChild->IsContainerNode())
|
|
continue;
|
|
if (pFormChild->IsUnusedNode())
|
|
continue;
|
|
|
|
UpdateBindingRelations(pDocument, pFormChild,
|
|
pDataNode ? pDataNode : pDataScope, bDataRef,
|
|
bParentDataRef);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UpdateDataRelation(CXFA_Node* pDataNode, CXFA_Node* pDataDescriptionNode) {
|
|
ASSERT(pDataDescriptionNode);
|
|
for (CXFA_Node* pDataChild = pDataNode->GetFirstChild(); pDataChild;
|
|
pDataChild = pDataChild->GetNextSibling()) {
|
|
uint32_t dwNameHash = pDataChild->GetNameHash();
|
|
if (!dwNameHash)
|
|
continue;
|
|
|
|
CXFA_NodeIteratorTemplate<CXFA_Node, CXFA_TraverseStrategy_DDGroup>
|
|
sIterator(pDataDescriptionNode);
|
|
for (CXFA_Node* pDDGroupNode = sIterator.GetCurrent(); pDDGroupNode;
|
|
pDDGroupNode = sIterator.MoveToNext()) {
|
|
if (pDDGroupNode != pDataDescriptionNode) {
|
|
if (pDDGroupNode->GetElementType() != XFA_Element::DataGroup)
|
|
continue;
|
|
|
|
Optional<WideString> ns = pDDGroupNode->JSObject()->TryNamespace();
|
|
if (!ns.has_value() ||
|
|
!ns.value().EqualsASCII("http://ns.adobe.com/data-description/")) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
CXFA_Node* pDDNode = pDDGroupNode->GetFirstChildByName(dwNameHash);
|
|
if (!pDDNode)
|
|
continue;
|
|
if (pDDNode->GetElementType() != pDataChild->GetElementType())
|
|
break;
|
|
|
|
pDataChild->SetDataDescriptionNode(pDDNode);
|
|
UpdateDataRelation(pDataChild, pDDNode);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
CXFA_Document::CXFA_Document(CXFA_FFNotify* notify,
|
|
std::unique_ptr<LayoutProcessorIface> pLayout)
|
|
: CXFA_NodeOwner(),
|
|
notify_(notify),
|
|
m_pLayoutProcessor(std::move(pLayout)) {
|
|
if (m_pLayoutProcessor)
|
|
m_pLayoutProcessor->SetDocument(this);
|
|
}
|
|
|
|
CXFA_Document::~CXFA_Document() = default;
|
|
|
|
void CXFA_Document::ClearLayoutData() {
|
|
m_pLayoutProcessor.reset();
|
|
m_pScriptContext.reset();
|
|
m_pLocaleMgr.reset();
|
|
m_pScriptDataWindow.reset();
|
|
m_pScriptEvent.reset();
|
|
m_pScriptHost.reset();
|
|
m_pScriptLog.reset();
|
|
m_pScriptLayout.reset();
|
|
m_pScriptSignature.reset();
|
|
}
|
|
|
|
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_DataGroup* pDatasetsChild =
|
|
pDatasetsNode->GetFirstChildByClass<CXFA_DataGroup>(
|
|
XFA_Element::DataGroup);
|
|
pDatasetsChild;
|
|
pDatasetsChild =
|
|
pDatasetsChild->GetNextSameClassSibling<CXFA_DataGroup>(
|
|
XFA_Element::DataGroup)) {
|
|
if (pDatasetsChild->GetNameHash() != XFA_HASHCODE_Data)
|
|
continue;
|
|
|
|
Optional<WideString> namespaceURI =
|
|
pDatasetsChild->JSObject()->TryNamespace();
|
|
if (!namespaceURI)
|
|
continue;
|
|
|
|
Optional<WideString> datasetsURI =
|
|
pDatasetsNode->JSObject()->TryNamespace();
|
|
if (!datasetsURI)
|
|
continue;
|
|
if (*namespaceURI == *datasetsURI)
|
|
return pDatasetsChild;
|
|
}
|
|
return nullptr;
|
|
}
|
|
case XFA_HASHCODE_Record: {
|
|
CXFA_Node* pData = ToNode(GetXFAObject(XFA_HASHCODE_Data));
|
|
return pData ? pData->GetFirstChildByClass<CXFA_DataGroup>(
|
|
XFA_Element::DataGroup)
|
|
: nullptr;
|
|
}
|
|
case XFA_HASHCODE_DataWindow: {
|
|
if (!m_pScriptDataWindow)
|
|
m_pScriptDataWindow = pdfium::MakeUnique<CScript_DataWindow>(this);
|
|
return m_pScriptDataWindow.get();
|
|
}
|
|
case XFA_HASHCODE_Event: {
|
|
if (!m_pScriptEvent)
|
|
m_pScriptEvent = pdfium::MakeUnique<CScript_EventPseudoModel>(this);
|
|
return m_pScriptEvent.get();
|
|
}
|
|
case XFA_HASHCODE_Host: {
|
|
if (!m_pScriptHost)
|
|
m_pScriptHost = pdfium::MakeUnique<CScript_HostPseudoModel>(this);
|
|
return m_pScriptHost.get();
|
|
}
|
|
case XFA_HASHCODE_Log: {
|
|
if (!m_pScriptLog)
|
|
m_pScriptLog = pdfium::MakeUnique<CScript_LogPseudoModel>(this);
|
|
return m_pScriptLog.get();
|
|
}
|
|
case XFA_HASHCODE_Signature: {
|
|
if (!m_pScriptSignature)
|
|
m_pScriptSignature =
|
|
pdfium::MakeUnique<CScript_SignaturePseudoModel>(this);
|
|
return m_pScriptSignature.get();
|
|
}
|
|
case XFA_HASHCODE_Layout: {
|
|
if (!m_pScriptLayout)
|
|
m_pScriptLayout = pdfium::MakeUnique<CScript_LayoutPseudoModel>(this);
|
|
return m_pScriptLayout.get();
|
|
}
|
|
default:
|
|
return m_pRootNode->GetFirstChildByName(dwNodeNameHash);
|
|
}
|
|
}
|
|
|
|
CXFA_Node* CXFA_Document::CreateNode(XFA_PacketType packet,
|
|
XFA_Element eElement) {
|
|
if (eElement == XFA_Element::Unknown)
|
|
return nullptr;
|
|
return AddOwnedNode(CXFA_Node::Create(this, eElement, packet));
|
|
}
|
|
|
|
bool CXFA_Document::IsInteractive() {
|
|
if (m_Interactive.has_value())
|
|
return m_Interactive.value();
|
|
|
|
CXFA_Node* pConfig = ToNode(GetXFAObject(XFA_HASHCODE_Config));
|
|
if (!pConfig)
|
|
return false;
|
|
|
|
CXFA_Present* pPresent =
|
|
pConfig->GetFirstChildByClass<CXFA_Present>(XFA_Element::Present);
|
|
if (!pPresent)
|
|
return false;
|
|
|
|
CXFA_Pdf* pPDF = pPresent->GetFirstChildByClass<CXFA_Pdf>(XFA_Element::Pdf);
|
|
if (!pPDF)
|
|
return false;
|
|
|
|
CXFA_Interactive* pFormFiller =
|
|
pPDF->GetChild<CXFA_Interactive>(0, XFA_Element::Interactive, false);
|
|
if (!pFormFiller)
|
|
return false;
|
|
|
|
WideString wsInteractive = pFormFiller->JSObject()->GetContent(false);
|
|
bool bInteractive = wsInteractive.EqualsASCII("1");
|
|
m_Interactive = bInteractive;
|
|
return bInteractive;
|
|
}
|
|
|
|
CXFA_LocaleMgr* CXFA_Document::GetLocaleMgr() {
|
|
if (!m_pLocaleMgr) {
|
|
m_pLocaleMgr = pdfium::MakeUnique<CXFA_LocaleMgr>(
|
|
ToNode(GetXFAObject(XFA_HASHCODE_LocaleSet)),
|
|
GetNotify()->GetAppProvider()->GetLanguage());
|
|
}
|
|
return m_pLocaleMgr.get();
|
|
}
|
|
|
|
CFXJSE_Engine* CXFA_Document::InitScriptContext(CJS_Runtime* fxjs_runtime) {
|
|
ASSERT(!m_pScriptContext);
|
|
m_pScriptContext = pdfium::MakeUnique<CFXJSE_Engine>(this, fxjs_runtime);
|
|
return m_pScriptContext.get();
|
|
}
|
|
|
|
CFXJSE_Engine* CXFA_Document::GetScriptContext() const {
|
|
ASSERT(m_pScriptContext);
|
|
return m_pScriptContext.get();
|
|
}
|
|
|
|
XFA_VERSION CXFA_Document::RecognizeXFAVersionNumber(
|
|
const WideString& wsTemplateNS) {
|
|
WideStringView wsTemplateURIPrefix(kTemplateNS);
|
|
if (wsTemplateNS.GetLength() <= wsTemplateURIPrefix.GetLength())
|
|
return XFA_VERSION_UNKNOWN;
|
|
|
|
size_t prefixLength = wsTemplateURIPrefix.GetLength();
|
|
if (WideStringView(wsTemplateNS.c_str(), prefixLength) != wsTemplateURIPrefix)
|
|
return XFA_VERSION_UNKNOWN;
|
|
|
|
auto nDotPos = wsTemplateNS.Find('.', prefixLength);
|
|
if (!nDotPos.has_value())
|
|
return XFA_VERSION_UNKNOWN;
|
|
|
|
int8_t iMajor = FXSYS_wtoi(
|
|
wsTemplateNS.Substr(prefixLength, nDotPos.value() - prefixLength)
|
|
.c_str());
|
|
int8_t iMinor =
|
|
FXSYS_wtoi(wsTemplateNS
|
|
.Substr(nDotPos.value() + 1,
|
|
wsTemplateNS.GetLength() - nDotPos.value() - 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;
|
|
}
|
|
|
|
FormType CXFA_Document::GetFormType() const {
|
|
return GetNotify()->GetHDOC()->GetFormType();
|
|
}
|
|
|
|
CXFA_Node* CXFA_Document::GetNodeByID(CXFA_Node* pRoot,
|
|
WideStringView wsID) const {
|
|
if (!pRoot || wsID.IsEmpty())
|
|
return nullptr;
|
|
|
|
CXFA_NodeIterator sIterator(pRoot);
|
|
for (CXFA_Node* pNode = sIterator.GetCurrent(); pNode;
|
|
pNode = sIterator.MoveToNext()) {
|
|
WideString wsIDVal = pNode->JSObject()->GetCData(XFA_Attribute::Id);
|
|
if (!wsIDVal.IsEmpty() && wsIDVal == wsID)
|
|
return pNode;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void CXFA_Document::DoProtoMerge() {
|
|
CXFA_Node* pTemplateRoot = ToNode(GetXFAObject(XFA_HASHCODE_Template));
|
|
if (!pTemplateRoot)
|
|
return;
|
|
|
|
std::map<uint32_t, CXFA_Node*> mIDMap;
|
|
std::set<CXFA_Node*> sUseNodes;
|
|
CXFA_NodeIterator sIterator(pTemplateRoot);
|
|
for (CXFA_Node* pNode = sIterator.GetCurrent(); pNode;
|
|
pNode = sIterator.MoveToNext()) {
|
|
WideString wsIDVal = pNode->JSObject()->GetCData(XFA_Attribute::Id);
|
|
if (!wsIDVal.IsEmpty())
|
|
mIDMap[FX_HashCode_GetW(wsIDVal.AsStringView(), false)] = pNode;
|
|
|
|
WideString wsUseVal = pNode->JSObject()->GetCData(XFA_Attribute::Use);
|
|
if (!wsUseVal.IsEmpty()) {
|
|
sUseNodes.insert(pNode);
|
|
} else {
|
|
wsUseVal = pNode->JSObject()->GetCData(XFA_Attribute::Usehref);
|
|
if (!wsUseVal.IsEmpty())
|
|
sUseNodes.insert(pNode);
|
|
}
|
|
}
|
|
|
|
for (CXFA_Node* pUseHrefNode : sUseNodes) {
|
|
// Must outlive the WideStringViews below.
|
|
WideString wsUseVal =
|
|
pUseHrefNode->JSObject()->GetCData(XFA_Attribute::Usehref);
|
|
WideStringView wsURI;
|
|
WideStringView wsID;
|
|
WideStringView wsSOM;
|
|
|
|
if (!wsUseVal.IsEmpty()) {
|
|
auto uSharpPos = wsUseVal.Find('#');
|
|
if (!uSharpPos.has_value()) {
|
|
wsURI = wsUseVal.AsStringView();
|
|
} else {
|
|
wsURI = WideStringView(wsUseVal.c_str(), uSharpPos.value());
|
|
size_t uLen = wsUseVal.GetLength();
|
|
if (uLen >= uSharpPos.value() + 5 &&
|
|
WideStringView(wsUseVal.c_str() + uSharpPos.value(), 5) ==
|
|
L"#som(" &&
|
|
wsUseVal[uLen - 1] == ')') {
|
|
wsSOM = WideStringView(wsUseVal.c_str() + uSharpPos.value() + 5,
|
|
uLen - 1 - uSharpPos.value() - 5);
|
|
} else {
|
|
wsID = WideStringView(wsUseVal.c_str() + uSharpPos.value() + 1,
|
|
uLen - uSharpPos.value() - 1);
|
|
}
|
|
}
|
|
} else {
|
|
wsUseVal = pUseHrefNode->JSObject()->GetCData(XFA_Attribute::Use);
|
|
if (!wsUseVal.IsEmpty()) {
|
|
if (wsUseVal[0] == '#')
|
|
wsID = WideStringView(wsUseVal.c_str() + 1, wsUseVal.GetLength() - 1);
|
|
else
|
|
wsSOM = WideStringView(wsUseVal.c_str(), wsUseVal.GetLength());
|
|
}
|
|
}
|
|
if (!wsURI.IsEmpty() && !wsURI.EqualsASCII("."))
|
|
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 resolveNodeRS;
|
|
if (m_pScriptContext->ResolveObjects(pUseHrefNode, wsSOM, &resolveNodeRS,
|
|
dwFlag, nullptr)) {
|
|
auto* pFirstObject = resolveNodeRS.objects.front().Get();
|
|
if (pFirstObject && pFirstObject->IsNode())
|
|
pProtoNode = pFirstObject->AsNode();
|
|
}
|
|
} else if (!wsID.IsEmpty()) {
|
|
auto it = mIDMap.find(FX_HashCode_GetW(wsID, false));
|
|
if (it == mIDMap.end())
|
|
continue;
|
|
pProtoNode = it->second;
|
|
}
|
|
if (!pProtoNode)
|
|
continue;
|
|
|
|
MergeNode(pUseHrefNode, pProtoNode);
|
|
}
|
|
}
|
|
|
|
CXFA_Node* CXFA_Document::DataMerge_CopyContainer(CXFA_Node* pTemplateNode,
|
|
CXFA_Node* pFormNode,
|
|
CXFA_Node* pDataScope,
|
|
bool bOneInstance,
|
|
bool bDataMerge,
|
|
bool bUpLevel) {
|
|
ASSERT(pTemplateNode->IsContainerNode());
|
|
switch (pTemplateNode->GetElementType()) {
|
|
case XFA_Element::Area:
|
|
case XFA_Element::PageArea:
|
|
case XFA_Element::Subform:
|
|
case XFA_Element::SubformSet:
|
|
return CopyContainer_SubformSet(this, pTemplateNode, pFormNode,
|
|
pDataScope, bOneInstance, bDataMerge);
|
|
case XFA_Element::ContentArea:
|
|
case XFA_Element::Draw:
|
|
case XFA_Element::ExclGroup:
|
|
case XFA_Element::Field:
|
|
return CopyContainer_Field(this, pTemplateNode, pFormNode, pDataScope,
|
|
bDataMerge, bUpLevel);
|
|
case XFA_Element::PageSet:
|
|
case XFA_Element::Variables:
|
|
return nullptr;
|
|
default:
|
|
NOTREACHED();
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
void CXFA_Document::DataMerge_UpdateBindingRelations(
|
|
CXFA_Node* pFormUpdateRoot) {
|
|
CXFA_Node* pDataScope =
|
|
XFA_DataMerge_FindDataScope(pFormUpdateRoot->GetParent());
|
|
if (!pDataScope)
|
|
return;
|
|
|
|
UpdateBindingRelations(this, pFormUpdateRoot, pDataScope, false, false);
|
|
UpdateBindingRelations(this, pFormUpdateRoot, pDataScope, true, false);
|
|
}
|
|
|
|
CXFA_Node* CXFA_Document::GetNotBindNode(
|
|
const std::vector<UnownedPtr<CXFA_Object>>& arrayObjects) const {
|
|
for (auto& pObject : arrayObjects) {
|
|
CXFA_Node* pNode = pObject->AsNode();
|
|
if (pNode && !pNode->HasBindItem())
|
|
return pNode;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void CXFA_Document::DoDataMerge() {
|
|
CXFA_Node* pDatasetsRoot = ToNode(GetXFAObject(XFA_HASHCODE_Datasets));
|
|
if (!pDatasetsRoot) {
|
|
// Ownership will be passed in the AppendChild below to the XML tree.
|
|
auto* pDatasetsXMLNode =
|
|
notify_->GetHDOC()->GetXMLDocument()->CreateNode<CFX_XMLElement>(
|
|
L"xfa:datasets");
|
|
pDatasetsXMLNode->SetAttribute(L"xmlns:xfa",
|
|
L"http://www.xfa.org/schema/xfa-data/1.0/");
|
|
pDatasetsRoot =
|
|
CreateNode(XFA_PacketType::Datasets, XFA_Element::DataModel);
|
|
pDatasetsRoot->JSObject()->SetCData(XFA_Attribute::Name, L"datasets", false,
|
|
false);
|
|
|
|
m_pRootNode->GetXMLMappingNode()->AppendLastChild(pDatasetsXMLNode);
|
|
m_pRootNode->InsertChildAndNotify(pDatasetsRoot, nullptr);
|
|
pDatasetsRoot->SetXMLMappingNode(pDatasetsXMLNode);
|
|
}
|
|
|
|
CXFA_Node* pDataRoot = nullptr;
|
|
CXFA_Node* pDDRoot = nullptr;
|
|
WideString wsDatasetsURI =
|
|
pDatasetsRoot->JSObject()->TryNamespace().value_or(WideString());
|
|
for (CXFA_Node* pChildNode = pDatasetsRoot->GetFirstChild(); pChildNode;
|
|
pChildNode = pChildNode->GetNextSibling()) {
|
|
if (pChildNode->GetElementType() != XFA_Element::DataGroup)
|
|
continue;
|
|
|
|
if (!pDDRoot && pChildNode->GetNameHash() == XFA_HASHCODE_DataDescription) {
|
|
Optional<WideString> namespaceURI =
|
|
pChildNode->JSObject()->TryNamespace();
|
|
if (!namespaceURI.has_value())
|
|
continue;
|
|
if (namespaceURI.value().EqualsASCII(
|
|
"http://ns.adobe.com/data-description/")) {
|
|
pDDRoot = pChildNode;
|
|
}
|
|
} else if (!pDataRoot && pChildNode->GetNameHash() == XFA_HASHCODE_Data) {
|
|
Optional<WideString> namespaceURI =
|
|
pChildNode->JSObject()->TryNamespace();
|
|
if (!namespaceURI)
|
|
continue;
|
|
if (*namespaceURI == wsDatasetsURI)
|
|
pDataRoot = pChildNode;
|
|
}
|
|
if (pDataRoot && pDDRoot)
|
|
break;
|
|
}
|
|
|
|
if (!pDataRoot) {
|
|
pDataRoot = CreateNode(XFA_PacketType::Datasets, XFA_Element::DataGroup);
|
|
pDataRoot->JSObject()->SetCData(XFA_Attribute::Name, L"data", false, false);
|
|
|
|
auto* elem =
|
|
notify_->GetHDOC()->GetXMLDocument()->CreateNode<CFX_XMLElement>(
|
|
L"xfa:data");
|
|
pDataRoot->SetXMLMappingNode(elem);
|
|
pDatasetsRoot->InsertChildAndNotify(pDataRoot, nullptr);
|
|
}
|
|
|
|
CXFA_DataGroup* pDataTopLevel =
|
|
pDataRoot->GetFirstChildByClass<CXFA_DataGroup>(XFA_Element::DataGroup);
|
|
uint32_t dwNameHash = pDataTopLevel ? pDataTopLevel->GetNameHash() : 0;
|
|
CXFA_Template* pTemplateRoot =
|
|
m_pRootNode->GetFirstChildByClass<CXFA_Template>(XFA_Element::Template);
|
|
if (!pTemplateRoot)
|
|
return;
|
|
|
|
CXFA_Node* pTemplateChosen =
|
|
dwNameHash != 0 ? pTemplateRoot->GetFirstChildByName(dwNameHash)
|
|
: nullptr;
|
|
if (!pTemplateChosen ||
|
|
pTemplateChosen->GetElementType() != XFA_Element::Subform) {
|
|
pTemplateChosen =
|
|
pTemplateRoot->GetFirstChildByClass<CXFA_Subform>(XFA_Element::Subform);
|
|
}
|
|
if (!pTemplateChosen)
|
|
return;
|
|
|
|
CXFA_Form* pFormRoot =
|
|
m_pRootNode->GetFirstChildByClass<CXFA_Form>(XFA_Element::Form);
|
|
bool bEmptyForm = false;
|
|
if (!pFormRoot) {
|
|
bEmptyForm = true;
|
|
pFormRoot = static_cast<CXFA_Form*>(
|
|
CreateNode(XFA_PacketType::Form, XFA_Element::Form));
|
|
ASSERT(pFormRoot);
|
|
pFormRoot->JSObject()->SetCData(XFA_Attribute::Name, L"form", false, false);
|
|
m_pRootNode->InsertChildAndNotify(pFormRoot, nullptr);
|
|
} else {
|
|
CXFA_NodeIteratorTemplate<CXFA_Node, CXFA_TraverseStrategy_XFANode>
|
|
sIterator(pFormRoot);
|
|
for (CXFA_Node* pNode = sIterator.MoveToNext(); pNode;
|
|
pNode = sIterator.MoveToNext()) {
|
|
pNode->SetFlag(XFA_NodeFlag_UnusedNode);
|
|
}
|
|
}
|
|
|
|
CXFA_Node* pSubformSetNode = XFA_NodeMerge_CloneOrMergeContainer(
|
|
this, pFormRoot, pTemplateChosen, false, nullptr);
|
|
ASSERT(pSubformSetNode);
|
|
if (!pDataTopLevel) {
|
|
WideString wsFormName =
|
|
pSubformSetNode->JSObject()->GetCData(XFA_Attribute::Name);
|
|
WideString wsDataTopLevelName(wsFormName.IsEmpty() ? L"form" : wsFormName);
|
|
|
|
pDataTopLevel = static_cast<CXFA_DataGroup*>(
|
|
CreateNode(XFA_PacketType::Datasets, XFA_Element::DataGroup));
|
|
pDataTopLevel->JSObject()->SetCData(XFA_Attribute::Name, wsDataTopLevelName,
|
|
false, false);
|
|
|
|
auto* elem =
|
|
notify_->GetHDOC()->GetXMLDocument()->CreateNode<CFX_XMLElement>(
|
|
wsDataTopLevelName);
|
|
pDataTopLevel->SetXMLMappingNode(elem);
|
|
|
|
CXFA_Node* pBeforeNode = pDataRoot->GetFirstChild();
|
|
pDataRoot->InsertChildAndNotify(pDataTopLevel, pBeforeNode);
|
|
}
|
|
|
|
ASSERT(pDataTopLevel);
|
|
CreateDataBinding(pSubformSetNode, pDataTopLevel, true);
|
|
for (CXFA_Node* pTemplateChild = pTemplateChosen->GetFirstChild();
|
|
pTemplateChild; pTemplateChild = pTemplateChild->GetNextSibling()) {
|
|
if (XFA_DataMerge_NeedGenerateForm(pTemplateChild, true)) {
|
|
XFA_NodeMerge_CloneOrMergeContainer(this, pSubformSetNode, pTemplateChild,
|
|
true, nullptr);
|
|
} else if (pTemplateChild->IsContainerNode()) {
|
|
DataMerge_CopyContainer(pTemplateChild, pSubformSetNode, pDataTopLevel,
|
|
false, true, true);
|
|
}
|
|
}
|
|
if (pDDRoot)
|
|
UpdateDataRelation(pDataRoot, pDDRoot);
|
|
|
|
DataMerge_UpdateBindingRelations(pSubformSetNode);
|
|
CXFA_PageSet* pPageSetNode =
|
|
pSubformSetNode->GetFirstChildByClass<CXFA_PageSet>(XFA_Element::PageSet);
|
|
while (pPageSetNode) {
|
|
m_pPendingPageSet.push_back(pPageSetNode);
|
|
CXFA_PageSet* pNextPageSetNode =
|
|
pPageSetNode->GetNextSameClassSibling<CXFA_PageSet>(
|
|
XFA_Element::PageSet);
|
|
pSubformSetNode->RemoveChildAndNotify(pPageSetNode, true);
|
|
pPageSetNode = pNextPageSetNode;
|
|
}
|
|
|
|
if (bEmptyForm)
|
|
return;
|
|
|
|
CXFA_NodeIteratorTemplate<CXFA_Node, CXFA_TraverseStrategy_XFANode> sIterator(
|
|
pFormRoot);
|
|
CXFA_Node* pNode = sIterator.MoveToNext();
|
|
while (pNode) {
|
|
if (pNode->IsUnusedNode()) {
|
|
if (pNode->IsContainerNode() ||
|
|
pNode->GetElementType() == XFA_Element::InstanceManager) {
|
|
CXFA_Node* pNext = sIterator.SkipChildrenAndMoveToNext();
|
|
pNode->GetParent()->RemoveChildAndNotify(pNode, true);
|
|
pNode = pNext;
|
|
} else {
|
|
pNode->ClearFlag(XFA_NodeFlag_UnusedNode);
|
|
pNode->SetFlagAndNotify(XFA_NodeFlag_Initialized);
|
|
pNode = sIterator.MoveToNext();
|
|
}
|
|
} else {
|
|
pNode->SetFlagAndNotify(XFA_NodeFlag_Initialized);
|
|
pNode = sIterator.MoveToNext();
|
|
}
|
|
}
|
|
}
|
|
|
|
void CXFA_Document::DoDataRemerge(bool bDoDataMerge) {
|
|
CXFA_Node* pFormRoot = ToNode(GetXFAObject(XFA_HASHCODE_Form));
|
|
if (pFormRoot) {
|
|
while (CXFA_Node* pNode = pFormRoot->GetFirstChild())
|
|
pFormRoot->RemoveChildAndNotify(pNode, true);
|
|
|
|
pFormRoot->SetBindingNode(nullptr);
|
|
}
|
|
m_rgGlobalBinding.clear();
|
|
|
|
if (bDoDataMerge)
|
|
DoDataMerge();
|
|
|
|
GetLayoutProcessor()->SetForceRelayout(true);
|
|
}
|
|
|
|
CXFA_Node* CXFA_Document::GetGlobalBinding(uint32_t dwNameHash) {
|
|
auto it = m_rgGlobalBinding.find(dwNameHash);
|
|
return it != m_rgGlobalBinding.end() ? it->second : nullptr;
|
|
}
|
|
|
|
void CXFA_Document::RegisterGlobalBinding(uint32_t dwNameHash,
|
|
CXFA_Node* pDataNode) {
|
|
m_rgGlobalBinding[dwNameHash] = pDataNode;
|
|
}
|
|
|
|
void CXFA_Document::SetPendingNodesUnusedAndUnbound() {
|
|
for (CXFA_Node* pPageNode : m_pPendingPageSet) {
|
|
CXFA_NodeIterator sIterator(pPageNode);
|
|
for (CXFA_Node* pNode = sIterator.GetCurrent(); pNode;
|
|
pNode = sIterator.MoveToNext()) {
|
|
if (pNode->IsContainerNode()) {
|
|
CXFA_Node* pBindNode = pNode->GetBindData();
|
|
if (pBindNode) {
|
|
pBindNode->RemoveBindItem(pNode);
|
|
pNode->SetBindingNode(nullptr);
|
|
}
|
|
}
|
|
pNode->SetFlag(XFA_NodeFlag_UnusedNode);
|
|
}
|
|
}
|
|
}
|
|
|
|
CXFA_Document::LayoutProcessorIface::LayoutProcessorIface() = default;
|
|
|
|
CXFA_Document::LayoutProcessorIface::~LayoutProcessorIface() = default;
|