| // 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(); |
| |
| CXFA_Border* border = GetUIBorder(); |
| if (border && border->GetPresence() != XFA_AttributeEnum::Visible) |
| return CFX_RectF(); |
| |
| Optional<float> left = mgUI->TryLeftInset(); |
| Optional<float> top = mgUI->TryTopInset(); |
| Optional<float> right = mgUI->TryRightInset(); |
| Optional<float> bottom = mgUI->TryBottomInset(); |
| if (border) { |
| bool bVisible = false; |
| float fThickness = 0; |
| XFA_AttributeEnum iType = XFA_AttributeEnum::Unknown; |
| std::tie(iType, bVisible, fThickness) = border->Get3DStyle(); |
| if (!left || !top || !right || !bottom) { |
| std::vector<CXFA_Stroke*> strokes = border->GetStrokes(); |
| if (!top) |
| top = GetEdgeThickness(strokes, bVisible, 0); |
| if (!right) |
| right = GetEdgeThickness(strokes, bVisible, 1); |
| if (!bottom) |
| bottom = GetEdgeThickness(strokes, bVisible, 2); |
| if (!left) |
| left = GetEdgeThickness(strokes, bVisible, 3); |
| } |
| } |
| return CFX_RectF(left.value_or(0.0), top.value_or(0.0), right.value_or(0.0), |
| bottom.value_or(0.0)); |
| } |
| |
| std::vector<CXFA_Event*> CXFA_Node::GetEventByActivity( |
| XFA_AttributeEnum iActivity, |
| bool bIsFormReady) { |
| std::vector<CXFA_Event*> events; |
| for (CXFA_Node* node : GetNodeList(0, XFA_Element::Event)) { |
| auto* event = static_cast<CXFA_Event*>(node); |
| if (event->GetActivity() != iActivity) |
| continue; |
| |
| if (iActivity != XFA_AttributeEnum::Ready) { |
| events.push_back(event); |
| continue; |
| } |
| |
| WideString wsRef = event->GetRef(); |
| if (bIsFormReady) { |
| if (wsRef == WideStringView(L"$form")) |
| events.push_back(event); |
| continue; |
| } |
| |
| if (wsRef == WideStringView(L"$layout")) |
| events.push_back(event); |
| } |
| return events; |
| } |
| |
| void CXFA_Node::ResetData() { |
| WideString wsValue; |
| switch (GetFFWidgetType()) { |
| case XFA_FFWidgetType::kImageEdit: { |
| CXFA_Value* imageValue = GetDefaultValueIfExists(); |
| CXFA_Image* image = imageValue ? imageValue->GetImageIfExists() : nullptr; |
| WideString wsContentType, wsHref; |
| if (image) { |
| wsValue = image->GetContent(); |
| wsContentType = image->GetContentType(); |
| wsHref = image->GetHref(); |
| } |
| SetImageEdit(wsContentType, wsHref, wsValue); |
| break; |
| } |
| case XFA_FFWidgetType::kExclGroup: { |
| CXFA_Node* pNextChild = GetFirstContainerChild(); |
| while (pNextChild) { |
| CXFA_Node* pChild = pNextChild; |
| if (!pChild->IsWidgetReady()) |
| continue; |
| |
| bool done = false; |
| if (wsValue.IsEmpty()) { |
| CXFA_Value* defValue = pChild->GetDefaultValueIfExists(); |
| if (defValue) { |
| wsValue = defValue->GetChildValueContent(); |
| SetValue(XFA_VALUEPICTURE_Raw, wsValue); |
| pChild->SetValue(XFA_VALUEPICTURE_Raw, wsValue); |
| done = true; |
| } |
| } |
| if (!done) { |
| CXFA_Items* pItems = |
| pChild->GetChild<CXFA_Items>(0, XFA_Element::Items, false); |
| if (!pItems) |
| continue; |
| |
| WideString itemText; |
| if (pItems->CountChildren(XFA_Element::Unknown, false) > 1) { |
| itemText = |
| pItems->GetChild<CXFA_Node>(1, XFA_Element::Unknown, false) |
| ->JSObject() |
| ->GetContent(false); |
| } |
| pChild->SetValue(XFA_VALUEPICTURE_Raw, itemText); |
| } |
| pNextChild = pChild->GetNextContainerSibling(); |
| } |
| break; |
| } |
| case XFA_FFWidgetType::kChoiceList: |
| ClearAllSelections(); |
| FALLTHROUGH; |
| default: { |
| CXFA_Value* defValue = GetDefaultValueIfExists(); |
| if (defValue) |
| wsValue = defValue->GetChildValueContent(); |
| |
| SetValue(XFA_VALUEPICTURE_Raw, wsValue); |
| break; |
| } |
| } |
| } |
| |
| void CXFA_Node::SetImageEdit(const WideString& wsContentType, |
| const WideString& wsHref, |
| const WideString& wsData) { |
| CXFA_Value* formValue = GetFormValueIfExists(); |
| CXFA_Image* image = formValue ? formValue->GetImageIfExists() : nullptr; |
| if (image) { |
| image->SetContentType(WideString(wsContentType)); |
| image->SetHref(wsHref); |
| } |
| |
| JSObject()->SetContent(wsData, GetFormatDataValue(wsData), true, false, true); |
| |
| CXFA_Node* pBind = GetBindData(); |
| if (!pBind) { |
| if (image) |
| image->SetTransferEncoding(XFA_AttributeEnum::Base64); |
| return; |
| } |
| pBind->JSObject()->SetCData(XFA_Attribute::ContentType, wsContentType, false, |
| false); |
| CXFA_Node* pHrefNode = pBind->GetFirstChild(); |
| if (pHrefNode) { |
| pHrefNode->JSObject()->SetCData(XFA_Attribute::Value, wsHref, false, false); |
| } else { |
| CFX_XMLNode* pXMLNode = pBind->GetXMLMappingNode(); |
| ASSERT(pXMLNode && pXMLNode->GetType() == FX_XMLNODE_Element); |
| static_cast<CFX_XMLElement*>(pXMLNode)->SetAttribute(L"href", wsHref); |
| } |
| } |
| |
| CXFA_FFWidget* CXFA_Node::GetNextWidget(CXFA_FFWidget* pWidget) { |
| return static_cast<CXFA_FFWidget*>(pWidget->GetNext()); |
| } |
| |
| void CXFA_Node::UpdateUIDisplay(CXFA_FFDocView* docView, |
| CXFA_FFWidget* pExcept) { |
| CXFA_FFWidget* pWidget = docView->GetWidgetForNode(this); |
| for (; pWidget; pWidget = GetNextWidget(pWidget)) { |
| if (pWidget == pExcept || !pWidget->IsLoaded() || |
| (GetFFWidgetType() != XFA_FFWidgetType::kCheckButton && |
| pWidget->IsFocused())) { |
| continue; |
| } |
| pWidget->UpdateFWLData(); |
| pWidget->InvalidateRect(); |
| } |
| } |
| |
| void CXFA_Node::CalcCaptionSize(CXFA_FFDoc* doc, CFX_SizeF* pszCap) { |
| CXFA_Caption* caption = GetCaptionIfExists(); |
| if (!caption || !caption->IsVisible()) |
| return; |
| |
| LoadCaption(doc); |
| |
| XFA_AttributeEnum iCapPlacement = caption->GetPlacementType(); |
| float fCapReserve = caption->GetReserve(); |
| const bool bVert = iCapPlacement == XFA_AttributeEnum::Top || |
| iCapPlacement == XFA_AttributeEnum::Bottom; |
| const bool bReserveExit = fCapReserve > 0.01; |
| CXFA_TextLayout* pCapTextLayout = |
| static_cast<CXFA_FieldLayoutData*>(m_pLayoutData.get()) |
| ->m_pCapTextLayout.get(); |
| if (pCapTextLayout) { |
| if (!bVert && GetFFWidgetType() != XFA_FFWidgetType::kButton) |
| pszCap->width = fCapReserve; |
| |
| CFX_SizeF minSize; |
| *pszCap = pCapTextLayout->CalcSize(minSize, *pszCap); |
| if (bReserveExit) |
| bVert ? pszCap->height = fCapReserve : pszCap->width = fCapReserve; |
| } else { |
| float fFontSize = 10.0f; |
| CXFA_Font* font = caption->GetFontIfExists(); |
| if (font) { |
| fFontSize = font->GetFontSize(); |
| } else { |
| CXFA_Font* widgetfont = GetFontIfExists(); |
| if (widgetfont) |
| fFontSize = widgetfont->GetFontSize(); |
| } |
| |
| if (bVert) { |
| pszCap->height = fCapReserve > 0 ? fCapReserve : fFontSize; |
| } else { |
| pszCap->width = fCapReserve > 0 ? fCapReserve : 0; |
| pszCap->height = fFontSize; |
| } |
| } |
| |
| CXFA_Margin* captionMargin = caption->GetMarginIfExists(); |
| if (!captionMargin) |
| return; |
| |
| float fLeftInset = captionMargin->GetLeftInset(); |
| float fTopInset = captionMargin->GetTopInset(); |
| float fRightInset = captionMargin->GetRightInset(); |
| float fBottomInset = captionMargin->GetBottomInset(); |
| if (bReserveExit) { |
| bVert ? (pszCap->width += fLeftInset + fRightInset) |
| : (pszCap->height += fTopInset + fBottomInset); |
| } else { |
| pszCap->width += fLeftInset + fRightInset; |
| pszCap->height += fTopInset + fBottomInset; |
| } |
| } |
| |
| bool CXFA_Node::CalculateFieldAutoSize(CXFA_FFDoc* doc, CFX_SizeF* pSize) { |
| CFX_SizeF szCap; |
| CalcCaptionSize(doc, &szCap); |
| |
| CFX_RectF rtUIMargin = GetUIMargin(); |
| pSize->width += rtUIMargin.left + rtUIMargin.width; |
| pSize->height += rtUIMargin.top + rtUIMargin.height; |
| if (szCap.width > 0 && szCap.height > 0) { |
| CXFA_Caption* caption = GetCaptionIfExists(); |
| XFA_AttributeEnum placement = caption ? caption->GetPlacementType() |
| : CXFA_Caption::kDefaultPlacementType; |
| switch (placement) { |
| case XFA_AttributeEnum::Left: |
| case XFA_AttributeEnum::Right: |
| case XFA_AttributeEnum::Inline: { |
| pSize->width += szCap.width; |
| pSize->height = std::max(pSize->height, szCap.height); |
| } break; |
| case XFA_AttributeEnum::Top: |
| case XFA_AttributeEnum::Bottom: { |
| pSize->height += szCap.height; |
| |