// Copyright 2017 The PDFium Authors // 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 "fxjs/xfa/cjx_object.h" #include #include #include #include "core/fxcrt/fx_extension.h" #include "core/fxcrt/fx_memory.h" #include "core/fxcrt/xml/cfx_xmlelement.h" #include "core/fxcrt/xml/cfx_xmltext.h" #include "fxjs/cjs_result.h" #include "fxjs/fxv8.h" #include "fxjs/gc/container_trace.h" #include "fxjs/xfa/cfxjse_engine.h" #include "fxjs/xfa/cfxjse_mapmodule.h" #include "fxjs/xfa/cjx_boolean.h" #include "fxjs/xfa/cjx_draw.h" #include "fxjs/xfa/cjx_field.h" #include "fxjs/xfa/cjx_instancemanager.h" #include "third_party/base/check.h" #include "third_party/base/check_op.h" #include "third_party/base/containers/contains.h" #include "v8/include/v8-forward.h" #include "v8/include/v8-object.h" #include "v8/include/v8-primitive.h" #include "xfa/fgas/crt/cfgas_decimal.h" #include "xfa/fxfa/cxfa_ffnotify.h" #include "xfa/fxfa/cxfa_ffwidget.h" #include "xfa/fxfa/parser/cxfa_border.h" #include "xfa/fxfa/parser/cxfa_datavalue.h" #include "xfa/fxfa/parser/cxfa_document.h" #include "xfa/fxfa/parser/cxfa_edge.h" #include "xfa/fxfa/parser/cxfa_fill.h" #include "xfa/fxfa/parser/cxfa_font.h" #include "xfa/fxfa/parser/cxfa_measurement.h" #include "xfa/fxfa/parser/cxfa_node.h" #include "xfa/fxfa/parser/cxfa_object.h" #include "xfa/fxfa/parser/cxfa_occur.h" #include "xfa/fxfa/parser/cxfa_proto.h" #include "xfa/fxfa/parser/cxfa_subform.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" namespace { enum XFA_KEYTYPE { XFA_KEYTYPE_Custom, XFA_KEYTYPE_Element, }; uint32_t GetMapKey_Custom(WideStringView wsKey) { uint32_t dwKey = FX_HashCode_GetW(wsKey); return ((dwKey << 1) | XFA_KEYTYPE_Custom); } uint32_t GetMapKey_Element(XFA_Element eType, XFA_Attribute eAttribute) { return ((static_cast(eType) << 16) | (static_cast(eAttribute) << 8) | XFA_KEYTYPE_Element); } std::tuple StrToRGB(const WideString& strRGB) { int32_t r = 0; int32_t g = 0; int32_t b = 0; size_t iIndex = 0; for (size_t i = 0; i < strRGB.GetLength(); ++i) { wchar_t ch = strRGB[i]; if (ch == L',') ++iIndex; if (iIndex > 2) break; int32_t iValue = ch - L'0'; if (iValue >= 0 && iValue <= 9) { switch (iIndex) { case 0: r = r * 10 + iValue; break; case 1: g = g * 10 + iValue; break; default: b = b * 10 + iValue; break; } } } return {r, g, b}; } } // namespace CJX_Object::CJX_Object(CXFA_Object* obj) : object_(obj) {} CJX_Object::~CJX_Object() = default; CJX_Object* CJX_Object::AsCJXObject() { return this; } void CJX_Object::Trace(cppgc::Visitor* visitor) const { visitor->Trace(object_); visitor->Trace(layout_item_); visitor->Trace(calc_data_); } bool CJX_Object::DynamicTypeIs(TypeTag eType) const { return eType == static_type__; } void CJX_Object::DefineMethods(pdfium::span methods) { for (const auto& item : methods) method_specs_[item.pName] = item.pMethodCall; } CXFA_Document* CJX_Object::GetDocument() const { return object_->GetDocument(); } CXFA_Node* CJX_Object::GetXFANode() const { return ToNode(GetXFAObject()); } void CJX_Object::className(v8::Isolate* pIsolate, v8::Local* pValue, bool bSetting, XFA_Attribute eAttribute) { if (bSetting) { ThrowInvalidPropertyException(pIsolate); return; } *pValue = fxv8::NewStringHelper(pIsolate, GetXFAObject()->GetClassName()); } int32_t CJX_Object::Subform_and_SubformSet_InstanceIndex() { int32_t index = 0; for (CXFA_Node* pNode = GetXFANode()->GetPrevSibling(); pNode; pNode = pNode->GetPrevSibling()) { if ((pNode->GetElementType() != XFA_Element::Subform) && (pNode->GetElementType() != XFA_Element::SubformSet)) { break; } index++; } return index; } bool CJX_Object::HasMethod(const WideString& func) const { return pdfium::Contains(method_specs_, func.ToUTF8()); } CJS_Result CJX_Object::RunMethod( CFXJSE_Engine* pScriptContext, const WideString& func, const std::vector>& params) { auto it = method_specs_.find(func.ToUTF8()); if (it == method_specs_.end()) return CJS_Result::Failure(JSMessage::kUnknownMethod); return it->second(this, pScriptContext, params); } void CJX_Object::ThrowTooManyOccurrencesException(v8::Isolate* pIsolate, const WideString& obj) const { ThrowException( pIsolate, WideString::FromASCII("The element [") + obj + WideString::FromASCII( "] has violated its allowable number of occurrences.")); } void CJX_Object::ThrowInvalidPropertyException(v8::Isolate* pIsolate) const { ThrowException(pIsolate, WideString::FromASCII("Invalid property set operation.")); } void CJX_Object::ThrowIndexOutOfBoundsException(v8::Isolate* pIsolate) const { ThrowException(pIsolate, WideString::FromASCII("Index value is out of bounds.")); } void CJX_Object::ThrowParamCountMismatchException( v8::Isolate* pIsolate, const WideString& method) const { ThrowException( pIsolate, WideString::FromASCII("Incorrect number of parameters calling method '") + method + WideString::FromASCII("'.")); } void CJX_Object::ThrowArgumentMismatchException(v8::Isolate* pIsolate) const { ThrowException(pIsolate, WideString::FromASCII( "Argument mismatch in property or function argument.")); } void CJX_Object::ThrowException(v8::Isolate* pIsolate, const WideString& str) const { DCHECK(!str.IsEmpty()); FXJSE_ThrowMessage(pIsolate, str.ToUTF8().AsStringView()); } bool CJX_Object::HasAttribute(XFA_Attribute eAttr) const { uint32_t key = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr); return HasMapModuleKey(key); } void CJX_Object::SetAttributeByEnum(XFA_Attribute eAttr, const WideString& wsValue, bool bNotify) { switch (GetXFANode()->GetAttributeType(eAttr)) { case XFA_AttributeType::Enum: { absl::optional item = XFA_GetAttributeValueByName(wsValue.AsStringView()); SetEnum(eAttr, item.has_value() ? item.value() : GetXFANode()->GetDefaultEnum(eAttr).value(), bNotify); break; } case XFA_AttributeType::CData: SetCDataImpl(eAttr, WideString(wsValue), bNotify, false); break; case XFA_AttributeType::Boolean: SetBoolean(eAttr, !wsValue.EqualsASCII("0"), bNotify); break; case XFA_AttributeType::Integer: SetInteger(eAttr, FXSYS_roundf(FXSYS_wcstof(wsValue.c_str(), wsValue.GetLength(), nullptr)), bNotify); break; case XFA_AttributeType::Measure: SetMeasure(eAttr, CXFA_Measurement(wsValue.AsStringView()), bNotify); break; } } void CJX_Object::SetAttributeByString(WideStringView wsAttr, const WideString& wsValue) { absl::optional attr = XFA_GetAttributeByName(wsAttr); if (attr.has_value()) { SetAttributeByEnum(attr.value().attribute, wsValue, true); return; } uint32_t key = GetMapKey_Custom(wsAttr); SetMapModuleString(key, wsValue); } WideString CJX_Object::GetAttributeByString(WideStringView attr) const { absl::optional result; absl::optional enum_attr = XFA_GetAttributeByName(attr); if (enum_attr.has_value()) result = TryAttribute(enum_attr.value().attribute, true); else result = GetMapModuleStringFollowingChain(GetMapKey_Custom(attr)); return result.value_or(WideString()); } WideString CJX_Object::GetAttributeByEnum(XFA_Attribute attr) const { return TryAttribute(attr, true).value_or(WideString()); } absl::optional CJX_Object::TryAttribute(XFA_Attribute eAttr, bool bUseDefault) const { switch (GetXFANode()->GetAttributeType(eAttr)) { case XFA_AttributeType::Enum: { absl::optional value = TryEnum(eAttr, bUseDefault); if (!value.has_value()) return absl::nullopt; return WideString::FromASCII(XFA_AttributeValueToName(value.value())); } case XFA_AttributeType::CData: return TryCData(eAttr, bUseDefault); case XFA_AttributeType::Boolean: { absl::optional value = TryBoolean(eAttr, bUseDefault); if (!value.has_value()) return absl::nullopt; return WideString(value.value() ? L"1" : L"0"); } case XFA_AttributeType::Integer: { absl::optional iValue = TryInteger(eAttr, bUseDefault); if (!iValue.has_value()) return absl::nullopt; return WideString::FormatInteger(iValue.value()); } case XFA_AttributeType::Measure: { absl::optional value = TryMeasure(eAttr, bUseDefault); if (!value.has_value()) return absl::nullopt; return value->ToString(); } } return absl::nullopt; } void CJX_Object::RemoveAttribute(WideStringView wsAttr) { RemoveMapModuleKey(GetMapKey_Custom(wsAttr)); } absl::optional CJX_Object::TryBoolean(XFA_Attribute eAttr, bool bUseDefault) const { uint32_t key = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr); absl::optional value = GetMapModuleValueFollowingChain(key); if (value.has_value()) return !!value.value(); if (!bUseDefault) return absl::nullopt; return GetXFANode()->GetDefaultBoolean(eAttr); } void CJX_Object::SetBoolean(XFA_Attribute eAttr, bool bValue, bool bNotify) { CFX_XMLElement* elem = SetValue(eAttr, static_cast(bValue), bNotify); if (elem) { elem->SetAttribute(WideString::FromASCII(XFA_AttributeToName(eAttr)), bValue ? L"1" : L"0"); } } bool CJX_Object::GetBoolean(XFA_Attribute eAttr) const { return TryBoolean(eAttr, true).value_or(false); } void CJX_Object::SetInteger(XFA_Attribute eAttr, int32_t iValue, bool bNotify) { CFX_XMLElement* elem = SetValue(eAttr, iValue, bNotify); if (elem) { elem->SetAttribute(WideString::FromASCII(XFA_AttributeToName(eAttr)), WideString::FormatInteger(iValue)); } } int32_t CJX_Object::GetInteger(XFA_Attribute eAttr) const { return TryInteger(eAttr, true).value_or(0); } absl::optional CJX_Object::TryInteger(XFA_Attribute eAttr, bool bUseDefault) const { uint32_t key = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr); absl::optional value = GetMapModuleValueFollowingChain(key); if (value.has_value()) return value.value(); if (!bUseDefault) return absl::nullopt; return GetXFANode()->GetDefaultInteger(eAttr); } absl::optional CJX_Object::TryEnum(XFA_Attribute eAttr, bool bUseDefault) const { uint32_t key = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr); absl::optional value = GetMapModuleValueFollowingChain(key); if (value.has_value()) return static_cast(value.value()); if (!bUseDefault) return absl::nullopt; return GetXFANode()->GetDefaultEnum(eAttr); } void CJX_Object::SetEnum(XFA_Attribute eAttr, XFA_AttributeValue eValue, bool bNotify) { CFX_XMLElement* elem = SetValue(eAttr, static_cast(eValue), bNotify); if (elem) { elem->SetAttribute(WideString::FromASCII(XFA_AttributeToName(eAttr)), WideString::FromASCII(XFA_AttributeValueToName(eValue))); } } XFA_AttributeValue CJX_Object::GetEnum(XFA_Attribute eAttr) const { return TryEnum(eAttr, true).value_or(XFA_AttributeValue::Unknown); } void CJX_Object::SetMeasure(XFA_Attribute eAttr, const CXFA_Measurement& mValue, bool bNotify) { // Can't short-circuit update here when the value is the same since it // might have come from further up the chain from where we are setting it. uint32_t key = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr); if (bNotify) OnChanging(eAttr); SetMapModuleMeasurement(key, mValue); if (bNotify) OnChanged(eAttr, false); } absl::optional CJX_Object::TryMeasure( XFA_Attribute eAttr, bool bUseDefault) const { uint32_t key = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr); absl::optional result = GetMapModuleMeasurementFollowingChain(key); if (result.has_value()) return result.value(); if (!bUseDefault) return absl::nullopt; return GetXFANode()->GetDefaultMeasurement(eAttr); } absl::optional CJX_Object::TryMeasureAsFloat(XFA_Attribute attr) const { absl::optional measure = TryMeasure(attr, false); if (!measure.has_value()) return absl::nullopt; return measure->ToUnit(XFA_Unit::Pt); } CXFA_Measurement CJX_Object::GetMeasure(XFA_Attribute eAttr) const { return TryMeasure(eAttr, true).value_or(CXFA_Measurement()); } float CJX_Object::GetMeasureInUnit(XFA_Attribute eAttr, XFA_Unit unit) const { return GetMeasure(eAttr).ToUnit(unit); } WideString CJX_Object::GetCData(XFA_Attribute eAttr) const { return TryCData(eAttr, true).value_or(WideString()); } void CJX_Object::SetCData(XFA_Attribute eAttr, const WideString& wsValue) { return SetCDataImpl(eAttr, wsValue, false, false); } void CJX_Object::SetCDataImpl(XFA_Attribute eAttr, const WideString& wsValue, bool bNotify, bool bScriptModify) { CXFA_Node* xfaObj = GetXFANode(); uint32_t key = GetMapKey_Element(xfaObj->GetElementType(), eAttr); absl::optional old_value = GetMapModuleString(key); if (!old_value.has_value() || old_value.value() != wsValue) { if (bNotify) OnChanging(eAttr); SetMapModuleString(key, wsValue); if (eAttr == XFA_Attribute::Name) xfaObj->UpdateNameHash(); if (bNotify) OnChanged(eAttr, bScriptModify); } if (!xfaObj->IsNeedSavingXMLNode() || eAttr == XFA_Attribute::QualifiedName || eAttr == XFA_Attribute::BindingNode) { return; } if (eAttr == XFA_Attribute::Name && (xfaObj->GetElementType() == XFA_Element::DataValue || xfaObj->GetElementType() == XFA_Element::DataGroup)) { return; } if (eAttr == XFA_Attribute::Value) { xfaObj->SetToXML(wsValue); return; } CFX_XMLElement* elem = ToXMLElement(xfaObj->GetXMLMappingNode()); if (!elem) { return; } WideString wsAttrName = WideString::FromASCII(XFA_AttributeToName(eAttr)); if (eAttr == XFA_Attribute::ContentType) wsAttrName = L"xfa:" + wsAttrName; elem->SetAttribute(wsAttrName, wsValue); } void CJX_Object::SetAttributeValue(const WideString& wsValue, const WideString& wsXMLValue) { SetAttributeValueImpl(wsValue, wsXMLValue, false, false); } void CJX_Object::SetAttributeValueImpl(const WideString& wsValue, const WideString& wsXMLValue, bool bNotify, bool bScriptModify) { auto* xfaObj = GetXFANode(); uint32_t key = GetMapKey_Element(xfaObj->GetElementType(), XFA_Attribute::Value); absl::optional old_value = GetMapModuleString(key); if (!old_value.has_value() || old_value.value() != wsValue) { if (bNotify) OnChanging(XFA_Attribute::Value); SetMapModuleString(key, wsValue); if (bNotify) OnChanged(XFA_Attribute::Value, bScriptModify); if (xfaObj->IsNeedSavingXMLNode()) xfaObj->SetToXML(wsXMLValue); } } absl::optional CJX_Object::TryCData(XFA_Attribute eAttr, bool bUseDefault) const { uint32_t key = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr); absl::optional value = GetMapModuleStringFollowingChain(key); if (value.has_value()) return value; if (!bUseDefault) return absl::nullopt; return GetXFANode()->GetDefaultCData(eAttr); } CFX_XMLElement* CJX_Object::SetValue(XFA_Attribute eAttr, int32_t value, bool bNotify) { uint32_t key = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr); absl::optional old_value = GetMapModuleValue(key); if (!old_value.has_value() || old_value.value() != value) { if (bNotify) OnChanging(eAttr); SetMapModuleValue(key, value); if (bNotify) OnChanged(eAttr, false); } CXFA_Node* pNode = GetXFANode(); return pNode->IsNeedSavingXMLNode() ? ToXMLElement(pNode->GetXMLMappingNode()) : nullptr; } void CJX_Object::SetContent(const WideString& wsContent, const WideString& wsXMLValue, bool bNotify, bool bScriptModify, bool bSyncData) { CXFA_Node* pNode = nullptr; CXFA_Node* pBindNode = nullptr; switch (GetXFANode()->GetObjectType()) { case XFA_ObjectType::ContainerNode: { if (XFA_FieldIsMultiListBox(GetXFANode())) { CXFA_Value* pValue = GetOrCreateProperty(0, XFA_Element::Value); if (!pValue) break; CXFA_Node* pChildValue = pValue->GetFirstChild(); pChildValue->JSObject()->SetCData(XFA_Attribute::ContentType, L"text/xml"); pChildValue->JSObject()->SetContent(wsContent, wsContent, bNotify, bScriptModify, false); CXFA_Node* pBind = GetXFANode()->GetBindData(); if (bSyncData && pBind) { std::vector wsSaveTextArray = fxcrt::Split(wsContent, L'\n'); std::vector valueNodes = pBind->GetNodeListForType(XFA_Element::DataValue); // Adusting node count might have side effects, do not trust that // we'll ever actually get there. size_t tries = 0; while (valueNodes.size() != wsSaveTextArray.size()) { if (++tries > 4) return; if (valueNodes.size() < wsSaveTextArray.size()) { size_t iAddNodes = wsSaveTextArray.size() - valueNodes.size(); while (iAddNodes-- > 0) { CXFA_Node* pValueNodes = pBind->CreateSamePacketNode(XFA_Element::DataValue); pValueNodes->JSObject()->SetCData(XFA_Attribute::Name, L"value"); pValueNodes->CreateXMLMappingNode(); pBind->InsertChildAndNotify(pValueNodes, nullptr); } } else { size_t iDelNodes = valueNodes.size() - wsSaveTextArray.size(); for (size_t i = 0; i < iDelNodes; ++i) pBind->RemoveChildAndNotify(valueNodes[i], true); } valueNodes = pBind->GetNodeListForType(XFA_Element::DataValue); } DCHECK_EQ(valueNodes.size(), wsSaveTextArray.size()); size_t i = 0; for (CXFA_Node* pValueNode : valueNodes) { pValueNode->JSObject()->SetAttributeValue(wsSaveTextArray[i], wsSaveTextArray[i]); i++; } for (auto* pArrayNode : pBind->GetBindItemsCopy()) { if (pArrayNode != GetXFANode()) { pArrayNode->JSObject()->SetContent(wsContent, wsContent, bNotify, bScriptModify, false); } } } break; } if (GetXFANode()->GetElementType() == XFA_Element::ExclGroup) { pNode = GetXFANode(); } else { CXFA_Value* pValue = GetOrCreateProperty(0, XFA_Element::Value); if (!pValue) break; CXFA_Node* pChildValue = pValue->GetFirstChild(); if (pChildValue) { pChildValue->JSObject()->SetContent(wsContent, wsContent, bNotify, bScriptModify, false); } } pBindNode = GetXFANode()->GetBindData(); if (pBindNode && bSyncData) { pBindNode->JSObject()->SetContent(wsContent, wsXMLValue, bNotify, bScriptModify, false); for (auto* pArrayNode : pBindNode->GetBindItemsCopy()) { if (pArrayNode != GetXFANode()) { pArrayNode->JSObject()->SetContent(wsContent, wsContent, bNotify, true, false); } } } pBindNode = nullptr; break; } case XFA_ObjectType::ContentNode: { WideString wsContentType; if (GetXFANode()->GetElementType() == XFA_Element::ExData) { absl::optional ret = TryAttribute(XFA_Attribute::ContentType, false); if (ret.has_value()) wsContentType = ret.value(); if (wsContentType.EqualsASCII("text/html")) { wsContentType.clear(); SetAttributeByEnum(XFA_Attribute::ContentType, wsContentType, false); } } CXFA_Node* pContentRawDataNode = GetXFANode()->GetFirstChild(); if (!pContentRawDataNode) { pContentRawDataNode = GetXFANode()->CreateSamePacketNode( wsContentType.EqualsASCII("text/xml") ? XFA_Element::Sharpxml : XFA_Element::Sharptext); GetXFANode()->InsertChildAndNotify(pContentRawDataNode, nullptr); } pContentRawDataNode->JSObject()->SetContent( wsContent, wsXMLValue, bNotify, bScriptModify, bSyncData); return; } case XFA_ObjectType::NodeC: case XFA_ObjectType::TextNode: pNode = GetXFANode(); break; case XFA_ObjectType::NodeV: pNode = GetXFANode(); if (bSyncData && GetXFANode()->GetPacketType() == XFA_PacketType::Form) { CXFA_Node* pParent = GetXFANode()->GetParent(); if (pParent) { pParent = pParent->GetParent(); } if (pParent && pParent->GetElementType() == XFA_Element::Value) { pParent = pParent->GetParent(); if (pParent && pParent->IsContainerNode()) { pBindNode = pParent->GetBindData(); if (pBindNode) { pBindNode->JSObject()->SetContent(wsContent, wsXMLValue, bNotify, bScriptModify, false); } } } } break; default: if (GetXFANode()->GetElementType() == XFA_Element::DataValue) { pNode = GetXFANode(); pBindNode = GetXFANode(); } break; } if (!pNode) return; SetAttributeValueImpl(wsContent, wsXMLValue, bNotify, bScriptModify); if (pBindNode && bSyncData) { for (auto* pArrayNode : pBindNode->GetBindItemsCopy()) { pArrayNode->JSObject()->SetContent(wsContent, wsContent, bNotify, bScriptModify, false); } } } WideString CJX_Object::GetContent(bool bScriptModify) const { return TryContent(bScriptModify, true).value_or(WideString()); } absl::optional CJX_Object::TryContent(bool bScriptModify, bool bProto) const { CXFA_Node* pNode = nullptr; switch (GetXFANode()->GetObjectType()) { case XFA_ObjectType::ContainerNode: if (GetXFANode()->GetElementType() == XFA_Element::ExclGroup) { pNode = GetXFANode(); } else { CXFA_Value* pValue = GetXFANode()->GetChild(0, XFA_Element::Value, false); if (!pValue) return absl::nullopt; CXFA_Node* pChildValue = pValue->GetFirstChild(); if (pChildValue && XFA_FieldIsMultiListBox(GetXFANode())) { pChildValue->JSObject()->SetAttributeByEnum( XFA_Attribute::ContentType, L"text/xml", false); } if (!pChildValue) return absl::nullopt; return pChildValue->JSObject()->TryContent(bScriptModify, bProto); } break; case XFA_ObjectType::ContentNode: { CXFA_Node* pContentRawDataNode = GetXFANode()->GetFirstChild(); if (!pContentRawDataNode) { XFA_Element element = XFA_Element::Sharptext; if (GetXFANode()->GetElementType() == XFA_Element::ExData) { absl::optional contentType = TryAttribute(XFA_Attribute::ContentType, false); if (contentType.has_value()) { if (contentType.value().EqualsASCII("text/html")) element = XFA_Element::SharpxHTML; else if (contentType.value().EqualsASCII("text/xml")) element = XFA_Element::Sharpxml; } } pContentRawDataNode = GetXFANode()->CreateSamePacketNode(element); GetXFANode()->InsertChildAndNotify(pContentRawDataNode, nullptr); } return pContentRawDataNode->JSObject()->TryContent(bScriptModify, true); } case XFA_ObjectType::NodeC: case XFA_ObjectType::NodeV: case XFA_ObjectType::TextNode: pNode = GetXFANode(); [[fallthrough]]; default: if (GetXFANode()->GetElementType() == XFA_Element::DataValue) pNode = GetXFANode(); break; } if (pNode) { if (bScriptModify) { CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext(); pScriptContext->AddNodesOfRunScript(GetXFANode()); } return TryCData(XFA_Attribute::Value, false); } return absl::nullopt; } absl::optional CJX_Object::TryNamespace() const { if (GetXFANode()->IsModelNode() || GetXFANode()->GetElementType() == XFA_Element::Packet) { CFX_XMLNode* pXMLNode = GetXFANode()->GetXMLMappingNode(); CFX_XMLElement* element = ToXMLElement(pXMLNode); if (!element) return absl::nullopt; return element->GetNamespaceURI(); } if (GetXFANode()->GetPacketType() != XFA_PacketType::Datasets) return GetXFANode()->GetModelNode()->JSObject()->TryNamespace(); CFX_XMLNode* pXMLNode = GetXFANode()->GetXMLMappingNode(); CFX_XMLElement* element = ToXMLElement(pXMLNode); if (!element) return absl::nullopt; if (GetXFANode()->GetElementType() == XFA_Element::DataValue && GetEnum(XFA_Attribute::Contains) == XFA_AttributeValue::MetaData) { WideString wsNamespace; if (!XFA_FDEExtension_ResolveNamespaceQualifier( element, GetCData(XFA_Attribute::QualifiedName), &wsNamespace)) { return absl::nullopt; } return wsNamespace; } return element->GetNamespaceURI(); } CXFA_Node* CJX_Object::GetPropertyInternal(int32_t index, XFA_Element eProperty) const { return GetXFANode()->GetProperty(index, eProperty).first; } CXFA_Node* CJX_Object::GetOrCreatePropertyInternal(int32_t index, XFA_Element eProperty) { return GetXFANode()->GetOrCreateProperty(index, eProperty); } CFXJSE_MapModule* CJX_Object::CreateMapModule() { if (!map_module_) map_module_ = std::make_unique(); return map_module_.get(); } CFXJSE_MapModule* CJX_Object::GetMapModule() const { return map_module_.get(); } void CJX_Object::SetMapModuleValue(uint32_t key, int32_t value) { CreateMapModule()->SetValue(key, value); } void CJX_Object::SetMapModuleString(uint32_t key, const WideString& wsValue) { CreateMapModule()->SetString(key, wsValue); } void CJX_Object::SetMapModuleMeasurement(uint32_t key, const CXFA_Measurement& value) { CreateMapModule()->SetMeasurement(key, value); } absl::optional CJX_Object::GetMapModuleValue(uint32_t key) const { CFXJSE_MapModule* pModule = GetMapModule(); if (!pModule) return absl::nullopt; return pModule->GetValue(key); } absl::optional CJX_Object::GetMapModuleString(uint32_t key) const { CFXJSE_MapModule* pModule = GetMapModule(); if (!pModule) return absl::nullopt; return pModule->GetString(key); } absl::optional CJX_Object::GetMapModuleMeasurement( uint32_t key) const { CFXJSE_MapModule* pModule = GetMapModule(); if (!pModule) return absl::nullopt; return pModule->GetMeasurement(key); } absl::optional CJX_Object::GetMapModuleValueFollowingChain( uint32_t key) const { std::set visited; for (const CXFA_Node* pNode = GetXFANode(); pNode; pNode = pNode->GetTemplateNodeIfExists()) { if (!visited.insert(pNode).second) break; absl::optional result = pNode->JSObject()->GetMapModuleValue(key); if (result.has_value()) return result; if (pNode->GetPacketType() == XFA_PacketType::Datasets) break; } return absl::nullopt; } absl::optional CJX_Object::GetMapModuleStringFollowingChain( uint32_t key) const { std::set visited; for (const CXFA_Node* pNode = GetXFANode(); pNode; pNode = pNode->GetTemplateNodeIfExists()) { if (!visited.insert(pNode).second) break; absl::optional result = pNode->JSObject()->GetMapModuleString(key); if (result.has_value()) return result; if (pNode->GetPacketType() == XFA_PacketType::Datasets) break; } return absl::nullopt; } absl::optional CJX_Object::GetMapModuleMeasurementFollowingChain(uint32_t key) const { std::set visited; for (const CXFA_Node* pNode = GetXFANode(); pNode; pNode = pNode->GetTemplateNodeIfExists()) { if (!visited.insert(pNode).second) break; absl::optional result = pNode->JSObject()->GetMapModuleMeasurement(key); if (result.has_value()) return result; if (pNode->GetPacketType() == XFA_PacketType::Datasets) break; } return absl::nullopt; } bool CJX_Object::HasMapModuleKey(uint32_t key) const { CFXJSE_MapModule* pModule = GetMapModule(); return pModule && pModule->HasKey(key); } void CJX_Object::RemoveMapModuleKey(uint32_t key) { CFXJSE_MapModule* pModule = GetMapModule(); if (pModule) pModule->RemoveKey(key); } void CJX_Object::MergeAllData(CXFA_Object* pDstObj) { CFXJSE_MapModule* pDstModule = ToNode(pDstObj)->JSObject()->CreateMapModule(); CFXJSE_MapModule* pSrcModule = GetMapModule(); if (!pSrcModule) return; pDstModule->MergeDataFrom(pSrcModule); } void CJX_Object::MoveBufferMapData(CXFA_Object* pDstObj) { if (!pDstObj) return; if (pDstObj->GetElementType() == GetXFAObject()->GetElementType()) ToNode(pDstObj)->JSObject()->TakeCalcDataFrom(this); if (!pDstObj->IsNodeV()) return; WideString wsValue = ToNode(pDstObj)->JSObject()->GetContent(false); WideString wsFormatValue(wsValue); CXFA_Node* pNode = ToNode(pDstObj)->GetContainerNode(); if (pNode) wsFormatValue = pNode->GetFormatDataValue(wsValue); ToNode(pDstObj)->JSObject()->SetContent(wsValue, wsFormatValue, true, true, true); } void CJX_Object::MoveBufferMapData(CXFA_Object* pSrcObj, CXFA_Object* pDstObj) { if (!pSrcObj || !pDstObj) return; CXFA_Node* pSrcChild = ToNode(pSrcObj)->GetFirstChild(); CXFA_Node* pDstChild = ToNode(pDstObj)->GetFirstChild(); while (pSrcChild && pDstChild) { MoveBufferMapData(pSrcChild, pDstChild); pSrcChild = pSrcChild->GetNextSibling(); pDstChild = pDstChild->GetNextSibling(); } ToNode(pSrcObj)->JSObject()->MoveBufferMapData(pDstObj); } void CJX_Object::OnChanging(XFA_Attribute eAttr) { if (!GetXFANode()->IsInitialized()) return; CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); if (!pNotify) return; pNotify->OnValueChanging(GetXFANode(), eAttr); } void CJX_Object::OnChanged(XFA_Attribute eAttr, bool bScriptModify) { if (!GetXFANode()->IsInitialized()) return; GetXFANode()->SendAttributeChangeMessage(eAttr, bScriptModify); } CJX_Object::CalcData* CJX_Object::GetOrCreateCalcData(cppgc::Heap* heap) { if (!calc_data_) { calc_data_ = cppgc::MakeGarbageCollected(heap->GetAllocationHandle()); } return calc_data_; } void CJX_Object::TakeCalcDataFrom(CJX_Object* that) { calc_data_ = that->calc_data_; that->calc_data_ = nullptr; } void CJX_Object::ScriptAttributeString(v8::Isolate* pIsolate, v8::Local* pValue, bool bSetting, XFA_Attribute eAttribute) { if (!bSetting) { *pValue = fxv8::NewStringHelper( pIsolate, GetAttributeByEnum(eAttribute).ToUTF8().AsStringView()); return; } WideString wsValue = fxv8::ReentrantToWideStringHelper(pIsolate, *pValue); SetAttributeByEnum(eAttribute, wsValue, true); if (eAttribute != XFA_Attribute::Use || GetXFAObject()->GetElementType() != XFA_Element::Desc) { return; } CXFA_Node* pTemplateNode = ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Template)); CXFA_Subform* pSubForm = pTemplateNode->GetFirstChildByClass(XFA_Element::Subform); CXFA_Proto* pProtoRoot = pSubForm ? pSubForm->GetFirstChildByClass(XFA_Element::Proto) : nullptr; WideString wsID; WideString wsSOM; if (!wsValue.IsEmpty()) { if (wsValue[0] == '#') wsID = wsValue.Substr(1); else wsSOM = std::move(wsValue); } CXFA_Node* pProtoNode = nullptr; if (!wsSOM.IsEmpty()) { absl::optional maybeResult = GetDocument()->GetScriptContext()->ResolveObjects( pProtoRoot, wsSOM.AsStringView(), Mask{ XFA_ResolveFlag::kChildren, XFA_ResolveFlag::kAttributes, XFA_ResolveFlag::kProperties, XFA_ResolveFlag::kParent, XFA_ResolveFlag::kSiblings}); if (maybeResult.has_value() && maybeResult.value().objects.front()->IsNode()) { pProtoNode = maybeResult.value().objects.front()->AsNode(); } } else if (!wsID.IsEmpty()) { pProtoNode = GetDocument()->GetNodeByID(pProtoRoot, wsID.AsStringView()); } if (!pProtoNode || pProtoNode->GetPacketType() != XFA_PacketType::Template) return; CXFA_Node* pHeadChild = GetXFANode()->GetFirstChild(); while (pHeadChild) { CXFA_Node* pSibling = pHeadChild->GetNextSibling(); GetXFANode()->RemoveChildAndNotify(pHeadChild, true); pHeadChild = pSibling; } CXFA_Node* pProtoForm = pProtoNode->CloneTemplateToForm(true); pHeadChild = pProtoForm->GetFirstChild(); while (pHeadChild) { CXFA_Node* pSibling = pHeadChild->GetNextSibling(); pProtoForm->RemoveChildAndNotify(pHeadChild, true); GetXFANode()->InsertChildAndNotify(pHeadChild, nullptr); pHeadChild = pSibling; } } void CJX_Object::ScriptAttributeBool(v8::Isolate* pIsolate, v8::Local* pValue, bool bSetting, XFA_Attribute eAttribute) { if (bSetting) { SetBoolean(eAttribute, fxv8::ReentrantToBooleanHelper(pIsolate, *pValue), true); return; } *pValue = fxv8::NewStringHelper(pIsolate, GetBoolean(eAttribute) ? "1" : "0"); } void CJX_Object::ScriptAttributeInteger(v8::Isolate* pIsolate, v8::Local* pValue, bool bSetting, XFA_Attribute eAttribute) { if (bSetting) { SetInteger(eAttribute, fxv8::ReentrantToInt32Helper(pIsolate, *pValue), true); return; } *pValue = fxv8::NewNumberHelper(pIsolate, GetInteger(eAttribute)); } void CJX_Object::ScriptSomFontColor(v8::Isolate* pIsolate, v8::Local* pValue, bool bSetting, XFA_Attribute eAttribute) { CXFA_Font* font = ToNode(object_.Get())->GetOrCreateFontIfPossible(); if (!font) return; if (bSetting) { int32_t r; int32_t g; int32_t b; std::tie(r, g, b) = StrToRGB(fxv8::ReentrantToWideStringHelper(pIsolate, *pValue)); FX_ARGB color = ArgbEncode(0xff, r, g, b); font->SetColor(color); return; } int32_t a; int32_t r; int32_t g; int32_t b; std::tie(a, r, g, b) = ArgbDecode(font->GetColor()); *pValue = fxv8::NewStringHelper( pIsolate, ByteString::Format("%d,%d,%d", r, g, b).AsStringView()); } void CJX_Object::ScriptSomFillColor(v8::Isolate* pIsolate, v8::Local* pValue, bool bSetting, XFA_Attribute eAttribute) { CXFA_Border* border = ToNode(object_.Get())->GetOrCreateBorderIfPossible(); CXFA_Fill* borderfill = border->GetOrCreateFillIfPossible(); if (!borderfill) return; if (bSetting) { int32_t r; int32_t g; int32_t b; std::tie(r, g, b) = StrToRGB(fxv8::ReentrantToWideStringHelper(pIsolate, *pValue)); FX_ARGB color = ArgbEncode(0xff, r, g, b); borderfill->SetColor(color); return; } FX_ARGB color = borderfill->GetFillColor(); int32_t a; int32_t r; int32_t g; int32_t b; std::tie(a, r, g, b) = ArgbDecode(color); *pValue = fxv8::NewStringHelper( pIsolate, ByteString::Format("%d,%d,%d", r, g, b).AsStringView()); } void CJX_Object::ScriptSomBorderColor(v8::Isolate* pIsolate, v8::Local* pValue, bool bSetting, XFA_Attribute eAttribute) { CXFA_Border* border = ToNode(object_.Get())->GetOrCreateBorderIfPossible(); int32_t iSize = border->CountEdges(); if (bSetting) { int32_t r = 0; int32_t g = 0; int32_t b = 0; std::tie(r, g, b) = StrToRGB(fxv8::ReentrantToWideStringHelper(pIsolate, *pValue)); FX_ARGB rgb = ArgbEncode(100, r, g, b); for (int32_t i = 0; i < iSize; ++i) { CXFA_Edge* edge = border->GetEdgeIfExists(i); if (edge) edge->SetColor(rgb); } return; } CXFA_Edge* edge = border->GetEdgeIfExists(0); FX_ARGB color = edge ? edge->GetColor() : CXFA_Edge::kDefaultColor; int32_t a; int32_t r; int32_t g; int32_t b; std::tie(a, r, g, b) = ArgbDecode(color); *pValue = fxv8::NewStringHelper( pIsolate, ByteString::Format("%d,%d,%d", r, g, b).AsStringView()); } void CJX_Object::ScriptSomBorderWidth(v8::Isolate* pIsolate, v8::Local* pValue, bool bSetting, XFA_Attribute eAttribute) { CXFA_Border* border = ToNode(object_.Get())->GetOrCreateBorderIfPossible(); if (bSetting) { CXFA_Edge* edge = border->GetEdgeIfExists(0); CXFA_Measurement thickness = edge ? edge->GetMSThickness() : CXFA_Measurement(0.5, XFA_Unit::Pt); *pValue = fxv8::NewStringHelper( pIsolate, thickness.ToString().ToUTF8().AsStringView()); return; } if (pValue->IsEmpty()) return; WideString wsThickness = fxv8::ReentrantToWideStringHelper(pIsolate, *pValue); for (size_t i = 0; i < border->CountEdges(); ++i) { CXFA_Edge* edge = border->GetEdgeIfExists(i); if (edge) edge->SetMSThickness(CXFA_Measurement(wsThickness.AsStringView())); } } void CJX_Object::ScriptSomMessage(v8::Isolate* pIsolate, v8::Local* pValue, bool bSetting, SOMMessageType iMessageType) { bool bNew = false; CXFA_Validate* validate = ToNode(object_.Get())->GetValidateIfExists(); if (!validate) { validate = ToNode(object_.Get())->GetOrCreateValidateIfPossible(); bNew = true; } if (bSetting) { if (validate) { switch (iMessageType) { case SOMMessageType::kValidationMessage: validate->SetScriptMessageText( fxv8::ReentrantToWideStringHelper(pIsolate, *pValue)); break; case SOMMessageType::kFormatMessage: validate->SetFormatMessageText( fxv8::ReentrantToWideStringHelper(pIsolate, *pValue)); break; case SOMMessageType::kMandatoryMessage: validate->SetNullMessageText( fxv8::ReentrantToWideStringHelper(pIsolate, *pValue)); break; } } if (!bNew) { CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); if (!pNotify) return; pNotify->AddCalcValidate(GetXFANode()); } return; } if (!validate) { // TODO(dsinclair): Better error message? ThrowInvalidPropertyException(pIsolate); return; } WideString wsMessage; switch (iMessageType) { case SOMMessageType::kValidationMessage: wsMessage = validate->GetScriptMessageText(); break; case SOMMessageType::kFormatMessage: wsMessage = validate->GetFormatMessageText(); break; case SOMMessageType::kMandatoryMessage: wsMessage = validate->GetNullMessageText(); break; } *pValue = fxv8::NewStringHelper(pIsolate, wsMessage.ToUTF8().AsStringView()); } void CJX_Object::ScriptSomValidationMessage(v8::Isolate* pIsolate, v8::Local* pValue, bool bSetting, XFA_Attribute eAttribute) { ScriptSomMessage(pIsolate, pValue, bSetting, SOMMessageType::kValidationMessage); } void CJX_Object::ScriptSomMandatoryMessage(v8::Isolate* pIsolate, v8::Local* pValue, bool bSetting, XFA_Attribute eAttribute) { ScriptSomMessage(pIsolate, pValue, bSetting, SOMMessageType::kMandatoryMessage); } void CJX_Object::ScriptSomDefaultValue(v8::Isolate* pIsolate, v8::Local* pValue, bool bSetting, XFA_Attribute /* unused */) { XFA_Element eType = GetXFANode()->GetElementType(); // TODO(dsinclair): This should look through the properties on the node to see // if defaultValue is defined and, if so, call that one. Just have to make // sure that those defaultValue calls don't call back to this one .... if (eType == XFA_Element::Field) { static_cast(this)->defaultValue(pIsolate, pValue, bSetting, XFA_Attribute::Unknown); return; } if (eType == XFA_Element::Draw) { static_cast(this)->defaultValue(pIsolate, pValue, bSetting, XFA_Attribute::Unknown); return; } if (eType == XFA_Element::Boolean) { static_cast(this)->defaultValue(pIsolate, pValue, bSetting, XFA_Attribute::Unknown); return; } if (bSetting) { WideString wsNewValue; if (pValue && !(pValue->IsEmpty() || fxv8::IsNull(*pValue) || fxv8::IsUndefined(*pValue))) { wsNewValue = fxv8::ReentrantToWideStringHelper(pIsolate, *pValue); } WideString wsFormatValue = wsNewValue; CXFA_Node* pContainerNode = nullptr; if (GetXFANode()->GetPacketType() == XFA_PacketType::Datasets) { WideString wsPicture; for (auto* pFormNode : GetXFANode()->GetBindItemsCopy()) { if (!pFormNode || pFormNode->HasRemovedChildren()) continue; pContainerNode = pFormNode->GetContainerNode(); if (pContainerNode) { wsPicture = pContainerNode->GetPictureContent(XFA_ValuePicture::kDataBind); } if (!wsPicture.IsEmpty()) break; pContainerNode = nullptr; } } else if (GetXFANode()->GetPacketType() == XFA_PacketType::Form) { pContainerNode = GetXFANode()->GetContainerNode(); } if (pContainerNode) wsFormatValue = pContainerNode->GetFormatDataValue(wsNewValue); SetContent(wsNewValue, wsFormatValue, true, true, true); return; } WideString content = GetContent(true); if (content.IsEmpty() && eType != XFA_Element::Text && eType != XFA_Element::SubmitUrl) { *pValue = fxv8::NewNullHelper(pIsolate); } else if (eType == XFA_Element::Integer) { *pValue = fxv8::NewNumberHelper(pIsolate, FXSYS_wtoi(content.c_str())); } else if (eType == XFA_Element::Float || eType == XFA_Element::Decimal) { CFGAS_Decimal decimal(content.AsStringView()); *pValue = fxv8::NewNumberHelper(pIsolate, decimal.ToFloat()); } else { *pValue = fxv8::NewStringHelper(pIsolate, content.ToUTF8().AsStringView()); } } void CJX_Object::ScriptSomDefaultValue_Read(v8::Isolate* pIsolate, v8::Local* pValue, bool bSetting, XFA_Attribute eAttribute) { if (bSetting) { ThrowInvalidPropertyException(pIsolate); return; } WideString content = GetContent(true); if (content.IsEmpty()) { *pValue = fxv8::NewNullHelper(pIsolate); return; } *pValue = fxv8::NewStringHelper(pIsolate, content.ToUTF8().AsStringView()); } void CJX_Object::ScriptSomDataNode(v8::Isolate* pIsolate, v8::Local* pValue, bool bSetting, XFA_Attribute eAttribute) { if (bSetting) { ThrowInvalidPropertyException(pIsolate); return; } CXFA_Node* pDataNode = GetXFANode()->GetBindData(); if (!pDataNode) { *pValue = fxv8::NewNullHelper(pIsolate); return; } *pValue = GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pDataNode); } void CJX_Object::ScriptSomMandatory(v8::Isolate* pIsolate, v8::Local* pValue, bool bSetting, XFA_Attribute eAttribute) { CXFA_Validate* validate = ToNode(object_.Get())->GetOrCreateValidateIfPossible(); if (!validate) return; if (bSetting) { validate->SetNullTest(fxv8::ReentrantToWideStringHelper(pIsolate, *pValue)); return; } *pValue = fxv8::NewStringHelper( pIsolate, XFA_AttributeValueToName(validate->GetNullTest())); } void CJX_Object::ScriptSomInstanceIndex(v8::Isolate* pIsolate, v8::Local* pValue, bool bSetting, XFA_Attribute eAttribute) { if (!bSetting) { *pValue = fxv8::NewNumberHelper(pIsolate, Subform_and_SubformSet_InstanceIndex()); return; } int32_t iTo = fxv8::ReentrantToInt32Helper(pIsolate, *pValue); int32_t iFrom = Subform_and_SubformSet_InstanceIndex(); CXFA_Node* pManagerNode = nullptr; for (CXFA_Node* pNode = GetXFANode()->GetPrevSibling(); pNode; pNode = pNode->GetPrevSibling()) { if (pNode->GetElementType() == XFA_Element::InstanceManager) { pManagerNode = pNode; break; } } if (!pManagerNode) return; auto* mgr = static_cast(pManagerNode->JSObject()); mgr->MoveInstance(pIsolate, iTo, iFrom); CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); if (!pNotify) return; auto* pToInstance = CXFA_Subform::FromNode(pManagerNode->GetItemIfExists(iTo)); if (pToInstance) pNotify->RunSubformIndexChange(pToInstance); auto* pFromInstance = CXFA_Subform::FromNode(pManagerNode->GetItemIfExists(iFrom)); if (pFromInstance) pNotify->RunSubformIndexChange(pFromInstance); } void CJX_Object::ScriptSubmitFormatMode(v8::Isolate* pIsolate, v8::Local* pValue, bool bSetting, XFA_Attribute eAttribute) {} CJX_Object::CalcData::CalcData() = default; CJX_Object::CalcData::~CalcData() = default; void CJX_Object::CalcData::Trace(cppgc::Visitor* visitor) const { ContainerTrace(visitor, m_Globals); }