1 // Copyright 2017 PDFium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com 6 7 #include "fxjs/xfa/cjx_node.h" 8 9 #include <memory> 10 #include <vector> 11 12 #include "core/fxcrt/cfx_memorystream.h" 13 #include "core/fxcrt/fx_codepage.h" 14 #include "fxjs/cfxjse_engine.h" 15 #include "fxjs/js_resources.h" 16 #include "third_party/base/ptr_util.h" 17 #include "xfa/fxfa/cxfa_eventparam.h" 18 #include "xfa/fxfa/cxfa_ffnotify.h" 19 #include "xfa/fxfa/parser/cxfa_document.h" 20 #include "xfa/fxfa/parser/cxfa_node.h" 21 #include "xfa/fxfa/parser/cxfa_simple_parser.h" 22 #include "xfa/fxfa/parser/xfa_utils.h" 23 24 namespace { 25 26 enum class EventAppliesToo { 27 kNone = 0, 28 kAll = 1, 29 kAllNonRecursive = 2, 30 kSubform = 3, 31 kFieldOrExclusion = 4, 32 kField = 5, 33 kSignature = 6, 34 kChoiceList = 7 35 }; 36 37 struct XFA_ExecEventParaInfo { 38 public: 39 uint32_t m_uHash; 40 const wchar_t* m_lpcEventName; 41 XFA_EVENTTYPE m_eventType; 42 EventAppliesToo m_validFlags; 43 }; 44 45 const XFA_ExecEventParaInfo gs_eventParaInfos[] = { 46 {0x109d7ce7, L"mouseEnter", XFA_EVENT_MouseEnter, EventAppliesToo::kField}, 47 {0x1bfc72d9, L"preOpen", XFA_EVENT_PreOpen, EventAppliesToo::kChoiceList}, 48 {0x2196a452, L"initialize", XFA_EVENT_Initialize, EventAppliesToo::kAll}, 49 {0x27410f03, L"mouseExit", XFA_EVENT_MouseExit, EventAppliesToo::kField}, 50 {0x36f1c6d8, L"preSign", XFA_EVENT_PreSign, EventAppliesToo::kSignature}, 51 {0x4731d6ba, L"exit", XFA_EVENT_Exit, EventAppliesToo::kAllNonRecursive}, 52 {0x7233018a, L"validate", XFA_EVENT_Validate, EventAppliesToo::kAll}, 53 {0x8808385e, L"indexChange", XFA_EVENT_IndexChange, 54 EventAppliesToo::kSubform}, 55 {0x891f4606, L"change", XFA_EVENT_Change, 56 EventAppliesToo::kFieldOrExclusion}, 57 {0x9f693b21, L"mouseDown", XFA_EVENT_MouseDown, EventAppliesToo::kField}, 58 {0xcdce56b3, L"full", XFA_EVENT_Full, EventAppliesToo::kFieldOrExclusion}, 59 {0xd576d08e, L"mouseUp", XFA_EVENT_MouseUp, EventAppliesToo::kField}, 60 {0xd95657a6, L"click", XFA_EVENT_Click, EventAppliesToo::kFieldOrExclusion}, 61 {0xdbfbe02e, L"calculate", XFA_EVENT_Calculate, EventAppliesToo::kAll}, 62 {0xe25fa7b8, L"postOpen", XFA_EVENT_PostOpen, EventAppliesToo::kChoiceList}, 63 {0xe28dce7e, L"enter", XFA_EVENT_Enter, EventAppliesToo::kAllNonRecursive}, 64 {0xfd54fbb7, L"postSign", XFA_EVENT_PostSign, EventAppliesToo::kSignature}, 65 }; 66 67 const XFA_ExecEventParaInfo* GetEventParaInfoByName( 68 const WideStringView& wsEventName) { 69 uint32_t uHash = FX_HashCode_GetW(wsEventName, false); 70 int32_t iStart = 0; 71 int32_t iEnd = (sizeof(gs_eventParaInfos) / sizeof(gs_eventParaInfos[0])) - 1; 72 do { 73 int32_t iMid = (iStart + iEnd) / 2; 74 const XFA_ExecEventParaInfo* eventParaInfo = &gs_eventParaInfos[iMid]; 75 if (uHash == eventParaInfo->m_uHash) 76 return eventParaInfo; 77 if (uHash < eventParaInfo->m_uHash) 78 iEnd = iMid - 1; 79 else 80 iStart = iMid + 1; 81 } while (iStart <= iEnd); 82 return nullptr; 83 } 84 85 } // namespace 86 87 const CJX_MethodSpec CJX_Node::MethodSpecs[] = { 88 {"applyXSL", applyXSL_static}, 89 {"assignNode", assignNode_static}, 90 {"clone", clone_static}, 91 {"getAttribute", getAttribute_static}, 92 {"getElement", getElement_static}, 93 {"isPropertySpecified", isPropertySpecified_static}, 94 {"loadXML", loadXML_static}, 95 {"saveFilteredXML", saveFilteredXML_static}, 96 {"saveXML", saveXML_static}, 97 {"setAttribute", setAttribute_static}, 98 {"setElement", setElement_static}}; 99 100 CJX_Node::CJX_Node(CXFA_Node* node) : CJX_Tree(node) { 101 DefineMethods(MethodSpecs, FX_ArraySize(MethodSpecs)); 102 } 103 104 CJX_Node::~CJX_Node() = default; 105 106 CXFA_Node* CJX_Node::GetXFANode() { 107 return static_cast<CXFA_Node*>(GetXFAObject()); 108 } 109 110 const CXFA_Node* CJX_Node::GetXFANode() const { 111 return static_cast<const CXFA_Node*>(GetXFAObject()); 112 } 113 114 CJS_Return CJX_Node::applyXSL(CJS_V8* runtime, 115 const std::vector<v8::Local<v8::Value>>& params) { 116 if (params.size() != 1) 117 return CJS_Return(JSGetStringFromID(JSMessage::kParamError)); 118 119 // TODO(weili): check whether we need to implement this, pdfium:501. 120 return CJS_Return(true); 121 } 122 123 CJS_Return CJX_Node::assignNode( 124 CJS_V8* runtime, 125 const std::vector<v8::Local<v8::Value>>& params) { 126 if (params.empty() || params.size() > 3) 127 return CJS_Return(JSGetStringFromID(JSMessage::kParamError)); 128 129 // TODO(weili): check whether we need to implement this, pdfium:501. 130 return CJS_Return(true); 131 } 132 133 CJS_Return CJX_Node::clone(CJS_V8* runtime, 134 const std::vector<v8::Local<v8::Value>>& params) { 135 if (params.size() != 1) 136 return CJS_Return(JSGetStringFromID(JSMessage::kParamError)); 137 138 CXFA_Node* pCloneNode = GetXFANode()->Clone(runtime->ToBoolean(params[0])); 139 CFXJSE_Value* value = 140 GetDocument()->GetScriptContext()->GetJSValueFromMap(pCloneNode); 141 if (!value) 142 return CJS_Return(runtime->NewNull()); 143 return CJS_Return(value->DirectGetValue().Get(runtime->GetIsolate())); 144 } 145 146 CJS_Return CJX_Node::getAttribute( 147 CJS_V8* runtime, 148 const std::vector<v8::Local<v8::Value>>& params) { 149 if (params.size() != 1) 150 return CJS_Return(JSGetStringFromID(JSMessage::kParamError)); 151 152 WideString expression = runtime->ToWideString(params[0]); 153 return CJS_Return(runtime->NewString( 154 GetAttribute(expression.AsStringView()).UTF8Encode().AsStringView())); 155 } 156 157 CJS_Return CJX_Node::getElement( 158 CJS_V8* runtime, 159 const std::vector<v8::Local<v8::Value>>& params) { 160 if (params.empty() || params.size() > 2) 161 return CJS_Return(JSGetStringFromID(JSMessage::kParamError)); 162 163 WideString expression = runtime->ToWideString(params[0]); 164 int32_t iValue = params.size() >= 2 ? runtime->ToInt32(params[1]) : 0; 165 166 CXFA_Node* pNode = GetOrCreateProperty<CXFA_Node>( 167 iValue, CXFA_Node::NameToElement(expression)); 168 if (!pNode) 169 return CJS_Return(runtime->NewNull()); 170 171 CFXJSE_Value* value = 172 GetDocument()->GetScriptContext()->GetJSValueFromMap(pNode); 173 if (!value) 174 return CJS_Return(runtime->NewNull()); 175 return CJS_Return(value->DirectGetValue().Get(runtime->GetIsolate())); 176 } 177 178 CJS_Return CJX_Node::isPropertySpecified( 179 CJS_V8* runtime, 180 const std::vector<v8::Local<v8::Value>>& params) { 181 if (params.empty() || params.size() > 3) 182 return CJS_Return(JSGetStringFromID(JSMessage::kParamError)); 183 184 WideString expression = runtime->ToWideString(params[0]); 185 XFA_Attribute attr = CXFA_Node::NameToAttribute(expression.AsStringView()); 186 if (attr != XFA_Attribute::Unknown && HasAttribute(attr)) 187 return CJS_Return(runtime->NewBoolean(true)); 188 189 bool bParent = params.size() < 2 || runtime->ToBoolean(params[1]); 190 int32_t iIndex = params.size() == 3 ? runtime->ToInt32(params[2]) : 0; 191 XFA_Element eType = CXFA_Node::NameToElement(expression); 192 bool bHas = !!GetOrCreateProperty<CXFA_Node>(iIndex, eType); 193 if (!bHas && bParent && GetXFANode()->GetParent()) { 194 // Also check on the parent. 195 auto* jsnode = GetXFANode()->GetParent()->JSObject(); 196 bHas = jsnode->HasAttribute(attr) || 197 !!jsnode->GetOrCreateProperty<CXFA_Node>(iIndex, eType); 198 } 199 return CJS_Return(runtime->NewBoolean(bHas)); 200 } 201 202 CJS_Return CJX_Node::loadXML(CJS_V8* runtime, 203 const std::vector<v8::Local<v8::Value>>& params) { 204 if (params.empty() || params.size() > 3) 205 return CJS_Return(JSGetStringFromID(JSMessage::kParamError)); 206 207 ByteString expression = runtime->ToByteString(params[0]); 208 if (expression.IsEmpty()) 209 return CJS_Return(true); 210 211 bool bIgnoreRoot = true; 212 if (params.size() >= 2) 213 bIgnoreRoot = runtime->ToBoolean(params[1]); 214 215 bool bOverwrite = 0; 216 if (params.size() >= 3) 217 bOverwrite = runtime->ToBoolean(params[2]); 218 219 auto pParser = pdfium::MakeUnique<CXFA_SimpleParser>(GetDocument()); 220 if (!pParser) 221 return CJS_Return(true); 222 223 CFX_XMLNode* pXMLNode = pParser->ParseXMLData(expression); 224 if (!pXMLNode) 225 return CJS_Return(true); 226 227 if (bIgnoreRoot && 228 (pXMLNode->GetType() != FX_XMLNODE_Element || 229 XFA_RecognizeRichText(static_cast<CFX_XMLElement*>(pXMLNode)))) { 230 bIgnoreRoot = false; 231 } 232 233 CXFA_Node* pFakeRoot = GetXFANode()->Clone(false); 234 WideString wsContentType = GetCData(XFA_Attribute::ContentType); 235 if (!wsContentType.IsEmpty()) { 236 pFakeRoot->JSObject()->SetCData(XFA_Attribute::ContentType, 237 WideString(wsContentType), false, false); 238 } 239 240 std::unique_ptr<CFX_XMLNode> pFakeXMLRoot(pFakeRoot->GetXMLMappingNode()); 241 if (!pFakeXMLRoot) { 242 CFX_XMLNode* pThisXMLRoot = GetXFANode()->GetXMLMappingNode(); 243 pFakeXMLRoot = pThisXMLRoot ? pThisXMLRoot->Clone() : nullptr; 244 } 245 if (!pFakeXMLRoot) { 246 pFakeXMLRoot = pdfium::MakeUnique<CFX_XMLElement>( 247 WideString(GetXFANode()->GetClassName())); 248 } 249 250 if (bIgnoreRoot) { 251 CFX_XMLNode* pXMLChild = pXMLNode->GetNodeItem(CFX_XMLNode::FirstChild); 252 while (pXMLChild) { 253 CFX_XMLNode* pXMLSibling = 254 pXMLChild->GetNodeItem(CFX_XMLNode::NextSibling); 255 pXMLNode->RemoveChildNode(pXMLChild); 256 pFakeXMLRoot->InsertChildNode(pXMLChild); 257 pXMLChild = pXMLSibling; 258 } 259 } else { 260 CFX_XMLNode* pXMLParent = pXMLNode->GetNodeItem(CFX_XMLNode::Parent); 261 if (pXMLParent) 262 pXMLParent->RemoveChildNode(pXMLNode); 263 264 pFakeXMLRoot->InsertChildNode(pXMLNode); 265 } 266 267 pParser->ConstructXFANode(pFakeRoot, pFakeXMLRoot.get()); 268 pFakeRoot = pParser->GetRootNode(); 269 if (!pFakeRoot) 270 return CJS_Return(true); 271 272 if (bOverwrite) { 273 CXFA_Node* pChild = GetXFANode()->GetFirstChild(); 274 CXFA_Node* pNewChild = pFakeRoot->GetFirstChild(); 275 int32_t index = 0; 276 while (pNewChild) { 277 CXFA_Node* pItem = pNewChild->GetNextSibling(); 278 pFakeRoot->RemoveChild(pNewChild, true); 279 GetXFANode()->InsertChild(index++, pNewChild); 280 pNewChild->SetFlag(XFA_NodeFlag_Initialized, true); 281 pNewChild = pItem; 282 } 283 284 while (pChild) { 285 CXFA_Node* pItem = pChild->GetNextSibling(); 286 GetXFANode()->RemoveChild(pChild, true); 287 pFakeRoot->InsertChild(pChild, nullptr); 288 pChild = pItem; 289 } 290 291 if (GetXFANode()->GetPacketType() == XFA_PacketType::Form && 292 GetXFANode()->GetElementType() == XFA_Element::ExData) { 293 CFX_XMLNode* pTempXMLNode = GetXFANode()->GetXMLMappingNode(); 294 GetXFANode()->SetXMLMappingNode(pFakeXMLRoot.release()); 295 GetXFANode()->SetFlag(XFA_NodeFlag_OwnXMLNode, false); 296 if (pTempXMLNode && !pTempXMLNode->GetNodeItem(CFX_XMLNode::Parent)) 297 pFakeXMLRoot.reset(pTempXMLNode); 298 else 299 pFakeXMLRoot = nullptr; 300 } 301 MoveBufferMapData(pFakeRoot, GetXFANode()); 302 } else { 303 CXFA_Node* pChild = pFakeRoot->GetFirstChild(); 304 while (pChild) { 305 CXFA_Node* pItem = pChild->GetNextSibling(); 306 pFakeRoot->RemoveChild(pChild, true); 307 GetXFANode()->InsertChild(pChild, nullptr); 308 pChild->SetFlag(XFA_NodeFlag_Initialized, true); 309 pChild = pItem; 310 } 311 } 312 313 if (pFakeXMLRoot) { 314 pFakeRoot->SetXMLMappingNode(pFakeXMLRoot.release()); 315 pFakeRoot->SetFlag(XFA_NodeFlag_OwnXMLNode, false); 316 } 317 pFakeRoot->SetFlag(XFA_NodeFlag_HasRemovedChildren, false); 318 319 return CJS_Return(true); 320 } 321 322 CJS_Return CJX_Node::saveFilteredXML( 323 CJS_V8* runtime, 324 const std::vector<v8::Local<v8::Value>>& params) { 325 // TODO(weili): Check whether we need to implement this, pdfium:501. 326 return CJS_Return(true); 327 } 328 329 CJS_Return CJX_Node::saveXML(CJS_V8* runtime, 330 const std::vector<v8::Local<v8::Value>>& params) { 331 if (params.size() > 1) 332 return CJS_Return(JSGetStringFromID(JSMessage::kParamError)); 333 if (params.size() == 1 && runtime->ToWideString(params[0]) != L"pretty") 334 return CJS_Return(JSGetStringFromID(JSMessage::kValueError)); 335 336 // TODO(weili): Check whether we need to save pretty print XML, pdfium:501. 337 338 WideString bsXMLHeader = L"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; 339 if (GetXFANode()->GetPacketType() != XFA_PacketType::Form && 340 GetXFANode()->GetPacketType() != XFA_PacketType::Datasets) { 341 return CJS_Return(runtime->NewString("")); 342 } 343 344 CFX_XMLNode* pElement = nullptr; 345 if (GetXFANode()->GetPacketType() == XFA_PacketType::Datasets) { 346 pElement = GetXFANode()->GetXMLMappingNode(); 347 if (!pElement || pElement->GetType() != FX_XMLNODE_Element) { 348 return CJS_Return( 349 runtime->NewString(bsXMLHeader.UTF8Encode().AsStringView())); 350 } 351 352 XFA_DataExporter_DealWithDataGroupNode(GetXFANode()); 353 } 354 355 auto pMemoryStream = pdfium::MakeRetain<CFX_MemoryStream>(true); 356 auto pStream = 357 pdfium::MakeRetain<CFX_SeekableStreamProxy>(pMemoryStream, true); 358 pStream->SetCodePage(FX_CODEPAGE_UTF8); 359 pStream->WriteString(bsXMLHeader.AsStringView()); 360 361 if (GetXFANode()->GetPacketType() == XFA_PacketType::Form) 362 XFA_DataExporter_RegenerateFormFile(GetXFANode(), pStream, nullptr, true); 363 else 364 pElement->SaveXMLNode(pStream); 365 366 return CJS_Return(runtime->NewString( 367 ByteStringView(pMemoryStream->GetBuffer(), pMemoryStream->GetSize()))); 368 } 369 370 CJS_Return CJX_Node::setAttribute( 371 CJS_V8* runtime, 372 const std::vector<v8::Local<v8::Value>>& params) { 373 if (params.size() != 2) 374 return CJS_Return(JSGetStringFromID(JSMessage::kParamError)); 375 376 WideString attributeValue = runtime->ToWideString(params[0]); 377 WideString attribute = runtime->ToWideString(params[1]); 378 SetAttribute(attribute.AsStringView(), attributeValue.AsStringView(), true); 379 return CJS_Return(true); 380 } 381 382 CJS_Return CJX_Node::setElement( 383 CJS_V8* runtime, 384 const std::vector<v8::Local<v8::Value>>& params) { 385 if (params.size() != 1 && params.size() != 2) 386 return CJS_Return(JSGetStringFromID(JSMessage::kParamError)); 387 388 // TODO(weili): check whether we need to implement this, pdfium:501. 389 return CJS_Return(true); 390 } 391 392 void CJX_Node::id(CFXJSE_Value* pValue, 393 bool bSetting, 394 XFA_Attribute eAttribute) { 395 Script_Attribute_String(pValue, bSetting, eAttribute); 396 } 397 398 void CJX_Node::ns(CFXJSE_Value* pValue, 399 bool bSetting, 400 XFA_Attribute eAttribute) { 401 if (bSetting) { 402 ThrowInvalidPropertyException(); 403 return; 404 } 405 pValue->SetString( 406 TryNamespace().value_or(WideString()).UTF8Encode().AsStringView()); 407 } 408 409 void CJX_Node::model(CFXJSE_Value* pValue, 410 bool bSetting, 411 XFA_Attribute eAttribute) { 412 if (bSetting) { 413 ThrowInvalidPropertyException(); 414 return; 415 } 416 pValue->Assign(GetDocument()->GetScriptContext()->GetJSValueFromMap( 417 GetXFANode()->GetModelNode())); 418 } 419 420 void CJX_Node::isContainer(CFXJSE_Value* pValue, 421 bool bSetting, 422 XFA_Attribute eAttribute) { 423 if (bSetting) { 424 ThrowInvalidPropertyException(); 425 return; 426 } 427 pValue->SetBoolean(GetXFANode()->IsContainerNode()); 428 } 429 430 void CJX_Node::isNull(CFXJSE_Value* pValue, 431 bool bSetting, 432 XFA_Attribute eAttribute) { 433 if (bSetting) { 434 ThrowInvalidPropertyException(); 435 return; 436 } 437 if (GetXFANode()->GetElementType() == XFA_Element::Subform) { 438 pValue->SetBoolean(false); 439 return; 440 } 441 pValue->SetBoolean(GetContent(false).IsEmpty()); 442 } 443 444 void CJX_Node::oneOfChild(CFXJSE_Value* pValue, 445 bool bSetting, 446 XFA_Attribute eAttribute) { 447 if (bSetting) { 448 ThrowInvalidPropertyException(); 449 return; 450 } 451 452 std::vector<CXFA_Node*> properties = GetXFANode()->GetNodeList( 453 XFA_NODEFILTER_OneOfProperty, XFA_Element::Unknown); 454 if (!properties.empty()) { 455 pValue->Assign(GetDocument()->GetScriptContext()->GetJSValueFromMap( 456 properties.front())); 457 } 458 } 459 460 int32_t CJX_Node::execSingleEventByName(const WideStringView& wsEventName, 461 XFA_Element eType) { 462 CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); 463 if (!pNotify) 464 return XFA_EVENTERROR_NotExist; 465 466 const XFA_ExecEventParaInfo* eventParaInfo = 467 GetEventParaInfoByName(wsEventName); 468 if (!eventParaInfo) 469 return XFA_EVENTERROR_NotExist; 470 471 switch (eventParaInfo->m_validFlags) { 472 case EventAppliesToo::kNone: 473 return XFA_EVENTERROR_NotExist; 474 case EventAppliesToo::kAll: 475 case EventAppliesToo::kAllNonRecursive: 476 return pNotify->ExecEventByDeepFirst( 477 GetXFANode(), eventParaInfo->m_eventType, false, 478 eventParaInfo->m_validFlags == EventAppliesToo::kAll); 479 case EventAppliesToo::kSubform: 480 if (eType != XFA_Element::Subform) 481 return XFA_EVENTERROR_NotExist; 482 483 return pNotify->ExecEventByDeepFirst( 484 GetXFANode(), eventParaInfo->m_eventType, false, false); 485 case EventAppliesToo::kFieldOrExclusion: { 486 if (eType != XFA_Element::ExclGroup && eType != XFA_Element::Field) 487 return XFA_EVENTERROR_NotExist; 488 489 CXFA_Node* pParentNode = GetXFANode()->GetParent(); 490 if (pParentNode && 491 pParentNode->GetElementType() == XFA_Element::ExclGroup) { 492 // TODO(dsinclair): This seems like a bug, we do the same work twice? 493 pNotify->ExecEventByDeepFirst(GetXFANode(), eventParaInfo->m_eventType, 494 false, false); 495 } 496 return pNotify->ExecEventByDeepFirst( 497 GetXFANode(), eventParaInfo->m_eventType, false, false); 498 } 499 case EventAppliesToo::kField: 500 if (eType != XFA_Element::Field) 501 return XFA_EVENTERROR_NotExist; 502 503 return pNotify->ExecEventByDeepFirst( 504 GetXFANode(), eventParaInfo->m_eventType, false, false); 505 case EventAppliesToo::kSignature: { 506 CXFA_WidgetAcc* pWidgetAcc = GetXFANode()->GetWidgetAcc(); 507 if (!pWidgetAcc) 508 return XFA_EVENTERROR_NotExist; 509 510 CXFA_Node* pUINode = pWidgetAcc->GetUIChild(); 511 if (pUINode->GetElementType() != XFA_Element::Signature) 512 return XFA_EVENTERROR_NotExist; 513 514 return pNotify->ExecEventByDeepFirst( 515 GetXFANode(), eventParaInfo->m_eventType, false, false); 516 } 517 case EventAppliesToo::kChoiceList: { 518 CXFA_WidgetAcc* pWidgetAcc = GetXFANode()->GetWidgetAcc(); 519 if (!pWidgetAcc) 520 return XFA_EVENTERROR_NotExist; 521 522 CXFA_Node* pUINode = pWidgetAcc->GetUIChild(); 523 if (pUINode->GetElementType() != XFA_Element::ChoiceList || 524 pWidgetAcc->IsListBox()) { 525 return XFA_EVENTERROR_NotExist; 526 } 527 return pNotify->ExecEventByDeepFirst( 528 GetXFANode(), eventParaInfo->m_eventType, false, false); 529 } 530 } 531 return XFA_EVENTERROR_NotExist; 532 } 533