blob: 6672e7fb29af1100d5b5da95b1bc7a75b27b9823 [file] [log] [blame]
// Copyright 2016 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_node.h"
#include <algorithm>
#include <map>
#include <memory>
#include <set>
#include <utility>
#include <vector>
#include "core/fxcrt/autorestorer.h"
#include "core/fxcrt/cfx_decimal.h"
#include "core/fxcrt/cfx_memorystream.h"
#include "core/fxcrt/fx_codepage.h"
#include "core/fxcrt/fx_extension.h"
#include "core/fxcrt/locale_iface.h"
#include "core/fxcrt/xml/cfx_xmlelement.h"
#include "core/fxcrt/xml/cfx_xmlnode.h"
#include "core/fxcrt/xml/cfx_xmltext.h"
#include "core/fxge/dib/cfx_dibitmap.h"
#include "fxjs/cfxjse_engine.h"
#include "fxjs/cfxjse_value.h"
#include "fxjs/xfa/cjx_node.h"
#include "third_party/base/compiler_specific.h"
#include "third_party/base/logging.h"
#include "third_party/base/ptr_util.h"
#include "third_party/base/stl_util.h"
#include "xfa/fde/cfde_textout.h"
#include "xfa/fgas/font/cfgas_fontmgr.h"
#include "xfa/fxfa/cxfa_eventparam.h"
#include "xfa/fxfa/cxfa_ffapp.h"
#include "xfa/fxfa/cxfa_ffdocview.h"
#include "xfa/fxfa/cxfa_ffnotify.h"
#include "xfa/fxfa/cxfa_ffwidget.h"
#include "xfa/fxfa/cxfa_fontmgr.h"
#include "xfa/fxfa/cxfa_textprovider.h"
#include "xfa/fxfa/parser/cxfa_arraynodelist.h"
#include "xfa/fxfa/parser/cxfa_attachnodelist.h"
#include "xfa/fxfa/parser/cxfa_bind.h"
#include "xfa/fxfa/parser/cxfa_border.h"
#include "xfa/fxfa/parser/cxfa_calculate.h"
#include "xfa/fxfa/parser/cxfa_caption.h"
#include "xfa/fxfa/parser/cxfa_comb.h"
#include "xfa/fxfa/parser/cxfa_decimal.h"
#include "xfa/fxfa/parser/cxfa_document.h"
#include "xfa/fxfa/parser/cxfa_document_parser.h"
#include "xfa/fxfa/parser/cxfa_event.h"
#include "xfa/fxfa/parser/cxfa_font.h"
#include "xfa/fxfa/parser/cxfa_format.h"
#include "xfa/fxfa/parser/cxfa_image.h"
#include "xfa/fxfa/parser/cxfa_items.h"
#include "xfa/fxfa/parser/cxfa_keep.h"
#include "xfa/fxfa/parser/cxfa_layoutprocessor.h"
#include "xfa/fxfa/parser/cxfa_localevalue.h"
#include "xfa/fxfa/parser/cxfa_margin.h"
#include "xfa/fxfa/parser/cxfa_measurement.h"
#include "xfa/fxfa/parser/cxfa_nodeiteratortemplate.h"
#include "xfa/fxfa/parser/cxfa_occur.h"
#include "xfa/fxfa/parser/cxfa_para.h"
#include "xfa/fxfa/parser/cxfa_picture.h"
#include "xfa/fxfa/parser/cxfa_stroke.h"
#include "xfa/fxfa/parser/cxfa_subform.h"
#include "xfa/fxfa/parser/cxfa_traversestrategy_xfacontainernode.h"
#include "xfa/fxfa/parser/cxfa_ui.h"
#include "xfa/fxfa/parser/cxfa_validate.h"
#include "xfa/fxfa/parser/cxfa_value.h"
#include "xfa/fxfa/parser/xfa_basic_data.h"
#include "xfa/fxfa/parser/xfa_utils.h"
class CXFA_WidgetLayoutData {
public:
CXFA_WidgetLayoutData() : m_fWidgetHeight(-1) {}
virtual ~CXFA_WidgetLayoutData() {}
float m_fWidgetHeight;
};
namespace {
constexpr uint8_t kMaxExecuteRecursion = 2;
constexpr uint8_t g_inv_base64[128] = {
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255,
255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255,
255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 255, 255, 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33,
34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, 255, 255, 255, 255, 255,
};
uint8_t* XFA_RemoveBase64Whitespace(const uint8_t* pStr, int32_t iLen) {
uint8_t* pCP;
int32_t i = 0, j = 0;
if (iLen == 0) {
iLen = strlen((char*)pStr);
}
pCP = FX_Alloc(uint8_t, iLen + 1);
for (; i < iLen; i++) {
if ((pStr[i] & 128) == 0) {
if (g_inv_base64[pStr[i]] != 0xFF || pStr[i] == '=') {
pCP[j++] = pStr[i];
}
}
}
pCP[j] = '\0';
return pCP;
}
int32_t XFA_Base64Decode(const char* pStr, uint8_t* pOutBuffer) {
if (!pStr) {
return 0;
}
uint8_t* pBuffer =
XFA_RemoveBase64Whitespace((uint8_t*)pStr, strlen((char*)pStr));
if (!pBuffer) {
return 0;
}
int32_t iLen = strlen((char*)pBuffer);
int32_t i = 0, j = 0;
uint32_t dwLimb = 0;
for (; i + 3 < iLen; i += 4) {
if (pBuffer[i] == '=' || pBuffer[i + 1] == '=' || pBuffer[i + 2] == '=' ||
pBuffer[i + 3] == '=') {
if (pBuffer[i] == '=' || pBuffer[i + 1] == '=') {
break;
}
if (pBuffer[i + 2] == '=') {
dwLimb = ((uint32_t)g_inv_base64[pBuffer[i]] << 6) |
((uint32_t)g_inv_base64[pBuffer[i + 1]]);
pOutBuffer[j] = (uint8_t)(dwLimb >> 4) & 0xFF;
j++;
} else {
dwLimb = ((uint32_t)g_inv_base64[pBuffer[i]] << 12) |
((uint32_t)g_inv_base64[pBuffer[i + 1]] << 6) |
((uint32_t)g_inv_base64[pBuffer[i + 2]]);
pOutBuffer[j] = (uint8_t)(dwLimb >> 10) & 0xFF;
pOutBuffer[j + 1] = (uint8_t)(dwLimb >> 2) & 0xFF;
j += 2;
}
} else {
dwLimb = ((uint32_t)g_inv_base64[pBuffer[i]] << 18) |
((uint32_t)g_inv_base64[pBuffer[i + 1]] << 12) |
((uint32_t)g_inv_base64[pBuffer[i + 2]] << 6) |
((uint32_t)g_inv_base64[pBuffer[i + 3]]);
pOutBuffer[j] = (uint8_t)(dwLimb >> 16) & 0xff;
pOutBuffer[j + 1] = (uint8_t)(dwLimb >> 8) & 0xff;
pOutBuffer[j + 2] = (uint8_t)(dwLimb)&0xff;
j += 3;
}
}
FX_Free(pBuffer);
return j;
}
FXCODEC_IMAGE_TYPE XFA_GetImageType(const WideString& wsType) {
WideString wsContentType(wsType);
wsContentType.MakeLower();
if (wsContentType == L"image/jpg")
return FXCODEC_IMAGE_JPG;
#ifdef PDF_ENABLE_XFA_BMP
if (wsContentType == L"image/bmp")
return FXCODEC_IMAGE_BMP;
#endif // PDF_ENABLE_XFA_BMP
#ifdef PDF_ENABLE_XFA_GIF
if (wsContentType == L"image/gif")
return FXCODEC_IMAGE_GIF;
#endif // PDF_ENABLE_XFA_GIF
#ifdef PDF_ENABLE_XFA_PNG
if (wsContentType == L"image/png")
return FXCODEC_IMAGE_PNG;
#endif // PDF_ENABLE_XFA_PNG
#ifdef PDF_ENABLE_XFA_TIFF
if (wsContentType == L"image/tif")
return FXCODEC_IMAGE_TIFF;
#endif // PDF_ENABLE_XFA_TIFF
return FXCODEC_IMAGE_UNKNOWN;
}
RetainPtr<CFX_DIBitmap> XFA_LoadImageData(CXFA_FFDoc* pDoc,
CXFA_Image* pImage,
bool& bNameImage,
int32_t& iImageXDpi,
int32_t& iImageYDpi) {
WideString wsHref = pImage->GetHref();
WideString wsImage = pImage->GetContent();
if (wsHref.IsEmpty() && wsImage.IsEmpty())
return nullptr;
FXCODEC_IMAGE_TYPE type = XFA_GetImageType(pImage->GetContentType());
ByteString bsContent;
uint8_t* pImageBuffer = nullptr;
RetainPtr<IFX_SeekableReadStream> pImageFileRead;
if (wsImage.GetLength() > 0) {
XFA_AttributeEnum iEncoding = pImage->GetTransferEncoding();
if (iEncoding == XFA_AttributeEnum::Base64) {
ByteString bsData = wsImage.UTF8Encode();
int32_t iLength = bsData.GetLength();
pImageBuffer = FX_Alloc(uint8_t, iLength);
int32_t iRead = XFA_Base64Decode(bsData.c_str(), pImageBuffer);
if (iRead > 0) {
pImageFileRead =
pdfium::MakeRetain<CFX_MemoryStream>(pImageBuffer, iRead, false);
}
} else {
bsContent = ByteString::FromUnicode(wsImage);
pImageFileRead = pdfium::MakeRetain<CFX_MemoryStream>(
const_cast<uint8_t*>(bsContent.raw_str()), bsContent.GetLength(),
false);
}
} else {
WideString wsURL = wsHref;
if (wsURL.Left(7) != L"http://" && wsURL.Left(6) != L"ftp://") {
RetainPtr<CFX_DIBitmap> pBitmap =
pDoc->GetPDFNamedImage(wsURL.AsStringView(), iImageXDpi, iImageYDpi);
if (pBitmap) {
bNameImage = true;
return pBitmap;
}
}
pImageFileRead = pDoc->GetDocEnvironment()->OpenLinkedFile(pDoc, wsURL);
}
if (!pImageFileRead) {
FX_Free(pImageBuffer);
return nullptr;
}
bNameImage = false;
RetainPtr<CFX_DIBitmap> pBitmap =
XFA_LoadImageFromBuffer(pImageFileRead, type, iImageXDpi, iImageYDpi);
FX_Free(pImageBuffer);
return pBitmap;
}
class CXFA_TextLayoutData : public CXFA_WidgetLayoutData {
public:
CXFA_TextLayoutData() {}
~CXFA_TextLayoutData() override {}
CXFA_TextLayout* GetTextLayout() const { return m_pTextLayout.get(); }
CXFA_TextProvider* GetTextProvider() const { return m_pTextProvider.get(); }
void LoadText(CXFA_FFDoc* doc, CXFA_Node* pNode) {
if (m_pTextLayout)
return;
m_pTextProvider =
pdfium::MakeUnique<CXFA_TextProvider>(pNode, XFA_TEXTPROVIDERTYPE_Text);
m_pTextLayout =
pdfium::MakeUnique<CXFA_TextLayout>(doc, m_pTextProvider.get());
}
private:
std::unique_ptr<CXFA_TextLayout> m_pTextLayout;
std::unique_ptr<CXFA_TextProvider> m_pTextProvider;
};
class CXFA_ImageLayoutData : public CXFA_WidgetLayoutData {
public:
CXFA_ImageLayoutData()
: m_bNamedImage(false), m_iImageXDpi(0), m_iImageYDpi(0) {}
~CXFA_ImageLayoutData() override {}
bool LoadImageData(CXFA_FFDoc* doc, CXFA_Node* pNode) {
if (m_pDIBitmap)
return true;
CXFA_Value* value = pNode->GetFormValueIfExists();
if (!value)
return false;
CXFA_Image* image = value->GetImageIfExists();
if (!image)
return false;
pNode->SetImageImage(XFA_LoadImageData(doc, image, m_bNamedImage,
m_iImageXDpi, m_iImageYDpi));
return !!m_pDIBitmap;
}
RetainPtr<CFX_DIBitmap> m_pDIBitmap;
bool m_bNamedImage;
int32_t m_iImageXDpi;
int32_t m_iImageYDpi;
};
class CXFA_FieldLayoutData : public CXFA_WidgetLayoutData {
public:
CXFA_FieldLayoutData() {}
~CXFA_FieldLayoutData() override {}
bool LoadCaption(CXFA_FFDoc* doc, CXFA_Node* pNode) {
if (m_pCapTextLayout)
return true;
CXFA_Caption* caption = pNode->GetCaptionIfExists();
if (!caption || caption->IsHidden())
return false;
m_pCapTextProvider = pdfium::MakeUnique<CXFA_TextProvider>(
pNode, XFA_TEXTPROVIDERTYPE_Caption);
m_pCapTextLayout =
pdfium::MakeUnique<CXFA_TextLayout>(doc, m_pCapTextProvider.get());
return true;
}
std::unique_ptr<CXFA_TextLayout> m_pCapTextLayout;
std::unique_ptr<CXFA_TextProvider> m_pCapTextProvider;
std::unique_ptr<CFDE_TextOut> m_pTextOut;
std::vector<float> m_FieldSplitArray;
};
class CXFA_TextEditData : public CXFA_FieldLayoutData {};
class CXFA_ImageEditData : public CXFA_FieldLayoutData {
public:
CXFA_ImageEditData()
: m_bNamedImage(false), m_iImageXDpi(0), m_iImageYDpi(0) {}
~CXFA_ImageEditData() override {}
bool LoadImageData(CXFA_FFDoc* doc, CXFA_Node* pNode) {
if (m_pDIBitmap)
return true;
CXFA_Value* value = pNode->GetFormValueIfExists();
if (!value)
return false;
CXFA_Image* image = value->GetImageIfExists();
if (!image)
return false;
pNode->SetImageEditImage(XFA_LoadImageData(doc, image, m_bNamedImage,
m_iImageXDpi, m_iImageYDpi));
return !!m_pDIBitmap;
}
RetainPtr<CFX_DIBitmap> m_pDIBitmap;
bool m_bNamedImage;
int32_t m_iImageXDpi;
int32_t m_iImageYDpi;
};
bool SplitDateTime(const WideString& wsDateTime,
WideString& wsDate,
WideString& wsTime) {
wsDate = L"";
wsTime = L"";
if (wsDateTime.IsEmpty())
return false;
auto nSplitIndex = wsDateTime.Find('T');
if (!nSplitIndex.has_value())
nSplitIndex = wsDateTime.Find(' ');
if (!nSplitIndex.has_value())
return false;
wsDate = wsDateTime.Left(nSplitIndex.value());
if (!wsDate.IsEmpty()) {
if (!std::any_of(wsDate.begin(), wsDate.end(), std::iswdigit))
return false;
}
wsTime = wsDateTime.Right(wsDateTime.GetLength() - nSplitIndex.value() - 1);
if (!wsTime.IsEmpty()) {
if (!std::any_of(wsTime.begin(), wsTime.end(), std::iswdigit))
return false;
}
return true;
}
std::vector<CXFA_Node*> NodesSortedByDocumentIdx(
const std::set<CXFA_Node*>& rgNodeSet) {
if (rgNodeSet.empty())
return std::vector<CXFA_Node*>();
std::vector<CXFA_Node*> rgNodeArray;
CXFA_Node* pCommonParent = (*rgNodeSet.begin())->GetParent();
for (CXFA_Node* pNode = pCommonParent->GetFirstChild(); pNode;
pNode = pNode->GetNextSibling()) {
if (pdfium::ContainsValue(rgNodeSet, pNode))
rgNodeArray.push_back(pNode);
}
return rgNodeArray;
}
using CXFA_NodeSetPair = std::pair<std::set<CXFA_Node*>, std::set<CXFA_Node*>>;
using CXFA_NodeSetPairMap =
std::map<uint32_t, std::unique_ptr<CXFA_NodeSetPair>>;
using CXFA_NodeSetPairMapMap =
std::map<CXFA_Node*, std::unique_ptr<CXFA_NodeSetPairMap>>;
CXFA_NodeSetPair* NodeSetPairForNode(CXFA_Node* pNode,
CXFA_NodeSetPairMapMap* pMap) {
CXFA_Node* pParentNode = pNode->GetParent();
uint32_t dwNameHash = pNode->GetNameHash();
if (!pParentNode || !dwNameHash)
return nullptr;
if (!(*pMap)[pParentNode])
(*pMap)[pParentNode] = pdfium::MakeUnique<CXFA_NodeSetPairMap>();
CXFA_NodeSetPairMap* pNodeSetPairMap = (*pMap)[pParentNode].get();
if (!(*pNodeSetPairMap)[dwNameHash])
(*pNodeSetPairMap)[dwNameHash] = pdfium::MakeUnique<CXFA_NodeSetPair>();
return (*pNodeSetPairMap)[dwNameHash].get();
}
void ReorderDataNodes(const std::set<CXFA_Node*>& sSet1,
const std::set<CXFA_Node*>& sSet2,
bool bInsertBefore) {
CXFA_NodeSetPairMapMap rgMap;
for (CXFA_Node* pNode : sSet1) {
CXFA_NodeSetPair* pNodeSetPair = NodeSetPairForNode(pNode, &rgMap);
if (pNodeSetPair)
pNodeSetPair->first.insert(pNode);
}
for (CXFA_Node* pNode : sSet2) {
CXFA_NodeSetPair* pNodeSetPair = NodeSetPairForNode(pNode, &rgMap);
if (pNodeSetPair) {
if (pdfium::ContainsValue(pNodeSetPair->first, pNode))
pNodeSetPair->first.erase(pNode);
else
pNodeSetPair->second.insert(pNode);
}
}
for (const auto& iter1 : rgMap) {
CXFA_NodeSetPairMap* pNodeSetPairMap = iter1.second.get();
if (!pNodeSetPairMap)
continue;
for (const auto& iter2 : *pNodeSetPairMap) {
CXFA_NodeSetPair* pNodeSetPair = iter2.second.get();
if (!pNodeSetPair)
continue;
if (!pNodeSetPair->first.empty() && !pNodeSetPair->second.empty()) {
std::vector<CXFA_Node*> rgNodeArray1 =
NodesSortedByDocumentIdx(pNodeSetPair->first);
std::vector<CXFA_Node*> rgNodeArray2 =
NodesSortedByDocumentIdx(pNodeSetPair->second);
CXFA_Node* pParentNode = nullptr;
CXFA_Node* pBeforeNode = nullptr;
if (bInsertBefore) {
pBeforeNode = rgNodeArray2.front();
pParentNode = pBeforeNode->GetParent();
} else {
CXFA_Node* pLastNode = rgNodeArray2.back();
pParentNode = pLastNode->GetParent();
pBeforeNode = pLastNode->GetNextSibling();
}
for (auto* pCurNode : rgNodeArray1) {
pParentNode->RemoveChild(pCurNode, true);
pParentNode->InsertChild(pCurNode, pBeforeNode);
}
}
}
pNodeSetPairMap->clear();
}
}
float GetEdgeThickness(const std::vector<CXFA_Stroke*>& strokes,
bool b3DStyle,
int32_t nIndex) {
float fThickness = 0;
CXFA_Stroke* stroke = strokes[nIndex * 2 + 1];
if (stroke->IsVisible()) {
if (nIndex == 0)
fThickness += 2.5f;
fThickness += stroke->GetThickness() * (b3DStyle ? 4 : 2);
}
return fThickness;
}
} // namespace
// static
WideString CXFA_Node::AttributeEnumToName(XFA_AttributeEnum item) {
return g_XFAEnumData[static_cast<int32_t>(item)].pName;
}
// static
Optional<XFA_AttributeEnum> CXFA_Node::NameToAttributeEnum(
const WideStringView& name) {
if (name.IsEmpty())
return {};
auto* it = std::lower_bound(g_XFAEnumData, g_XFAEnumData + g_iXFAEnumCount,
FX_HashCode_GetW(name, false),
[](const XFA_AttributeEnumInfo& arg,
uint32_t hash) { return arg.uHash < hash; });
if (it != g_XFAEnumData + g_iXFAEnumCount && name == it->pName)
return {it->eName};
return {};
}
CXFA_Node::CXFA_Node(CXFA_Document* pDoc,
XFA_PacketType ePacket,
uint32_t validPackets,
XFA_ObjectType oType,
XFA_Element eType,
const PropertyData* properties,
const AttributeData* attributes,
const WideStringView& elementName,
std::unique_ptr<CJX_Object> js_node)
: CXFA_Object(pDoc, oType, eType, elementName, std::move(js_node)),
m_Properties(properties),
m_Attributes(attributes),
m_ValidPackets(validPackets),
parent_(nullptr),
next_sibling_(nullptr),
prev_sibling_(nullptr),
first_child_(nullptr),
last_child_(nullptr),
m_ePacket(ePacket),
m_uNodeFlags(XFA_NodeFlag_None),
m_dwNameHash(0),
m_pAuxNode(nullptr) {
ASSERT(m_pDocument);
}
CXFA_Node::CXFA_Node(CXFA_Document* pDoc,
XFA_PacketType ePacket,
uint32_t validPackets,
XFA_ObjectType oType,
XFA_Element eType,
const PropertyData* properties,
const AttributeData* attributes,
const WideStringView& elementName)
: CXFA_Node(pDoc,
ePacket,
validPackets,
oType,
eType,
properties,
attributes,
elementName,
pdfium::MakeUnique<CJX_Node>(this)) {}
CXFA_Node::~CXFA_Node() = default;
CXFA_Node* CXFA_Node::Clone(bool bRecursive) {
CXFA_Node* pClone = m_pDocument->CreateNode(m_ePacket, m_elementType);
if (!pClone)
return nullptr;
JSObject()->MergeAllData(pClone);
pClone->UpdateNameHash();
if (IsNeedSavingXMLNode()) {
CFX_XMLNode* pCloneXML;
if (IsAttributeInXML()) {
WideString wsName = JSObject()
->TryAttribute(XFA_Attribute::Name, false)
.value_or(WideString());
auto* pCloneXMLElement = GetDocument()
->GetNotify()
->GetHDOC()
->GetXMLDocument()
->CreateNode<CFX_XMLElement>(wsName);
WideString wsValue = JSObject()->GetCData(XFA_Attribute::Value);
if (!wsValue.IsEmpty()) {
auto* text = GetDocument()
->GetNotify()
->GetHDOC()
->GetXMLDocument()
->CreateNode<CFX_XMLText>(wsValue);
pCloneXMLElement->AppendChild(text);
}
pCloneXML = pCloneXMLElement;
pClone->JSObject()->SetEnum(XFA_Attribute::Contains,
XFA_AttributeEnum::Unknown, false);
} else {
pCloneXML = xml_node_->Clone(
GetDocument()->GetNotify()->GetHDOC()->GetXMLDocument());
}
pClone->SetXMLMappingNode(pCloneXML);
}
if (bRecursive) {
for (CXFA_Node* pChild = GetFirstChild(); pChild;
pChild = pChild->GetNextSibling()) {
pClone->InsertChild(pChild->Clone(bRecursive), nullptr);
}
}
pClone->SetFlagAndNotify(XFA_NodeFlag_Initialized);
pClone->SetBindingNode(nullptr);
return pClone;
}
CXFA_Node* CXFA_Node::GetNextContainerSibling() const {
for (auto* pNode = next_sibling_; pNode; pNode = pNode->next_sibling_) {
if (pNode->GetObjectType() == XFA_ObjectType::ContainerNode)
return pNode;
}
return nullptr;
}
CXFA_Node* CXFA_Node::GetPrevContainerSibling() const {
for (auto* pNode = prev_sibling_; pNode; pNode = pNode->prev_sibling_) {
if (pNode->GetObjectType() == XFA_ObjectType::ContainerNode)
return pNode;
}
return nullptr;
}
CXFA_Node* CXFA_Node::GetFirstContainerChild() const {
for (auto* pNode = first_child_; pNode; pNode = pNode->next_sibling_) {
if (pNode->GetObjectType() == XFA_ObjectType::ContainerNode)
return pNode;
}
return nullptr;
}
CXFA_Node* CXFA_Node::GetContainerParent() const {
for (auto* pNode = parent_; pNode; pNode = pNode->parent_) {
if (pNode->GetObjectType() == XFA_ObjectType::ContainerNode)
return pNode;
}
return nullptr;
}
bool CXFA_Node::IsValidInPacket(XFA_PacketType packet) const {
return !!(m_ValidPackets & (1 << static_cast<uint8_t>(packet)));
}
const CXFA_Node::PropertyData* CXFA_Node::GetPropertyData(
XFA_Element property) const {
if (m_Properties == nullptr)
return nullptr;
for (size_t i = 0;; ++i) {
const PropertyData* data = m_Properties + i;
if (data->property == XFA_Element::Unknown)
break;
if (data->property == property)
return data;
}
return nullptr;
}
bool CXFA_Node::HasProperty(XFA_Element property) const {
return !!GetPropertyData(property);
}
bool CXFA_Node::HasPropertyFlags(XFA_Element property, uint8_t flags) const {
const PropertyData* data = GetPropertyData(property);
return data && !!(data->flags & flags);
}
uint8_t CXFA_Node::PropertyOccuranceCount(XFA_Element property) const {
const PropertyData* data = GetPropertyData(property);
return data ? data->occurance_count : 0;
}
Optional<XFA_Element> CXFA_Node::GetFirstPropertyWithFlag(uint8_t flag) {
if (m_Properties == nullptr)
return {};
for (size_t i = 0;; ++i) {
const PropertyData* data = m_Properties + i;
if (data->property == XFA_Element::Unknown)
break;
if (data->flags & flag)
return {data->property};
}
return {};
}
const CXFA_Node::AttributeData* CXFA_Node::GetAttributeData(
XFA_Attribute attr) const {
if (m_Attributes == nullptr)
return nullptr;
for (size_t i = 0;; ++i) {
const AttributeData* cur_attr = &m_Attributes[i];
if (cur_attr->attribute == XFA_Attribute::Unknown)
break;
if (cur_attr->attribute == attr)
return cur_attr;
}
return nullptr;
}
bool CXFA_Node::HasAttribute(XFA_Attribute attr) const {
return !!GetAttributeData(attr);
}
// Note: This Method assumes that i is a valid index ....
XFA_Attribute CXFA_Node::GetAttribute(size_t i) const {
if (m_Attributes == nullptr)
return XFA_Attribute::Unknown;
return m_Attributes[i].attribute;
}
XFA_AttributeType CXFA_Node::GetAttributeType(XFA_Attribute type) const {
const AttributeData* data = GetAttributeData(type);
return data ? data->type : XFA_AttributeType::CData;
}
std::vector<CXFA_Node*> CXFA_Node::GetNodeList(uint32_t dwTypeFilter,
XFA_Element eTypeFilter) {
if (eTypeFilter != XFA_Element::Unknown) {
std::vector<CXFA_Node*> nodes;
for (CXFA_Node* pChild = first_child_; pChild;
pChild = pChild->next_sibling_) {
if (pChild->GetElementType() == eTypeFilter)
nodes.push_back(pChild);
}
return nodes;
}
if (dwTypeFilter == (XFA_NODEFILTER_Children | XFA_NODEFILTER_Properties)) {
std::vector<CXFA_Node*> nodes;
for (CXFA_Node* pChild = first_child_; pChild;
pChild = pChild->next_sibling_)
nodes.push_back(pChild);
return nodes;
}
if (dwTypeFilter == 0)
return std::vector<CXFA_Node*>();
bool bFilterChildren = !!(dwTypeFilter & XFA_NODEFILTER_Children);
bool bFilterProperties = !!(dwTypeFilter & XFA_NODEFILTER_Properties);
bool bFilterOneOfProperties = !!(dwTypeFilter & XFA_NODEFILTER_OneOfProperty);
std::vector<CXFA_Node*> nodes;
for (CXFA_Node* pChild = first_child_; pChild;
pChild = pChild->next_sibling_) {
if (!HasProperty(pChild->GetElementType())) {
if (bFilterProperties) {
nodes.push_back(pChild);
} else if (bFilterOneOfProperties &&
HasPropertyFlags(pChild->GetElementType(),
XFA_PROPERTYFLAG_OneOf)) {
nodes.push_back(pChild);
} else if (bFilterChildren &&
(pChild->GetElementType() == XFA_Element::Variables ||
pChild->GetElementType() == XFA_Element::PageSet)) {
nodes.push_back(pChild);
}
} else if (bFilterChildren) {
nodes.push_back(pChild);
}
}
if (!bFilterOneOfProperties || !nodes.empty())
return nodes;
if (m_Properties == nullptr)
return nodes;
Optional<XFA_Element> property =
GetFirstPropertyWithFlag(XFA_PROPERTYFLAG_DefaultOneOf);
if (!property)
return nodes;
CXFA_Node* pNewNode = m_pDocument->CreateNode(GetPacketType(), *property);
if (pNewNode) {
InsertChild(pNewNode, nullptr);
pNewNode->SetFlagAndNotify(XFA_NodeFlag_Initialized);
nodes.push_back(pNewNode);
}
return nodes;
}
CXFA_Node* CXFA_Node::CreateSamePacketNode(XFA_Element eType) {
CXFA_Node* pNode = m_pDocument->CreateNode(m_ePacket, eType);
pNode->SetFlagAndNotify(XFA_NodeFlag_Initialized);
return pNode;
}
CXFA_Node* CXFA_Node::CloneTemplateToForm(bool bRecursive) {
ASSERT(m_ePacket == XFA_PacketType::Template);
CXFA_Node* pClone =
m_pDocument->CreateNode(XFA_PacketType::Form, m_elementType);
if (!pClone)
return nullptr;
pClone->SetTemplateNode(this);
pClone->UpdateNameHash();
pClone->SetXMLMappingNode(GetXMLMappingNode());
if (bRecursive) {
for (CXFA_Node* pChild = GetFirstChild(); pChild;
pChild = pChild->GetNextSibling()) {
pClone->InsertChild(pChild->CloneTemplateToForm(bRecursive), nullptr);
}
}
pClone->SetFlagAndNotify(XFA_NodeFlag_Initialized);
return pClone;
}
CXFA_Node* CXFA_Node::GetTemplateNodeIfExists() const {
return m_pAuxNode;
}
void CXFA_Node::SetTemplateNode(CXFA_Node* pTemplateNode) {
m_pAuxNode = pTemplateNode;
}
CXFA_Node* CXFA_Node::GetBindData() {
ASSERT(GetPacketType() == XFA_PacketType::Form);
return GetBindingNode();
}
std::vector<UnownedPtr<CXFA_Node>>* CXFA_Node::GetBindItems() {
return &binding_nodes_;
}
int32_t CXFA_Node::AddBindItem(CXFA_Node* pFormNode) {
ASSERT(pFormNode);
if (BindsFormItems()) {
bool found = false;
for (auto& v : binding_nodes_) {
if (v.Get() == pFormNode) {
found = true;
break;
}
}
if (!found)
binding_nodes_.emplace_back(pFormNode);
return pdfium::CollectionSize<int32_t>(binding_nodes_);
}
CXFA_Node* pOldFormItem = GetBindingNode();
if (!pOldFormItem) {
SetBindingNode(pFormNode);
return 1;
}
if (pOldFormItem == pFormNode)
return 1;
std::vector<UnownedPtr<CXFA_Node>> items;
items.emplace_back(pOldFormItem);
items.emplace_back(pFormNode);
binding_nodes_ = std::move(items);
m_uNodeFlags |= XFA_NodeFlag_BindFormItems;
return 2;
}
int32_t CXFA_Node::RemoveBindItem(CXFA_Node* pFormNode) {
if (BindsFormItems()) {
auto it = std::find_if(binding_nodes_.begin(), binding_nodes_.end(),
[&pFormNode](const UnownedPtr<CXFA_Node>& node) {
return node.Get() == pFormNode;
});
if (it != binding_nodes_.end())
binding_nodes_.erase(it);
if (binding_nodes_.size() == 1) {
m_uNodeFlags &= ~XFA_NodeFlag_BindFormItems;
return 1;
}
return pdfium::CollectionSize<int32_t>(binding_nodes_);
}
CXFA_Node* pOldFormItem = GetBindingNode();
if (pOldFormItem != pFormNode)
return pOldFormItem ? 1 : 0;
SetBindingNode(nullptr);
return 0;
}
bool CXFA_Node::HasBindItem() {
return GetPacketType() == XFA_PacketType::Datasets && GetBindingNode();
}
CXFA_Node* CXFA_Node::GetContainerNode() {
if (GetPacketType() != XFA_PacketType::Form)
return nullptr;
XFA_Element eType = GetElementType();
if (eType == XFA_Element::ExclGroup)
return nullptr;
CXFA_Node* pParentNode = GetParent();
if (pParentNode && pParentNode->GetElementType() == XFA_Element::ExclGroup)
return nullptr;
if (eType == XFA_Element::Field) {
if (IsChoiceListMultiSelect())
return nullptr;
WideString wsPicture = GetPictureContent(XFA_VALUEPICTURE_DataBind);
if (!wsPicture.IsEmpty())
return this;
CXFA_Node* pDataNode = GetBindData();
if (!pDataNode)
return nullptr;
CXFA_Node* pFieldNode = nullptr;
for (const auto& pFormNode : *(pDataNode->GetBindItems())) {
if (!pFormNode || pFormNode->HasRemovedChildren())
continue;
pFieldNode = pFormNode->IsWidgetReady() ? pFormNode.Get() : nullptr;
if (pFieldNode)
wsPicture = pFieldNode->GetPictureContent(XFA_VALUEPICTURE_DataBind);
if (!wsPicture.IsEmpty())
break;
pFieldNode = nullptr;
}
return pFieldNode;
}
CXFA_Node* pGrandNode = pParentNode ? pParentNode->GetParent() : nullptr;
CXFA_Node* pValueNode =
(pParentNode && pParentNode->GetElementType() == XFA_Element::Value)
? pParentNode
: nullptr;
if (!pValueNode) {
pValueNode =
(pGrandNode && pGrandNode->GetElementType() == XFA_Element::Value)
? pGrandNode
: nullptr;
}
CXFA_Node* pParentOfValueNode =
pValueNode ? pValueNode->GetParent() : nullptr;
return pParentOfValueNode ? pParentOfValueNode->GetContainerNode() : nullptr;
}
LocaleIface* CXFA_Node::GetLocale() {
Optional<WideString> localeName = GetLocaleName();
if (!localeName)
return nullptr;
if (localeName.value() == L"ambient")
return GetDocument()->GetLocalMgr()->GetDefLocale();
return GetDocument()->GetLocalMgr()->GetLocaleByName(localeName.value());
}
Optional<WideString> CXFA_Node::GetLocaleName() {
CXFA_Node* pForm = GetDocument()->GetXFAObject(XFA_HASHCODE_Form)->AsNode();
CXFA_Subform* pTopSubform =
pForm->GetFirstChildByClass<CXFA_Subform>(XFA_Element::Subform);
ASSERT(pTopSubform);
CXFA_Node* pLocaleNode = this;
do {
Optional<WideString> localeName =
pLocaleNode->JSObject()->TryCData(XFA_Attribute::Locale, false);
if (localeName)
return localeName;
pLocaleNode = pLocaleNode->GetParent();
} while (pLocaleNode && pLocaleNode != pTopSubform);
CXFA_Node* pConfig = ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Config));
Optional<WideString> localeName = {
GetDocument()->GetLocalMgr()->GetConfigLocaleName(pConfig)};
if (localeName && !localeName->IsEmpty())
return localeName;
if (pTopSubform) {
localeName =
pTopSubform->JSObject()->TryCData(XFA_Attribute::Locale, false);
if (localeName)
return localeName;
}
LocaleIface* pLocale = GetDocument()->GetLocalMgr()->GetDefLocale();
if (!pLocale)
return {};
return {pLocale->GetName()};
}
XFA_AttributeEnum CXFA_Node::GetIntact() {
CXFA_Keep* pKeep = GetFirstChildByClass<CXFA_Keep>(XFA_Element::Keep);
XFA_AttributeEnum eLayoutType = JSObject()
->TryEnum(XFA_Attribute::Layout, true)
.value_or(XFA_AttributeEnum::Position);
if (pKeep) {
Optional<XFA_AttributeEnum> intact =
pKeep->JSObject()->TryEnum(XFA_Attribute::Intact, false);
if (intact) {
if (*intact == XFA_AttributeEnum::None &&
eLayoutType == XFA_AttributeEnum::Row &&
m_pDocument->GetCurVersionMode() < XFA_VERSION_208) {
CXFA_Node* pPreviewRow = GetPrevContainerSibling();
if (pPreviewRow &&
pPreviewRow->JSObject()->GetEnum(XFA_Attribute::Layout) ==
XFA_AttributeEnum::Row) {
Optional<XFA_AttributeEnum> value =
pKeep->JSObject()->TryEnum(XFA_Attribute::Previous, false);
if (value && (*value == XFA_AttributeEnum::ContentArea ||
*value == XFA_AttributeEnum::PageArea)) {
return XFA_AttributeEnum::ContentArea;
}
CXFA_Keep* pNode =
pPreviewRow->GetFirstChildByClass<CXFA_Keep>(XFA_Element::Keep);
Optional<XFA_AttributeEnum> ret;
if (pNode)
ret = pNode->JSObject()->TryEnum(XFA_Attribute::Next, false);
if (ret && (*ret == XFA_AttributeEnum::ContentArea ||
*ret == XFA_AttributeEnum::PageArea)) {
return XFA_AttributeEnum::ContentArea;
}
}
}
return *intact;
}
}
switch (GetElementType()) {
case XFA_Element::Subform:
switch (eLayoutType) {
case XFA_AttributeEnum::Position:
case XFA_AttributeEnum::Row:
return XFA_AttributeEnum::ContentArea;
default:
return XFA_AttributeEnum::None;
}
case XFA_Element::Field: {
CXFA_Node* parent = GetParent();
if (!parent || parent->GetElementType() == XFA_Element::PageArea)
return XFA_AttributeEnum::ContentArea;
if (parent->GetIntact() != XFA_AttributeEnum::None)
return XFA_AttributeEnum::ContentArea;
XFA_AttributeEnum eParLayout = parent->JSObject()
->TryEnum(XFA_Attribute::Layout, true)
.value_or(XFA_AttributeEnum::Position);
if (eParLayout == XFA_AttributeEnum::Position ||
eParLayout == XFA_AttributeEnum::Row ||
eParLayout == XFA_AttributeEnum::Table) {
return XFA_AttributeEnum::None;
}
XFA_VERSION version = m_pDocument->GetCurVersionMode();
if (eParLayout == XFA_AttributeEnum::Tb && version < XFA_VERSION_208) {
Optional<CXFA_Measurement> measureH =
JSObject()->TryMeasure(XFA_Attribute::H, false);
if (measureH)
return XFA_AttributeEnum::ContentArea;
}
return XFA_AttributeEnum::None;
}
case XFA_Element::Draw:
return XFA_AttributeEnum::ContentArea;
default:
return XFA_AttributeEnum::None;
}
}
CXFA_Node* CXFA_Node::GetDataDescriptionNode() {
if (m_ePacket == XFA_PacketType::Datasets)
return m_pAuxNode;
return nullptr;
}
void CXFA_Node::SetDataDescriptionNode(CXFA_Node* pDataDescriptionNode) {
ASSERT(m_ePacket == XFA_PacketType::Datasets);
m_pAuxNode = pDataDescriptionNode;
}
CXFA_Node* CXFA_Node::GetModelNode() {
switch (GetPacketType()) {
case XFA_PacketType::Xdp:
return m_pDocument->GetRoot();
case XFA_PacketType::Config:
return ToNode(m_pDocument->GetXFAObject(XFA_HASHCODE_Config));
case XFA_PacketType::Template:
return ToNode(m_pDocument->GetXFAObject(XFA_HASHCODE_Template));
case XFA_PacketType::Form:
return ToNode(m_pDocument->GetXFAObject(XFA_HASHCODE_Form));
case XFA_PacketType::Datasets:
return ToNode(m_pDocument->GetXFAObject(XFA_HASHCODE_Datasets));
case XFA_PacketType::LocaleSet:
return ToNode(m_pDocument->GetXFAObject(XFA_HASHCODE_LocaleSet));
case XFA_PacketType::ConnectionSet:
return ToNode(m_pDocument->GetXFAObject(XFA_HASHCODE_ConnectionSet));
case XFA_PacketType::SourceSet:
return ToNode(m_pDocument->GetXFAObject(XFA_HASHCODE_SourceSet));
case XFA_PacketType::Xdc:
return ToNode(m_pDocument->GetXFAObject(XFA_HASHCODE_Xdc));
default:
return this;
}
}
size_t CXFA_Node::CountChildren(XFA_Element eType, bool bOnlyChild) {
size_t count = 0;
for (CXFA_Node* pNode = first_child_; pNode;
pNode = pNode->GetNextSibling()) {
if (pNode->GetElementType() != eType && eType != XFA_Element::Unknown)
continue;
if (bOnlyChild && HasProperty(pNode->GetElementType()))
continue;
++count;
}
return count;
}
CXFA_Node* CXFA_Node::GetChildInternal(size_t index,
XFA_Element eType,
bool bOnlyChild) {
size_t count = 0;
for (CXFA_Node* pNode = first_child_; pNode;
pNode = pNode->GetNextSibling()) {
if (pNode->GetElementType() != eType && eType != XFA_Element::Unknown)
continue;
if (bOnlyChild && HasProperty(pNode->GetElementType()))
continue;
if (count == index)
return pNode;
++count;
}
return nullptr;
}
void CXFA_Node::InsertChild(int32_t index, CXFA_Node* pNode) {
if (!pNode || pNode->parent_ != nullptr) {
PDFIUM_IMMEDIATE_CRASH();
}
pNode->parent_ = this;
pNode->ClearFlag(XFA_NodeFlag_HasRemovedChildren);
if (!first_child_) {
ASSERT(!last_child_);
pNode->prev_sibling_ = nullptr;
pNode->next_sibling_ = nullptr;
first_child_ = pNode;
last_child_ = pNode;
index = 0;
} else if (index == 0) {
pNode->prev_sibling_ = nullptr;
pNode->next_sibling_ = first_child_;
first_child_->prev_sibling_ = pNode;
first_child_ = pNode;
} else if (index < 0) {
pNode->prev_sibling_ = last_child_;
pNode->next_sibling_ = nullptr;
last_child_->next_sibling_ = pNode;
last_child_ = pNode;
} else {
CXFA_Node* pPrev = first_child_;
int32_t count = 0;
while (++count < index && pPrev->next_sibling_)
pPrev = pPrev->next_sibling_;
pNode->prev_sibling_ = pPrev;
pNode->next_sibling_ = pPrev->next_sibling_;
if (pPrev->next_sibling_)
pPrev->next_sibling_->prev_sibling_ = pNode;
pPrev->next_sibling_ = pNode;
// Move the last child pointer if needed.
if (pPrev == last_child_)
last_child_ = pNode;
index = count;
}
CXFA_FFNotify* pNotify = m_pDocument->GetNotify();
if (pNotify)
pNotify->OnChildAdded(this);
if (!IsNeedSavingXMLNode() || !pNode->xml_node_)
return;
ASSERT(!pNode->xml_node_->GetParent());
xml_node_->InsertChildNode(pNode->xml_node_.Get(), index);
}
void CXFA_Node::InsertChild(CXFA_Node* pNode, CXFA_Node* pBeforeNode) {
if (pBeforeNode && pBeforeNode->parent_ != this) {
PDFIUM_IMMEDIATE_CRASH();
}
int32_t index = -1;
if (!first_child_ || pBeforeNode == first_child_) {
index = 0;
} else if (!pBeforeNode) {
index = -1;
} else {
index = 0;
CXFA_Node* prev = first_child_;
while (prev && prev != pBeforeNode) {
prev = prev->next_sibling_;
++index;
}
}
InsertChild(index, pNode);
}
void CXFA_Node::RemoveChild(CXFA_Node* pNode, bool bNotify) {
if (!pNode || pNode->parent_ != this) {
PDFIUM_IMMEDIATE_CRASH();
}
pNode->SetFlag(XFA_NodeFlag_HasRemovedChildren);
if (first_child_ == pNode && last_child_ == pNode) {
first_child_ = nullptr;
last_child_ = nullptr;
} else if (first_child_ == pNode) {
first_child_ = pNode->next_sibling_;
first_child_->prev_sibling_ = nullptr;
} else if (last_child_ == pNode) {
last_child_ = pNode->prev_sibling_;
last_child_->next_sibling_ = nullptr;
} else {
CXFA_Node* pPrev = pNode->prev_sibling_;
pPrev->next_sibling_ = pNode->next_sibling_;
pPrev->next_sibling_->prev_sibling_ = pPrev;
}
pNode->next_sibling_ = nullptr;
pNode->prev_sibling_ = nullptr;
pNode->parent_ = nullptr;
OnRemoved(bNotify);
if (!IsNeedSavingXMLNode() || !pNode->xml_node_)
return;
if (!pNode->IsAttributeInXML()) {
xml_node_->RemoveChildNode(pNode->xml_node_.Get());
return;
}
ASSERT(pNode->xml_node_.Get() == xml_node_.Get() &&
xml_node_->GetType() == FX_XMLNODE_Element);
if (pNode->xml_node_->GetType() == FX_XMLNODE_Element) {
CFX_XMLElement* pXMLElement =
static_cast<CFX_XMLElement*>(pNode->xml_node_.Get());
WideString wsAttributeName =
pNode->JSObject()->GetCData(XFA_Attribute::QualifiedName);
pXMLElement->RemoveAttribute(wsAttributeName.c_str());
}
WideString wsName = pNode->JSObject()
->TryAttribute(XFA_Attribute::Name, false)
.value_or(WideString());
auto* pNewXMLElement = GetDocument()
->GetNotify()
->GetHDOC()
->GetXMLDocument()
->CreateNode<CFX_XMLElement>(wsName);
WideString wsValue = JSObject()->GetCData(XFA_Attribute::Value);
if (!wsValue.IsEmpty()) {
auto* text = GetDocument()
->GetNotify()
->GetHDOC()
->GetXMLDocument()
->CreateNode<CFX_XMLText>(wsValue);
pNewXMLElement->AppendChild(text);
}
pNode->xml_node_ = pNewXMLElement;
pNode->JSObject()->SetEnum(XFA_Attribute::Contains,
XFA_AttributeEnum::Unknown, false);
}
CXFA_Node* CXFA_Node::GetFirstChildByName(const WideStringView& wsName) const {
return GetFirstChildByName(FX_HashCode_GetW(wsName, false));
}
CXFA_Node* CXFA_Node::GetFirstChildByName(uint32_t dwNameHash) const {
for (CXFA_Node* pNode = GetFirstChild(); pNode;
pNode = pNode->GetNextSibling()) {
if (pNode->GetNameHash() == dwNameHash)
return pNode;
}
return nullptr;
}
CXFA_Node* CXFA_Node::GetFirstChildByClassInternal(XFA_Element eType) const {
for (CXFA_Node* pNode = GetFirstChild(); pNode;
pNode = pNode->GetNextSibling()) {
if (pNode->GetElementType() == eType)
return pNode;
}
return nullptr;
}
CXFA_Node* CXFA_Node::GetNextSameNameSibling(uint32_t dwNameHash) const {
for (CXFA_Node* pNode = GetNextSibling(); pNode;
pNode = pNode->GetNextSibling()) {
if (pNode->GetNameHash() == dwNameHash)
return pNode;
}
return nullptr;
}
CXFA_Node* CXFA_Node::GetNextSameNameSiblingInternal(
const WideStringView& wsNodeName) const {
return GetNextSameNameSibling(FX_HashCode_GetW(wsNodeName, false));
}
CXFA_Node* CXFA_Node::GetNextSameClassSiblingInternal(XFA_Element eType) const {
for (CXFA_Node* pNode = GetNextSibling(); pNode;
pNode = pNode->GetNextSibling()) {
if (pNode->GetElementType() == eType)
return pNode;
}
return nullptr;
}
CXFA_Node* CXFA_Node::GetInstanceMgrOfSubform() {
CXFA_Node* pInstanceMgr = nullptr;
if (m_ePacket == XFA_PacketType::Form) {
CXFA_Node* pParentNode = GetParent();
if (!pParentNode || pParentNode->GetElementType() == XFA_Element::Area)
return pInstanceMgr;
for (CXFA_Node* pNode = GetPrevSibling(); pNode;
pNode = pNode->GetPrevSibling()) {
XFA_Element eType = pNode->GetElementType();
if ((eType == XFA_Element::Subform || eType == XFA_Element::SubformSet) &&
pNode->m_dwNameHash != m_dwNameHash) {
break;
}
if (eType == XFA_Element::InstanceManager) {
WideString wsName = JSObject()->GetCData(XFA_Attribute::Name);
WideString wsInstName =
pNode->JSObject()->GetCData(XFA_Attribute::Name);
if (wsInstName.GetLength() > 0 && wsInstName[0] == '_' &&
wsInstName.Right(wsInstName.GetLength() - 1) == wsName) {
pInstanceMgr = pNode;
}
break;
}
}
}
return pInstanceMgr;
}
CXFA_Occur* CXFA_Node::GetOccurIfExists() {
return GetFirstChildByClass<CXFA_Occur>(XFA_Element::Occur);
}
bool CXFA_Node::HasFlag(XFA_NodeFlag dwFlag) const {
if (m_uNodeFlags & dwFlag)
return true;
if (dwFlag == XFA_NodeFlag_HasRemovedChildren)
return parent_ && parent_->HasFlag(dwFlag);
return false;
}
void CXFA_Node::SetFlagAndNotify(uint32_t dwFlag) {
ASSERT(dwFlag == XFA_NodeFlag_Initialized);
if (!IsInitialized()) {
CXFA_FFNotify* pNotify = m_pDocument->GetNotify();
if (pNotify) {
pNotify->OnNodeReady(this);
}
}
m_uNodeFlags |= dwFlag;
}
void CXFA_Node::SetFlag(uint32_t dwFlag) {
m_uNodeFlags |= dwFlag;
}
void CXFA_Node::ClearFlag(uint32_t dwFlag) {
m_uNodeFlags &= ~dwFlag;
}
void CXFA_Node::ReleaseBindingNodes() {
// Clear any binding nodes as we don't necessarily destruct in an order that
// makes sense.
for (auto& node : binding_nodes_)
node.Release();
for (CXFA_Node* pNode = first_child_; pNode; pNode = pNode->next_sibling_)
pNode->ReleaseBindingNodes();
}
bool CXFA_Node::IsAttributeInXML() {
return JSObject()->GetEnum(XFA_Attribute::Contains) ==
XFA_AttributeEnum::MetaData;
}
void CXFA_Node::OnRemoved(bool bNotify) {
if (!bNotify)
return;
CXFA_FFNotify* pNotify = m_pDocument->GetNotify();
if (pNotify)
pNotify->OnChildRemoved();
}
void CXFA_Node::UpdateNameHash() {
WideString wsName = JSObject()->GetCData(XFA_Attribute::Name);
m_dwNameHash = FX_HashCode_GetW(wsName.AsStringView(), false);
}
CFX_XMLNode* CXFA_Node::CreateXMLMappingNode() {
if (!xml_node_) {
xml_node_ = GetDocument()
->GetNotify()
->GetHDOC()
->GetXMLDocument()
->CreateNode<CFX_XMLElement>(
JSObject()->GetCData(XFA_Attribute::Name));
}
return xml_node_.Get();
}
bool CXFA_Node::IsNeedSavingXMLNode() {
return xml_node_ && (GetPacketType() == XFA_PacketType::Datasets ||
GetElementType() == XFA_Element::Xfa);
}
CXFA_Node* CXFA_Node::GetItemIfExists(int32_t iIndex) {
int32_t iCount = 0;
uint32_t dwNameHash = 0;
for (CXFA_Node* pNode = GetNextSibling(); pNode;
pNode = pNode->GetNextSibling()) {
XFA_Element eCurType = pNode->GetElementType();
if (eCurType == XFA_Element::InstanceManager)
break;
if ((eCurType != XFA_Element::Subform) &&
(eCurType != XFA_Element::SubformSet)) {
continue;
}
if (iCount == 0) {
WideString wsName = pNode->JSObject()->GetCData(XFA_Attribute::Name);
WideString wsInstName = JSObject()->GetCData(XFA_Attribute::Name);
if (wsInstName.GetLength() < 1 || wsInstName[0] != '_' ||
wsInstName.Right(wsInstName.GetLength() - 1) != wsName) {
return nullptr;
}
dwNameHash = pNode->GetNameHash();
}
if (dwNameHash != pNode->GetNameHash())
break;
iCount++;
if (iCount > iIndex)
return pNode;
}
return nullptr;
}
int32_t CXFA_Node::GetCount() {
int32_t iCount = 0;
uint32_t dwNameHash = 0;
for (CXFA_Node* pNode = GetNextSibling(); pNode;
pNode = pNode->GetNextSibling()) {
XFA_Element eCurType = pNode->GetElementType();
if (eCurType == XFA_Element::InstanceManager)
break;
if ((eCurType != XFA_Element::Subform) &&
(eCurType != XFA_Element::SubformSet)) {
continue;
}
if (iCount == 0) {
WideString wsName = pNode->JSObject()->GetCData(XFA_Attribute::Name);
WideString wsInstName = JSObject()->GetCData(XFA_Attribute::Name);
if (wsInstName.GetLength() < 1 || wsInstName[0] != '_' ||
wsInstName.Right(wsInstName.GetLength() - 1) != wsName) {
return iCount;
}
dwNameHash = pNode->GetNameHash();
}
if (dwNameHash != pNode->GetNameHash())
break;
iCount++;
}
return iCount;
}
void CXFA_Node::InsertItem(CXFA_Node* pNewInstance,
int32_t iPos,
int32_t iCount,
bool bMoveDataBindingNodes) {
if (iCount < 0)
iCount = GetCount();
if (iPos < 0)
iPos = iCount;
if (iPos == iCount) {
CXFA_Node* item = GetItemIfExists(iCount - 1);
if (!item)
return;
CXFA_Node* pNextSibling =
iCount > 0 ? item->GetNextSibling() : GetNextSibling();
GetParent()->InsertChild(pNewInstance, pNextSibling);
if (bMoveDataBindingNodes) {
std::set<CXFA_Node*> sNew;
std::set<CXFA_Node*> sAfter;
CXFA_NodeIteratorTemplate<CXFA_Node,
CXFA_TraverseStrategy_XFAContainerNode>
sIteratorNew(pNewInstance);
for (CXFA_Node* pNode = sIteratorNew.GetCurrent(); pNode;
pNode = sIteratorNew.MoveToNext()) {
CXFA_Node* pDataNode = pNode->GetBindData();
if (!pDataNode)
continue;
sNew.insert(pDataNode);
}
CXFA_NodeIteratorTemplate<CXFA_Node,
CXFA_TraverseStrategy_XFAContainerNode>
sIteratorAfter(pNextSibling);
for (CXFA_Node* pNode = sIteratorAfter.GetCurrent(); pNode;
pNode = sIteratorAfter.MoveToNext()) {
CXFA_Node* pDataNode = pNode->GetBindData();
if (!pDataNode)
continue;
sAfter.insert(pDataNode);
}
ReorderDataNodes(sNew, sAfter, false);
}
} else {
CXFA_Node* pBeforeInstance = GetItemIfExists(iPos);
if (!pBeforeInstance) {
// TODO(dsinclair): What should happen here?
return;
}
GetParent()->InsertChild(pNewInstance, pBeforeInstance);
if (bMoveDataBindingNodes) {
std::set<CXFA_Node*> sNew;
std::set<CXFA_Node*> sBefore;
CXFA_NodeIteratorTemplate<CXFA_Node,
CXFA_TraverseStrategy_XFAContainerNode>
sIteratorNew(pNewInstance);
for (CXFA_Node* pNode = sIteratorNew.GetCurrent(); pNode;
pNode = sIteratorNew.MoveToNext()) {
CXFA_Node* pDataNode = pNode->GetBindData();
if (!pDataNode)
continue;
sNew.insert(pDataNode);
}
CXFA_NodeIteratorTemplate<CXFA_Node,
CXFA_TraverseStrategy_XFAContainerNode>
sIteratorBefore(pBeforeInstance);
for (CXFA_Node* pNode = sIteratorBefore.GetCurrent(); pNode;
pNode = sIteratorBefore.MoveToNext()) {
CXFA_Node* pDataNode = pNode->GetBindData();
if (!pDataNode)
continue;
sBefore.insert(pDataNode);
}
ReorderDataNodes(sNew, sBefore, true);
}
}
}
void CXFA_Node::RemoveItem(CXFA_Node* pRemoveInstance,
bool bRemoveDataBinding) {
GetParent()->RemoveChild(pRemoveInstance, true);
if (!bRemoveDataBinding)
return;
CXFA_NodeIteratorTemplate<CXFA_Node, CXFA_TraverseStrategy_XFAContainerNode>
sIterator(pRemoveInstance);
for (CXFA_Node* pFormNode = sIterator.GetCurrent(); pFormNode;
pFormNode = sIterator.MoveToNext()) {
CXFA_Node* pDataNode = pFormNode->GetBindData();
if (!pDataNode)
continue;
if (pDataNode->RemoveBindItem(pFormNode) == 0) {
if (CXFA_Node* pDataParent = pDataNode->GetParent()) {
pDataParent->RemoveChild(pDataNode, true);
}
}
pFormNode->SetBindingNode(nullptr);
}
}
CXFA_Node* CXFA_Node::CreateInstanceIfPossible(bool bDataMerge) {
CXFA_Document* pDocument = GetDocument();
CXFA_Node* pTemplateNode = GetTemplateNodeIfExists();
if (!pTemplateNode)
return nullptr;
CXFA_Node* pFormParent = GetParent();
CXFA_Node* pDataScope = nullptr;
for (CXFA_Node* pRootBoundNode = pFormParent;
pRootBoundNode && pRootBoundNode->IsContainerNode();
pRootBoundNode = pRootBoundNode->GetParent()) {
pDataScope = pRootBoundNode->GetBindData();
if (pDataScope)
break;
}
if (!pDataScope) {
pDataScope = ToNode(pDocument->GetXFAObject(XFA_HASHCODE_Record));
ASSERT(pDataScope);
}
CXFA_Node* pInstance = pDocument->DataMerge_CopyContainer(
pTemplateNode, pFormParent, pDataScope, true, bDataMerge, true);
if (pInstance) {
pDocument->DataMerge_UpdateBindingRelations(pInstance);
pFormParent->RemoveChild(pInstance, true);
}
return pInstance;
}
Optional<bool> CXFA_Node::GetDefaultBoolean(XFA_Attribute attr) const {
Optional<void*> value = GetDefaultValue(attr, XFA_AttributeType::Boolean);
if (!value)
return {};
return {!!*value};
}
Optional<int32_t> CXFA_Node::GetDefaultInteger(XFA_Attribute attr) const {
Optional<void*> value = GetDefaultValue(attr, XFA_AttributeType::Integer);
if (!value)
return {};
return {static_cast<int32_t>(reinterpret_cast<uintptr_t>(*value))};
}
Optional<CXFA_Measurement> CXFA_Node::GetDefaultMeasurement(
XFA_Attribute attr) const {
Optional<void*> value = GetDefaultValue(attr, XFA_AttributeType::Measure);
if (!value)
return {};
WideString str = WideString(static_cast<const wchar_t*>(*value));
return {CXFA_Measurement(str.AsStringView())};
}
Optional<WideString> CXFA_Node::GetDefaultCData(XFA_Attribute attr) const {
Optional<void*> value = GetDefaultValue(attr, XFA_AttributeType::CData);
if (!value)
return {};
return {WideString(static_cast<const wchar_t*>(*value))};
}
Optional<XFA_AttributeEnum> CXFA_Node::GetDefaultEnum(
XFA_Attribute attr) const {
Optional<void*> value = GetDefaultValue(attr, XFA_AttributeType::Enum);
if (!value)
return {};
return {static_cast<XFA_AttributeEnum>(reinterpret_cast<uintptr_t>(*value))};
}
Optional<void*> CXFA_Node::GetDefaultValue(XFA_Attribute attr,
XFA_AttributeType eType) const {
const AttributeData* data = GetAttributeData(attr);
if (!data)
return {};
if (data->type == eType)
return {data->default_value};
return {};
}
void CXFA_Node::SendAttributeChangeMessage(XFA_Attribute eAttribute,
bool bScriptModify) {
CXFA_LayoutProcessor* pLayoutPro = GetDocument()->GetLayoutProcessor();
if (!pLayoutPro)
return;
CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
if (!pNotify)
return;
if (GetPacketType() != XFA_PacketType::Form) {
pNotify->OnValueChanged(this, eAttribute, this, this);
return;
}
bool bNeedFindContainer = false;
switch (GetElementType()) {
case XFA_Element::Caption:
bNeedFindContainer = true;
pNotify->OnValueChanged(this, eAttribute, this, GetParent());
break;
case XFA_Element::Font:
case XFA_Element::Para: {
bNeedFindContainer = true;
CXFA_Node* pParentNode = GetParent();
if (pParentNode->GetElementType() == XFA_Element::Caption) {
pNotify->OnValueChanged(this, eAttribute, pParentNode,
pParentNode->GetParent());
} else {
pNotify->OnValueChanged(this, eAttribute, this, pParentNode);
}
break;
}
case XFA_Element::Margin: {
bNeedFindContainer = true;
CXFA_Node* pParentNode = GetParent();
XFA_Element eParentType = pParentNode->GetElementType();
if (pParentNode->IsContainerNode()) {
pNotify->OnValueChanged(this, eAttribute, this, pParentNode);
} else if (eParentType == XFA_Element::Caption) {
pNotify->OnValueChanged(this, eAttribute, pParentNode,
pParentNode->GetParent());
} else {
CXFA_Node* pNode = pParentNode->GetParent();
if (pNode && pNode->GetElementType() == XFA_Element::Ui)
pNotify->OnValueChanged(this, eAttribute, pNode, pNode->GetParent());
}
break;
}
case XFA_Element::Comb: {
CXFA_Node* pEditNode = GetParent();
XFA_Element eUIType = pEditNode->GetElementType();
if (pEditNode && (eUIType == XFA_Element::DateTimeEdit ||
eUIType == XFA_Element::NumericEdit ||
eUIType == XFA_Element::TextEdit)) {
CXFA_Node* pUINode = pEditNode->GetParent();
if (pUINode) {
pNotify->OnValueChanged(this, eAttribute, pUINode,
pUINode->GetParent());
}
}
break;
}
case XFA_Element::Button:
case XFA_Element::Barcode:
case XFA_Element::ChoiceList:
case XFA_Element::DateTimeEdit:
case XFA_Element::NumericEdit:
case XFA_Element::PasswordEdit:
case XFA_Element::TextEdit: {
CXFA_Node* pUINode = GetParent();
if (pUINode) {
pNotify->OnValueChanged(this, eAttribute, pUINode,
pUINode->GetParent());
}
break;
}
case XFA_Element::CheckButton: {
bNeedFindContainer = true;
CXFA_Node* pUINode = GetParent();
if (pUINode) {
pNotify->OnValueChanged(this, eAttribute, pUINode,
pUINode->GetParent());
}
break;
}
case XFA_Element::Keep:
case XFA_Element::Bookend:
case XFA_Element::Break:
case XFA_Element::BreakAfter:
case XFA_Element::BreakBefore:
case XFA_Element::Overflow:
bNeedFindContainer = true;
break;
case XFA_Element::Area:
case XFA_Element::Draw:
case XFA_Element::ExclGroup:
case XFA_Element::Field:
case XFA_Element::Subform:
case XFA_Element::SubformSet:
pLayoutPro->AddChangedContainer(this);
pNotify->OnValueChanged(this, eAttribute, this, this);
break;
case XFA_Element::Sharptext:
case XFA_Element::Sharpxml:
case XFA_Element::SharpxHTML: {
CXFA_Node* pTextNode = GetParent();
if (!pTextNode)
return;
CXFA_Node* pValueNode = pTextNode->GetParent();
if (!pValueNode)
return;
XFA_Element eType = pValueNode->GetElementType();
if (eType == XFA_Element::Value) {
bNeedFindContainer = true;
CXFA_Node* pNode = pValueNode->GetParent();
if (pNode && pNode->IsContainerNode()) {
if (bScriptModify)
pValueNode = pNode;
pNotify->OnValueChanged(this, eAttribute, pValueNode, pNode);
} else {
pNotify->OnValueChanged(this, eAttribute, pNode, pNode->GetParent());
}
} else {
if (eType == XFA_Element::Items) {
CXFA_Node* pNode = pValueNode->GetParent();
if (pNode && pNode->IsContainerNode()) {
pNotify->OnValueChanged(this, eAttribute, pValueNode, pNode);
}
}
}
break;
}
default:
break;
}
if (!bNeedFindContainer)
return;
CXFA_Node* pParent = this;
while (pParent && !pParent->IsContainerNode())
pParent = pParent->GetParent();
if (pParent)
pLayoutPro->AddChangedContainer(pParent);
}
void CXFA_Node::SyncValue(const WideString& wsValue, bool bNotify) {
WideString wsFormatValue = wsValue;
CXFA_Node* pContainerNode = GetContainerNode();
if (pContainerNode)
wsFormatValue = pContainerNode->GetFormatDataValue(wsValue);
JSObject()->SetContent(wsValue, wsFormatValue, bNotify, false, true);
}
WideString CXFA_Node::GetRawValue() {
return JSObject()->GetContent(false);
}
int32_t CXFA_Node::GetRotate() {
Optional<int32_t> degrees =
JSObject()->TryInteger(XFA_Attribute::Rotate, false);
return degrees ? XFA_MapRotation(*degrees) / 90 * 90 : 0;
}
CXFA_Border* CXFA_Node::GetBorderIfExists() const {
return JSObject()->GetProperty<CXFA_Border>(0, XFA_Element::Border);
}
CXFA_Border* CXFA_Node::GetOrCreateBorderIfPossible() {
return JSObject()->GetOrCreateProperty<CXFA_Border>(0, XFA_Element::Border);
}
CXFA_Caption* CXFA_Node::GetCaptionIfExists() const {
return JSObject()->GetProperty<CXFA_Caption>(0, XFA_Element::Caption);
}
CXFA_Font* CXFA_Node::GetOrCreateFontIfPossible() {
return JSObject()->GetOrCreateProperty<CXFA_Font>(0, XFA_Element::Font);
}
CXFA_Font* CXFA_Node::GetFontIfExists() const {
return JSObject()->GetProperty<CXFA_Font>(0, XFA_Element::Font);
}
float CXFA_Node::GetFontSize() const {
CXFA_Font* font = GetFontIfExists();
float fFontSize = font ? font->GetFontSize() : 10.0f;
return fFontSize < 0.1f ? 10.0f : fFontSize;
}
float CXFA_Node::GetLineHeight() const {
float fLineHeight = 0;
CXFA_Para* para = GetParaIfExists();
if (para)
fLineHeight = para->GetLineHeight();
if (fLineHeight < 1)
fLineHeight = GetFontSize() * 1.2f;
return fLineHeight;
}
FX_ARGB CXFA_Node::GetTextColor() const {
CXFA_Font* font = GetFontIfExists();
return font ? font->GetColor() : 0xFF000000;
}
CXFA_Margin* CXFA_Node::GetMarginIfExists() const {
return JSObject()->GetProperty<CXFA_Margin>(0, XFA_Element::Margin);
}
CXFA_Para* CXFA_Node::GetParaIfExists() const {
return JSObject()->GetProperty<CXFA_Para>(0, XFA_Element::Para);
}
bool CXFA_Node::IsOpenAccess() {
for (auto* pNode = this; pNode; pNode = pNode->GetContainerParent()) {
XFA_AttributeEnum iAcc = pNode->JSObject()->GetEnum(XFA_Attribute::Access);
if (iAcc != XFA_AttributeEnum::Open)
return false;
}
return true;
}
CXFA_Value* CXFA_Node::GetDefaultValueIfExists() {
CXFA_Node* pTemNode = GetTemplateNodeIfExists();
return pTemNode ? pTemNode->JSObject()->GetProperty<CXFA_Value>(
0, XFA_Element::Value)
: nullptr;
}
CXFA_Value* CXFA_Node::GetFormValueIfExists() const {
return JSObject()->GetProperty<CXFA_Value>(0, XFA_Element::Value);
}
CXFA_Calculate* CXFA_Node::GetCalculateIfExists() const {
return JSObject()->GetProperty<CXFA_Calculate>(0, XFA_Element::Calculate);
}
CXFA_Validate* CXFA_Node::GetValidateIfExists() const {
return JSObject()->GetProperty<CXFA_Validate>(0, XFA_Element::Validate);
}
CXFA_Validate* CXFA_Node::GetOrCreateValidateIfPossible() {
return JSObject()->GetOrCreateProperty<CXFA_Validate>(0,
XFA_Element::Validate);
}
CXFA_Bind* CXFA_Node::GetBindIfExists() const {
return JSObject()->GetProperty<CXFA_Bind>(0, XFA_Element::Bind);
}
Optional<float> CXFA_Node::TryWidth() {
return JSObject()->TryMeasureAsFloat(XFA_Attribute::W);
}
Optional<float> CXFA_Node::TryHeight() {
return JSObject()->TryMeasureAsFloat(XFA_Attribute::H);
}
Optional<float> CXFA_Node::TryMinWidth() {
return JSObject()->TryMeasureAsFloat(XFA_Attribute::MinW);
}
Optional<float> CXFA_Node::TryMinHeight() {
return JSObject()->TryMeasureAsFloat(XFA_Attribute::MinH);
}
Optional<float> CXFA_Node::TryMaxWidth() {
return JSObject()->TryMeasureAsFloat(XFA_Attribute::MaxW);
}
Optional<float> CXFA_Node::TryMaxHeight() {
return JSObject()->TryMeasureAsFloat(XFA_Attribute::MaxH);
}
CXFA_Node* CXFA_Node::GetExclGroupIfExists() {
CXFA_Node* pExcl = GetParent();
if (!pExcl || pExcl->GetElementType() != XFA_Element::ExclGroup)
return nullptr;
return pExcl;
}
int32_t CXFA_Node::ProcessEvent(CXFA_FFDocView* docView,
XFA_AttributeEnum iActivity,
CXFA_EventParam* pEventParam) {
if (GetElementType() == XFA_Element::Draw)
return XFA_EVENTERROR_NotExist;
std::vector<CXFA_Event*> eventArray =
GetEventByActivity(iActivity, pEventParam->m_bIsFormReady);
bool first = true;
int32_t iRet = XFA_EVENTERROR_NotExist;
for (CXFA_Event* event : eventArray) {
int32_t result = ProcessEvent(docView, event, pEventParam);
if (first || result == XFA_EVENTERROR_Success)
iRet = result;
first = false;
}
return iRet;
}
int32_t CXFA_Node::ProcessEvent(CXFA_FFDocView* docView,
CXFA_Event* event,
CXFA_EventParam* pEventParam) {
if (!event)
return XFA_EVENTERROR_NotExist;
switch (event->GetEventType()) {
case XFA_Element::Execute:
break;
case XFA_Element::Script:
return ExecuteScript(docView, event->GetScriptIfExists(), pEventParam);
case XFA_Element::SignData:
break;
case XFA_Element::Submit: {
CXFA_Submit* submit = event->GetSubmitIfExists();
if (!submit)
return XFA_EVENTERROR_NotExist;
return docView->GetDoc()->GetDocEnvironment()->Submit(docView->GetDoc(),
submit);
}
default:
break;
}
return XFA_EVENTERROR_NotExist;
}
int32_t CXFA_Node::ProcessCalculate(CXFA_FFDocView* docView) {
if (GetElementType() == XFA_Element::Draw)
return XFA_EVENTERROR_NotExist;
CXFA_Calculate* calc = GetCalculateIfExists();
if (!calc)
return XFA_EVENTERROR_NotExist;
if (IsUserInteractive())
return XFA_EVENTERROR_Disabled;
CXFA_EventParam EventParam;
EventParam.m_eType = XFA_EVENT_Calculate;
int32_t iRet = ExecuteScript(docView, calc->GetScriptIfExists(), &EventParam);
if (iRet != XFA_EVENTERROR_Success)
return iRet;
if (GetRawValue() != EventParam.m_wsResult) {
SetValue(XFA_VALUEPICTURE_Raw, EventParam.m_wsResult);
UpdateUIDisplay(docView, nullptr);
}
return XFA_EVENTERROR_Success;
}
void CXFA_Node::ProcessScriptTestValidate(CXFA_FFDocView* docView,
CXFA_Validate* validate,
int32_t iRet,
bool bRetValue,
bool bVersionFlag) {
if (iRet != XFA_EVENTERROR_Success)
return;
if (bRetValue)
return;
IXFA_AppProvider* pAppProvider =
docView->GetDoc()->GetApp()->GetAppProvider();
if (!pAppProvider)
return;
WideString wsTitle = pAppProvider->GetAppTitle();
WideString wsScriptMsg = validate->GetScriptMessageText();
if (validate->GetScriptTest() == XFA_AttributeEnum::Warning) {
if (IsUserInteractive())
return;
if (wsScriptMsg.IsEmpty())
wsScriptMsg = GetValidateMessage(false, bVersionFlag);
if (bVersionFlag) {
pAppProvider->MsgBox(wsScriptMsg, wsTitle, XFA_MBICON_Warning, XFA_MB_OK);
return;
}
if (pAppProvider->MsgBox(wsScriptMsg, wsTitle, XFA_MBICON_Warning,
XFA_MB_YesNo) == XFA_IDYes) {
SetFlag(XFA_NodeFlag_UserInteractive);
}
return;
}
if (wsScriptMsg.IsEmpty())
wsScriptMsg = GetValidateMessage(true, bVersionFlag);
pAppProvider->MsgBox(wsScriptMsg, wsTitle, XFA_MBICON_Error, XFA_MB_OK);
}
int32_t CXFA_Node::ProcessFormatTestValidate(CXFA_FFDocView* docView,
CXFA_Validate* validate,
bool bVersionFlag) {
WideString wsRawValue = GetRawValue();
if (!wsRawValue.IsEmpty()) {
WideString wsPicture = validate->GetPicture();
if (wsPicture.IsEmpty())
return XFA_EVENTERROR_NotExist;
LocaleIface* pLocale = GetLocale();
if (!pLocale)
return XFA_EVENTERROR_NotExist;
CXFA_LocaleValue lcValue = XFA_GetLocaleValue(this);
if (!lcValue.ValidateValue(lcValue.GetValue(), wsPicture, pLocale,
nullptr)) {
IXFA_AppProvider* pAppProvider =
docView->GetDoc()->GetApp()->GetAppProvider();
if (!pAppProvider)
return XFA_EVENTERROR_NotExist;
WideString wsFormatMsg = validate->GetFormatMessageText();
WideString wsTitle = pAppProvider->GetAppTitle();
if (validate->GetFormatTest() == XFA_AttributeEnum::Error) {
if (wsFormatMsg.IsEmpty())
wsFormatMsg = GetValidateMessage(true, bVersionFlag);
pAppProvider->MsgBox(wsFormatMsg, wsTitle, XFA_MBICON_Error, XFA_MB_OK);
return XFA_EVENTERROR_Success;
}
if (IsUserInteractive())
return XFA_EVENTERROR_NotExist;
if (wsFormatMsg.IsEmpty())
wsFormatMsg = GetValidateMessage(false, bVersionFlag);
if (bVersionFlag) {
pAppProvider->MsgBox(wsFormatMsg, wsTitle, XFA_MBICON_Warning,
XFA_MB_OK);
return XFA_EVENTERROR_Success;
}
if (pAppProvider->MsgBox(wsFormatMsg, wsTitle, XFA_MBICON_Warning,
XFA_MB_YesNo) == XFA_IDYes) {
SetFlag(XFA_NodeFlag_UserInteractive);
}
return XFA_EVENTERROR_Success;
}
}
return XFA_EVENTERROR_NotExist;
}
int32_t CXFA_Node::ProcessNullTestValidate(CXFA_FFDocView* docView,
CXFA_Validate* validate,
int32_t iFlags,
bool bVersionFlag) {
if (!GetValue(XFA_VALUEPICTURE_Raw).IsEmpty())
return XFA_EVENTERROR_Success;
if (m_bIsNull && m_bPreNull)
return XFA_EVENTERROR_Success;
XFA_AttributeEnum eNullTest = validate->GetNullTest();
WideString wsNullMsg = validate->GetNullMessageText();
if (iFlags & 0x01) {
int32_t iRet = XFA_EVENTERROR_Success;
if (eNullTest != XFA_AttributeEnum::Disabled)
iRet = XFA_EVENTERROR_Error;
if (!wsNullMsg.IsEmpty()) {
if (eNullTest != XFA_AttributeEnum::Disabled) {
docView->m_arrNullTestMsg.push_back(wsNullMsg);
return XFA_EVENTERROR_Error;
}
return XFA_EVENTERROR_Success;
}
return iRet;
}
if (wsNullMsg.IsEmpty() && bVersionFlag &&
eNullTest != XFA_AttributeEnum::Disabled) {
return XFA_EVENTERROR_Error;
}
IXFA_AppProvider* pAppProvider =
docView->GetDoc()->GetApp()->GetAppProvider();
if (!pAppProvider)
return XFA_EVENTERROR_NotExist;
WideString wsCaptionName;
WideString wsTitle = pAppProvider->GetAppTitle();
switch (eNullTest) {
case XFA_AttributeEnum::Error: {
if (wsNullMsg.IsEmpty()) {
wsCaptionName = GetValidateCaptionName(bVersionFlag);
wsNullMsg =
WideString::Format(L"%ls cannot be blank.", wsCaptionName.c_str());
}
pAppProvider->MsgBox(wsNullMsg, wsTitle, XFA_MBICON_Status, XFA_MB_OK);
return XFA_EVENTERROR_Error;
}
case XFA_AttributeEnum::Warning: {
if (IsUserInteractive())
return true;
if (wsNullMsg.IsEmpty()) {
wsCaptionName = GetValidateCaptionName(bVersionFlag);
wsNullMsg = WideString::Format(
L"%ls cannot be blank. To ignore validations for %ls, click "
L"Ignore.",
wsCaptionName.c_str(), wsCaptionName.c_str());
}
if (pAppProvider->MsgBox(wsNullMsg, wsTitle, XFA_MBICON_Warning,
XFA_MB_YesNo) == XFA_IDYes) {
SetFlag(XFA_NodeFlag_UserInteractive);
}
return XFA_EVENTERROR_Error;
}
case XFA_AttributeEnum::Disabled:
default:
break;
}
return XFA_EVENTERROR_Success;
}
int32_t CXFA_Node::ProcessValidate(CXFA_FFDocView* docView, int32_t iFlags) {
if (GetElementType() == XFA_Element::Draw)
return XFA_EVENTERROR_NotExist;
CXFA_Validate* validate = GetValidateIfExists();
if (!validate)
return XFA_EVENTERROR_NotExist;
bool bInitDoc = validate->NeedsInitApp();
bool bStatus = docView->GetLayoutStatus() < XFA_DOCVIEW_LAYOUTSTATUS_End;
int32_t iFormat = 0;
int32_t iRet = XFA_EVENTERROR_NotExist;
CXFA_Script* script = validate->GetScriptIfExists();
bool bRet = false;
bool hasBoolResult = (bInitDoc || bStatus) && GetRawValue().IsEmpty();
if (script) {
CXFA_EventParam eParam;
eParam.m_eType = XFA_EVENT_Validate;
eParam.m_pTarget = this;
std::tie(iRet, bRet) = ExecuteBoolScript(docView, script, &eParam);
}
XFA_VERSION version = docView->GetDoc()->GetXFADoc()->GetCurVersionMode();
bool bVersionFlag = false;
if (version < XFA_VERSION_208)
bVersionFlag = true;
if (bInitDoc) {
validate->ClearFlag(XFA_NodeFlag_NeedsInitApp);
} else {
iFormat = ProcessFormatTestValidate(docView, validate, bVersionFlag);
if (!bVersionFlag) {
bVersionFlag =
docView->GetDoc()->GetXFADoc()->HasFlag(XFA_DOCFLAG_Scripting);
}
iRet |= ProcessNullTestValidate(docView, validate, iFlags, bVersionFlag);
}
if (iFormat != XFA_EVENTERROR_Success && hasBoolResult)
ProcessScriptTestValidate(docView, validate, iRet, bRet, bVersionFlag);
return iRet | iFormat;
}
WideString CXFA_Node::GetValidateCaptionName(bool bVersionFlag) {
WideString wsCaptionName;
if (!bVersionFlag) {
CXFA_Caption* caption = GetCaptionIfExists();
if (caption) {
CXFA_Value* capValue = caption->GetValueIfExists();
if (capValue) {
CXFA_Text* captionText = capValue->GetTextIfExists();
if (captionText)
wsCaptionName = captionText->GetContent();
}
}
}
if (!wsCaptionName.IsEmpty())
return wsCaptionName;
return JSObject()->GetCData(XFA_Attribute::Name);
}
WideString CXFA_Node::GetValidateMessage(bool bError, bool bVersionFlag) {
WideString wsCaptionName = GetValidateCaptionName(bVersionFlag);
if (bVersionFlag)
return WideString::Format(L"%ls validation failed", wsCaptionName.c_str());
if (bError) {
return WideString::Format(L"The value you entered for %ls is invalid.",
wsCaptionName.c_str());
}
return WideString::Format(
L"The value you entered for %ls is invalid. To ignore "
L"validations for %ls, click Ignore.",
wsCaptionName.c_str(), wsCaptionName.c_str());
}
int32_t CXFA_Node::ExecuteScript(CXFA_FFDocView* docView,
CXFA_Script* script,
CXFA_EventParam* pEventParam) {
bool bRet;
int32_t iRet;
std::tie(iRet, bRet) = ExecuteBoolScript(docView, script, pEventParam);
return iRet;
}
std::pair<int32_t, bool> CXFA_Node::ExecuteBoolScript(
CXFA_FFDocView* docView,
CXFA_Script* script,
CXFA_EventParam* pEventParam) {
if (m_ExecuteRecursionDepth > kMaxExecuteRecursion)
return {XFA_EVENTERROR_Success, false};
ASSERT(pEventParam);
if (!script)
return {XFA_EVENTERROR_NotExist, false};
if (script->GetRunAt() == XFA_AttributeEnum::Server)
return {XFA_EVENTERROR_Disabled, false};
WideString wsExpression = script->GetExpression();
if (wsExpression.IsEmpty())
return {XFA_EVENTERROR_NotExist, false};
CXFA_Script::Type eScriptType = script->GetContentType();
if (eScriptType == CXFA_Script::Type::Unknown)
return {XFA_EVENTERROR_Success, false};
CXFA_FFDoc* pDoc = docView->GetDoc();
CFXJSE_Engine* pContext = pDoc->GetXFADoc()->GetScriptContext();
pContext->SetEventParam(pEventParam);
pContext->SetRunAtType(script->GetRunAt());
std::vector<CXFA_Node*> refNodes;
if (pEventParam->m_eType == XFA_EVENT_InitCalculate ||
pEventParam->m_eType == XFA_EVENT_Calculate) {
pContext->SetNodesOfRunScript(&refNodes);
}
auto pTmpRetValue = pdfium::MakeUnique<CFXJSE_Value>(pContext->GetIsolate());
bool bRet = false;
{
AutoRestorer<uint8_t> restorer(&m_ExecuteRecursionDepth);
++m_ExecuteRecursionDepth;
bRet = pContext->RunScript(eScriptType, wsExpression.AsStringView(),
pTmpRetValue.get(), this);
}
int32_t iRet = XFA_EVENTERROR_Error;
if (bRet) {
iRet = XFA_EVENTERROR_Success;
if (pEventParam->m_eType == XFA_EVENT_Calculate ||
pEventParam->m_eType == XFA_EVENT_InitCalculate) {
if (!pTmpRetValue->IsUndefined()) {
if (!pTmpRetValue->IsNull())
pEventParam->m_wsResult = pTmpRetValue->ToWideString();
iRet = XFA_EVENTERROR_Success;
} else {
iRet = XFA_EVENTERROR_Error;
}
if (pEventParam->m_eType == XFA_EVENT_InitCalculate) {
if ((iRet == XFA_EVENTERROR_Success) &&
(GetRawValue() != pEventParam->m_wsResult)) {
SetValue(XFA_VALUEPICTURE_Raw, pEventParam->m_wsResult);
docView->AddValidateNode(this);
}
}
for (CXFA_Node* pRefNode : refNodes) {
if (pRefNode == this)
continue;
CXFA_CalcData* pGlobalData = pRefNode->JSObject()->GetCalcData();
if (!pGlobalData) {
pRefNode->JSObject()->SetCalcData(
pdfium::MakeUnique<CXFA_CalcData>());
pGlobalData = pRefNode->JSObject()->GetCalcData();
}
if (!pdfium::ContainsValue(pGlobalData->m_Globals, this))
pGlobalData->m_Globals.push_back(this);
}
}
}
pContext->SetNodesOfRunScript(nullptr);
pContext->SetEventParam(nullptr);
return {iRet, pTmpRetValue->IsBoolean() ? pTmpRetValue->ToBoolean() : false};
}
std::pair<XFA_FFWidgetType, CXFA_Ui*>
CXFA_Node::CreateChildUIAndValueNodesIfNeeded() {
XFA_Element eType = GetElementType();
ASSERT(eType == XFA_Element::Field || eType == XFA_Element::Draw);
// Both Field and Draw have a UI property. We should always be able to
// retrieve or create the UI element. If we can't something is wrong.
CXFA_Ui* pUI = JSObject()->GetOrCreateProperty<CXFA_Ui>(0, XFA_Element::Ui);
ASSERT(pUI);
CXFA_Node* pUIChild = nullptr;
// Search through the children of the UI node to see if we have any of our
// One-Of entries. If so, that is the node associated with our UI.
for (CXFA_Node* pChild = pUI->GetFirstChild(); pChild;
pChild = pChild->GetNextSibling()) {
if (pUI->IsAOneOfChild(pChild)) {
pUIChild = pChild;
break;
}
}
XFA_FFWidgetType widget_type = XFA_FFWidgetType::kNone;
XFA_Element expected_ui_child_type = XFA_Element::Unknown;
// Both Field and Draw nodes have a Value child. So, we should either always
// have it, or always create it. If we don't get the Value child for some
// reason something has gone really wrong.
CXFA_Value* value =
JSObject()->GetOrCreateProperty<CXFA_Value>(0, XFA_Element::Value);
ASSERT(value);
// The Value nodes only have One-Of children. So, if we have a first child
// that child must be the type we want to use.
CXFA_Node* child = value->GetFirstChild();
if (child) {
switch (child->GetElementType()) {
case XFA_Element::Boolean:
expected_ui_child_type = XFA_Element::CheckButton;
break;
case XFA_Element::Integer:
case XFA_Element::Decimal:
case XFA_Element::Float:
expected_ui_child_type = XFA_Element::NumericEdit;
break;
case XFA_Element::ExData:
case XFA_Element::Text:
expected_ui_child_type = XFA_Element::TextEdit;
widget_type = XFA_FFWidgetType::kText;
break;
case XFA_Element::Date:
case XFA_Element::Time:
case XFA_Element::DateTime:
expected_ui_child_type = XFA_Element::DateTimeEdit;
break;
case XFA_Element::Image:
expected_ui_child_type = XFA_Element::ImageEdit;
widget_type = XFA_FFWidgetType::kImage;
break;
case XFA_Element::Arc:
expected_ui_child_type = XFA_Element::DefaultUi;
widget_type = XFA_FFWidgetType::kArc;
break;
case XFA_Element::Line:
expected_ui_child_type = XFA_Element::DefaultUi;
widget_type = XFA_FFWidgetType::kLine;
break;
case XFA_Element::Rectangle:
expected_ui_child_type = XFA_Element::DefaultUi;
widget_type = XFA_FFWidgetType::kRectangle;
break;
default:
NOTREACHED();
break;
}
}
if (eType == XFA_Element::Draw) {
if (pUIChild && pUIChild->GetElementType() == XFA_Element::TextEdit) {
widget_type = XFA_FFWidgetType::kText;
} else if (pUIChild &&
pUIChild->GetElementType() == XFA_Element::ImageEdit) {
widget_type = XFA_FFWidgetType::kImage;
} else if (widget_type == XFA_FFWidgetType::kNone) {
widget_type = XFA_FFWidgetType::kText;
}
} else if (eType == XFA_Element::Field) {
if (pUIChild && pUIChild->GetElementType() == XFA_Element::DefaultUi) {
widget_type = XFA_FFWidgetType::kTextEdit;
} else if (pUIChild) {
widget_type = pUIChild->GetDefaultFFWidgetType();
} else if (expected_ui_child_type == XFA_Element::Unknown) {
widget_type = XFA_FFWidgetType::kTextEdit;
}
} else {
NOTREACHED();
}
if (!pUIChild) {
if (expected_ui_child_type == XFA_Element::Unknown)
expected_ui_child_type = XFA_Element::TextEdit;
pUIChild = pUI->JSObject()->GetOrCreateProperty<CXFA_Node>(
0, expected_ui_child_type);
}
CreateValueNodeIfNeeded(value, pUIChild);
return {widget_type, pUI};
}
XFA_FFWidgetType CXFA_Node::GetDefaultFFWidgetType() const {
NOTREACHED();
return XFA_FFWidgetType::kNone;
}
CXFA_Node* CXFA_Node::CreateUINodeIfNeeded(CXFA_Ui* ui, XFA_Element type) {
return ui->JSObject()->GetOrCreateProperty<CXFA_Node>(0, type);
}
void CXFA_Node::CreateValueNodeIfNeeded(CXFA_Value* value,
CXFA_Node* pUIChild) {
// Value nodes only have one child. If we have one already we're done.
if (value->GetFirstChild() != nullptr)
return;
// Create the Value node for our UI if needed.
XFA_Element valueType = pUIChild->GetValueNodeType();
if (pUIChild->GetElementType() == XFA_Element::CheckButton) {
CXFA_Items* pItems = GetChild<CXFA_Items>(0, XFA_Element::Items, false);
if (pItems) {
CXFA_Node* pItem =
pItems->GetChild<CXFA_Node>(0, XFA_Element::Unknown, false);
if (pItem)
valueType = pItem->GetElementType();
}
}
value->JSObject()->GetOrCreateProperty<CXFA_Node>(0, valueType);
}
XFA_Element CXFA_Node::GetValueNodeType() const {
return XFA_Element::Text;
}
CXFA_Node* CXFA_Node::GetUIChildNode() {
ASSERT(HasCreatedUIWidget());
if (ff_widget_type_ != XFA_FFWidgetType::kNone)
return ui_ ? ui_->GetFirstChild() : nullptr;
XFA_Element type = GetElementType();
if (type == XFA_Element::Field || type == XFA_Element::Draw) {
std::tie(ff_widget_type_, ui_) = CreateChildUIAndValueNodesIfNeeded();
} else if (type == XFA_Element::Subform) {
ff_widget_type_ = XFA_FFWidgetType::kSubform;
} else if (type == XFA_Element::ExclGroup) {
ff_widget_type_ = XFA_FFWidgetType::kExclGroup;
} else {
NOTREACHED();
}
return ui_ ? ui_->GetFirstChild() : nullptr;
}
XFA_FFWidgetType CXFA_Node::GetFFWidgetType() {
GetUIChildNode();
return ff_widget_type_;
}
CXFA_Border* CXFA_Node::GetUIBorder() {
CXFA_Node* pUIChild = GetUIChildNode();
return pUIChild ? pUIChild->JSObject()->GetProperty<CXFA_Border>(
0, XFA_Element::Border)
: nullptr;
}
CFX_RectF CXFA_Node::GetUIMargin() {
CXFA_Node* pUIChild = GetUIChildNode();
if (!pUIChild)
return CFX_RectF();
CXFA_Margin* mgUI =
pUIChild->JSObject()->GetProperty<CXFA_Margin>(0, XFA_Element::Margin);
if (!mgUI)
return CFX_RectF();