|  | // Copyright 2014 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 "xfa/fxfa/cxfa_ffdocview.h" | 
|  |  | 
|  | #include <set> | 
|  | #include <utility> | 
|  |  | 
|  | #include "core/fxcrt/check_op.h" | 
|  | #include "core/fxcrt/containers/contains.h" | 
|  | #include "core/fxcrt/fx_extension.h" | 
|  | #include "core/fxcrt/stl_util.h" | 
|  | #include "core/fxcrt/xml/cfx_xmlparser.h" | 
|  | #include "fxjs/gc/container_trace.h" | 
|  | #include "fxjs/xfa/cfxjse_engine.h" | 
|  | #include "fxjs/xfa/cjx_object.h" | 
|  | #include "xfa/fxfa/cxfa_ffapp.h" | 
|  | #include "xfa/fxfa/cxfa_ffbarcode.h" | 
|  | #include "xfa/fxfa/cxfa_ffcheckbutton.h" | 
|  | #include "xfa/fxfa/cxfa_ffdoc.h" | 
|  | #include "xfa/fxfa/cxfa_ffexclgroup.h" | 
|  | #include "xfa/fxfa/cxfa_fffield.h" | 
|  | #include "xfa/fxfa/cxfa_ffimage.h" | 
|  | #include "xfa/fxfa/cxfa_ffimageedit.h" | 
|  | #include "xfa/fxfa/cxfa_ffpageview.h" | 
|  | #include "xfa/fxfa/cxfa_ffpushbutton.h" | 
|  | #include "xfa/fxfa/cxfa_ffsignature.h" | 
|  | #include "xfa/fxfa/cxfa_fftext.h" | 
|  | #include "xfa/fxfa/cxfa_ffwidget.h" | 
|  | #include "xfa/fxfa/cxfa_ffwidgethandler.h" | 
|  | #include "xfa/fxfa/cxfa_fwladapterwidgetmgr.h" | 
|  | #include "xfa/fxfa/cxfa_readynodeiterator.h" | 
|  | #include "xfa/fxfa/cxfa_textprovider.h" | 
|  | #include "xfa/fxfa/layout/cxfa_layoutprocessor.h" | 
|  | #include "xfa/fxfa/parser/cxfa_acrobat.h" | 
|  | #include "xfa/fxfa/parser/cxfa_binditems.h" | 
|  | #include "xfa/fxfa/parser/cxfa_calculate.h" | 
|  | #include "xfa/fxfa/parser/cxfa_pageset.h" | 
|  | #include "xfa/fxfa/parser/cxfa_present.h" | 
|  | #include "xfa/fxfa/parser/cxfa_subform.h" | 
|  | #include "xfa/fxfa/parser/cxfa_validate.h" | 
|  | #include "xfa/fxfa/parser/xfa_utils.h" | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | bool IsValidXMLNameString(const WideString& str) { | 
|  | bool first = true; | 
|  | for (const auto ch : str) { | 
|  | if (!CFX_XMLParser::IsXMLNameChar(ch, first)) { | 
|  | return false; | 
|  | } | 
|  | first = false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | const XFA_AttributeValue kXFAEventActivityData[] = { | 
|  | XFA_AttributeValue::Click,      XFA_AttributeValue::Change, | 
|  | XFA_AttributeValue::DocClose,   XFA_AttributeValue::DocReady, | 
|  | XFA_AttributeValue::Enter,      XFA_AttributeValue::Exit, | 
|  | XFA_AttributeValue::Full,       XFA_AttributeValue::IndexChange, | 
|  | XFA_AttributeValue::Initialize, XFA_AttributeValue::MouseDown, | 
|  | XFA_AttributeValue::MouseEnter, XFA_AttributeValue::MouseExit, | 
|  | XFA_AttributeValue::MouseUp,    XFA_AttributeValue::PostExecute, | 
|  | XFA_AttributeValue::PostOpen,   XFA_AttributeValue::PostPrint, | 
|  | XFA_AttributeValue::PostSave,   XFA_AttributeValue::PostSign, | 
|  | XFA_AttributeValue::PostSubmit, XFA_AttributeValue::PreExecute, | 
|  | XFA_AttributeValue::PreOpen,    XFA_AttributeValue::PrePrint, | 
|  | XFA_AttributeValue::PreSave,    XFA_AttributeValue::PreSign, | 
|  | XFA_AttributeValue::PreSubmit,  XFA_AttributeValue::Ready, | 
|  | XFA_AttributeValue::Unknown, | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | const pdfium::span<const XFA_AttributeValue> kXFAEventActivity{ | 
|  | kXFAEventActivityData}; | 
|  |  | 
|  | CXFA_FFDocView::UpdateScope::UpdateScope(CXFA_FFDocView* pDocView) | 
|  | : doc_view_(pDocView) { | 
|  | doc_view_->LockUpdate(); | 
|  | } | 
|  |  | 
|  | CXFA_FFDocView::UpdateScope::~UpdateScope() { | 
|  | doc_view_->UnlockUpdate(); | 
|  | doc_view_->UpdateDocView(); | 
|  | } | 
|  |  | 
|  | CXFA_FFDocView::CXFA_FFDocView(CXFA_FFDoc* doc) : doc_(doc) {} | 
|  |  | 
|  | CXFA_FFDocView::~CXFA_FFDocView() = default; | 
|  |  | 
|  | void CXFA_FFDocView::Trace(cppgc::Visitor* visitor) const { | 
|  | visitor->Trace(doc_); | 
|  | visitor->Trace(widget_handler_); | 
|  | visitor->Trace(focus_node_); | 
|  | visitor->Trace(focus_widget_); | 
|  | ContainerTrace(visitor, validate_nodes_); | 
|  | ContainerTrace(visitor, calculate_nodes_); | 
|  | ContainerTrace(visitor, new_added_nodes_); | 
|  | ContainerTrace(visitor, bind_items_); | 
|  | ContainerTrace(visitor, index_changed_subforms_); | 
|  | } | 
|  |  | 
|  | void CXFA_FFDocView::InitLayout(CXFA_Node* pNode) { | 
|  | RunBindItems(); | 
|  | ExecEventActivityByDeepFirst(pNode, XFA_EVENT_Initialize, false, true); | 
|  | ExecEventActivityByDeepFirst(pNode, XFA_EVENT_IndexChange, false, true); | 
|  | } | 
|  |  | 
|  | int32_t CXFA_FFDocView::StartLayout() { | 
|  | status_ = LayoutStatus::kStart; | 
|  | doc_->GetXFADoc()->DoProtoMerge(); | 
|  | doc_->GetXFADoc()->DoDataMerge(); | 
|  |  | 
|  | int32_t iStatus = GetLayoutProcessor()->StartLayout(); | 
|  | if (iStatus < 0) { | 
|  | return iStatus; | 
|  | } | 
|  |  | 
|  | CXFA_Node* pRootItem = | 
|  | ToNode(doc_->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Form)); | 
|  | if (!pRootItem) { | 
|  | return iStatus; | 
|  | } | 
|  |  | 
|  | InitLayout(pRootItem); | 
|  | InitCalculate(pRootItem); | 
|  | InitValidate(pRootItem); | 
|  |  | 
|  | ExecEventActivityByDeepFirst(pRootItem, XFA_EVENT_Ready, true, true); | 
|  | status_ = LayoutStatus::kStart; | 
|  | return iStatus; | 
|  | } | 
|  |  | 
|  | int32_t CXFA_FFDocView::DoLayout() { | 
|  | int32_t iStatus = GetLayoutProcessor()->DoLayout(); | 
|  | if (iStatus != 100) { | 
|  | return iStatus; | 
|  | } | 
|  |  | 
|  | status_ = LayoutStatus::kDoing; | 
|  | return iStatus; | 
|  | } | 
|  |  | 
|  | void CXFA_FFDocView::StopLayout() { | 
|  | CXFA_Node* pRootItem = | 
|  | ToNode(doc_->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Form)); | 
|  | if (!pRootItem) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | CXFA_Subform* pSubformNode = | 
|  | pRootItem->GetChild<CXFA_Subform>(0, XFA_Element::Subform, false); | 
|  | if (!pSubformNode) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | CXFA_PageSet* pPageSetNode = | 
|  | pSubformNode->GetFirstChildByClass<CXFA_PageSet>(XFA_Element::PageSet); | 
|  | if (!pPageSetNode) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | RunCalculateWidgets(); | 
|  | RunValidate(); | 
|  |  | 
|  | InitLayout(pPageSetNode); | 
|  | InitCalculate(pPageSetNode); | 
|  | InitValidate(pPageSetNode); | 
|  |  | 
|  | ExecEventActivityByDeepFirst(pPageSetNode, XFA_EVENT_Ready, true, true); | 
|  | ExecEventActivityByDeepFirst(pRootItem, XFA_EVENT_Ready, false, true); | 
|  | ExecEventActivityByDeepFirst(pRootItem, XFA_EVENT_DocReady, false, true); | 
|  |  | 
|  | RunCalculateWidgets(); | 
|  | RunValidate(); | 
|  |  | 
|  | if (RunLayout()) { | 
|  | ExecEventActivityByDeepFirst(pRootItem, XFA_EVENT_Ready, false, true); | 
|  | } | 
|  |  | 
|  | calculate_nodes_.clear(); | 
|  | if (focus_node_ && !focus_widget_) { | 
|  | SetFocusNode(focus_node_); | 
|  | } | 
|  |  | 
|  | status_ = LayoutStatus::kEnd; | 
|  | } | 
|  |  | 
|  | void CXFA_FFDocView::AddNullTestMsg(const WideString& msg) { | 
|  | null_test_msg_array_.push_back(msg); | 
|  | } | 
|  |  | 
|  | void CXFA_FFDocView::ShowNullTestMsg() { | 
|  | int32_t iCount = fxcrt::CollectionSize<int32_t>(null_test_msg_array_); | 
|  | CXFA_FFApp* pApp = doc_->GetApp(); | 
|  | CXFA_FFApp::CallbackIface* pAppProvider = pApp->GetAppProvider(); | 
|  | if (pAppProvider && iCount) { | 
|  | int32_t remaining = iCount > 7 ? iCount - 7 : 0; | 
|  | iCount -= remaining; | 
|  | WideString wsMsg; | 
|  | for (int32_t i = 0; i < iCount; i++) { | 
|  | wsMsg += null_test_msg_array_[i] + L"\n"; | 
|  | } | 
|  |  | 
|  | if (remaining > 0) { | 
|  | wsMsg += L"\n" + WideString::Format( | 
|  | L"Message limit exceeded. Remaining %d " | 
|  | L"validation errors not reported.", | 
|  | remaining); | 
|  | } | 
|  | pAppProvider->MsgBox(wsMsg, pAppProvider->GetAppTitle(), | 
|  | static_cast<uint32_t>(AlertIcon::kStatus), | 
|  | static_cast<uint32_t>(AlertButton::kOK)); | 
|  | } | 
|  | null_test_msg_array_.clear(); | 
|  | } | 
|  |  | 
|  | void CXFA_FFDocView::UpdateDocView() { | 
|  | if (IsUpdateLocked()) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | LockUpdate(); | 
|  | while (!new_added_nodes_.empty()) { | 
|  | CXFA_Node* pNode = new_added_nodes_.front(); | 
|  | new_added_nodes_.pop_front(); | 
|  | InitCalculate(pNode); | 
|  | InitValidate(pNode); | 
|  | ExecEventActivityByDeepFirst(pNode, XFA_EVENT_Ready, true, true); | 
|  | } | 
|  |  | 
|  | RunSubformIndexChange(); | 
|  | RunCalculateWidgets(); | 
|  | RunValidate(); | 
|  |  | 
|  | ShowNullTestMsg(); | 
|  |  | 
|  | if (RunLayout() && layout_event_) { | 
|  | RunEventLayoutReady(); | 
|  | } | 
|  |  | 
|  | layout_event_ = false; | 
|  | calculate_nodes_.clear(); | 
|  | UnlockUpdate(); | 
|  | } | 
|  |  | 
|  | void CXFA_FFDocView::UpdateUIDisplay(CXFA_Node* pNode, CXFA_FFWidget* pExcept) { | 
|  | CXFA_FFWidget* pWidget = GetWidgetForNode(pNode); | 
|  | CXFA_FFWidget* pNext = nullptr; | 
|  | for (; pWidget; pWidget = pNext) { | 
|  | pNext = pWidget->GetNextFFWidget(); | 
|  | if (pWidget == pExcept || !pWidget->IsLoaded() || | 
|  | (pNode->GetFFWidgetType() != XFA_FFWidgetType::kCheckButton && | 
|  | pWidget->IsFocused())) { | 
|  | continue; | 
|  | } | 
|  | pWidget->UpdateFWLData(); | 
|  | pWidget->InvalidateRect(); | 
|  | } | 
|  | } | 
|  |  | 
|  | int32_t CXFA_FFDocView::CountPageViews() const { | 
|  | CXFA_LayoutProcessor* pProcessor = GetLayoutProcessor(); | 
|  | return pProcessor ? pProcessor->CountPages() : 0; | 
|  | } | 
|  |  | 
|  | CXFA_FFPageView* CXFA_FFDocView::GetPageView(int32_t nIndex) const { | 
|  | CXFA_LayoutProcessor* pProcessor = GetLayoutProcessor(); | 
|  | if (!pProcessor) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | auto* pPage = pProcessor->GetPage(nIndex); | 
|  | return pPage ? pPage->GetPageView() : nullptr; | 
|  | } | 
|  |  | 
|  | CXFA_LayoutProcessor* CXFA_FFDocView::GetLayoutProcessor() const { | 
|  | return CXFA_LayoutProcessor::FromDocument(doc_->GetXFADoc()); | 
|  | } | 
|  |  | 
|  | bool CXFA_FFDocView::ResetSingleNodeData(CXFA_Node* pNode) { | 
|  | XFA_Element eType = pNode->GetElementType(); | 
|  | if (eType != XFA_Element::Field && eType != XFA_Element::ExclGroup) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | pNode->ResetData(); | 
|  | UpdateUIDisplay(pNode, nullptr); | 
|  | CXFA_Validate* validate = pNode->GetValidateIfExists(); | 
|  | if (!validate) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | AddValidateNode(pNode); | 
|  | validate->SetFlag(XFA_NodeFlag::kNeedsInitApp); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void CXFA_FFDocView::ResetNode(CXFA_Node* pNode) { | 
|  | layout_event_ = true; | 
|  | bool bChanged = false; | 
|  | CXFA_Node* pFormNode = nullptr; | 
|  | if (pNode) { | 
|  | bChanged = ResetSingleNodeData(pNode); | 
|  | pFormNode = pNode; | 
|  | } else { | 
|  | pFormNode = GetRootSubform(); | 
|  | } | 
|  | if (!pFormNode) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (pFormNode->GetElementType() != XFA_Element::Field && | 
|  | pFormNode->GetElementType() != XFA_Element::ExclGroup) { | 
|  | CXFA_ReadyNodeIterator it(pFormNode); | 
|  | while (CXFA_Node* next_node = it.MoveToNext()) { | 
|  | bChanged |= ResetSingleNodeData(next_node); | 
|  | if (next_node->GetElementType() == XFA_Element::ExclGroup) { | 
|  | it.SkipTree(); | 
|  | } | 
|  | } | 
|  | } | 
|  | if (bChanged) { | 
|  | doc_->SetChangeMark(); | 
|  | } | 
|  | } | 
|  |  | 
|  | CXFA_FFWidget* CXFA_FFDocView::GetWidgetForNode(CXFA_Node* node) { | 
|  | return GetFFWidget( | 
|  | ToContentLayoutItem(GetLayoutProcessor()->GetLayoutItem(node))); | 
|  | } | 
|  |  | 
|  | CXFA_FFWidgetHandler* CXFA_FFDocView::GetWidgetHandler() { | 
|  | if (!widget_handler_) { | 
|  | widget_handler_ = cppgc::MakeGarbageCollected<CXFA_FFWidgetHandler>( | 
|  | doc_->GetHeap()->GetAllocationHandle(), this); | 
|  | } | 
|  | return widget_handler_; | 
|  | } | 
|  |  | 
|  | bool CXFA_FFDocView::SetFocus(CXFA_FFWidget* pNewFocus) { | 
|  | if (pNewFocus == focus_widget_) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (focus_widget_) { | 
|  | CXFA_ContentLayoutItem* pItem = focus_widget_->GetLayoutItem(); | 
|  | if (pItem->TestStatusBits(XFA_WidgetStatus::kVisible) && | 
|  | !pItem->TestStatusBits(XFA_WidgetStatus::kFocused)) { | 
|  | if (!focus_widget_->IsLoaded()) { | 
|  | focus_widget_->LoadWidget(); | 
|  | } | 
|  | if (!focus_widget_->OnSetFocus(focus_widget_)) { | 
|  | focus_widget_.Clear(); | 
|  | } | 
|  | } | 
|  | } | 
|  | if (focus_widget_) { | 
|  | if (!focus_widget_->OnKillFocus(pNewFocus)) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (pNewFocus) { | 
|  | if (pNewFocus->GetLayoutItem()->TestStatusBits( | 
|  | XFA_WidgetStatus::kVisible)) { | 
|  | if (!pNewFocus->IsLoaded()) { | 
|  | pNewFocus->LoadWidget(); | 
|  | } | 
|  | if (!pNewFocus->OnSetFocus(focus_widget_)) { | 
|  | pNewFocus = nullptr; | 
|  | } | 
|  | } | 
|  | } | 
|  | if (pNewFocus) { | 
|  | CXFA_Node* node = pNewFocus->GetNode(); | 
|  | focus_node_ = node->IsWidgetReady() ? node : nullptr; | 
|  | focus_widget_ = pNewFocus; | 
|  | } else { | 
|  | focus_node_.Clear(); | 
|  | focus_widget_.Clear(); | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void CXFA_FFDocView::SetFocusNode(CXFA_Node* node) { | 
|  | CXFA_FFWidget* pNewFocus = node ? GetWidgetForNode(node) : nullptr; | 
|  | if (!SetFocus(pNewFocus)) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | focus_node_ = node; | 
|  | if (status_ != LayoutStatus::kEnd) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | doc_->SetFocusWidget(focus_widget_); | 
|  | } | 
|  |  | 
|  | void CXFA_FFDocView::DeleteLayoutItem(CXFA_FFWidget* pWidget) { | 
|  | if (focus_node_ != pWidget->GetNode()) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | focus_node_.Clear(); | 
|  | focus_widget_.Clear(); | 
|  | } | 
|  |  | 
|  | static XFA_EventError XFA_ProcessEvent(CXFA_FFDocView* pDocView, | 
|  | CXFA_Node* pNode, | 
|  | CXFA_EventParam* pParam) { | 
|  | if (!pParam || pParam->type_ == XFA_EVENT_Unknown) { | 
|  | return XFA_EventError::kNotExist; | 
|  | } | 
|  | if (pNode && pNode->GetElementType() == XFA_Element::Draw) { | 
|  | return XFA_EventError::kNotExist; | 
|  | } | 
|  |  | 
|  | switch (pParam->type_) { | 
|  | case XFA_EVENT_Calculate: | 
|  | return pNode->ProcessCalculate(pDocView); | 
|  | case XFA_EVENT_Validate: | 
|  | if (pDocView->GetDoc()->IsValidationsEnabled()) { | 
|  | return pNode->ProcessValidate(pDocView, 0x01); | 
|  | } | 
|  | return XFA_EventError::kDisabled; | 
|  | case XFA_EVENT_InitCalculate: { | 
|  | CXFA_Calculate* calc = pNode->GetCalculateIfExists(); | 
|  | if (!calc) { | 
|  | return XFA_EventError::kNotExist; | 
|  | } | 
|  | if (pNode->IsUserInteractive()) { | 
|  | return XFA_EventError::kDisabled; | 
|  | } | 
|  | return pNode->ExecuteScript(pDocView, calc->GetScriptIfExists(), pParam); | 
|  | } | 
|  | default: | 
|  | return pNode->ProcessEvent(pDocView, kXFAEventActivity[pParam->type_], | 
|  | pParam); | 
|  | } | 
|  | } | 
|  |  | 
|  | XFA_EventError CXFA_FFDocView::ExecEventActivityByDeepFirst( | 
|  | CXFA_Node* pFormNode, | 
|  | XFA_EVENTTYPE eEventType, | 
|  | bool bIsFormReady, | 
|  | bool bRecursive) { | 
|  | if (!pFormNode) { | 
|  | return XFA_EventError::kNotExist; | 
|  | } | 
|  |  | 
|  | XFA_Element elementType = pFormNode->GetElementType(); | 
|  | if (elementType == XFA_Element::Field) { | 
|  | if (eEventType == XFA_EVENT_IndexChange) { | 
|  | return XFA_EventError::kNotExist; | 
|  | } | 
|  |  | 
|  | if (!pFormNode->IsWidgetReady()) { | 
|  | return XFA_EventError::kNotExist; | 
|  | } | 
|  |  | 
|  | CXFA_EventParam eParam(eEventType); | 
|  | eParam.is_form_ready_ = bIsFormReady; | 
|  | return XFA_ProcessEvent(this, pFormNode, &eParam); | 
|  | } | 
|  |  | 
|  | XFA_EventError iRet = XFA_EventError::kNotExist; | 
|  | if (bRecursive) { | 
|  | for (CXFA_Node* pNode = pFormNode->GetFirstContainerChild(); pNode; | 
|  | pNode = pNode->GetNextContainerSibling()) { | 
|  | elementType = pNode->GetElementType(); | 
|  | if (elementType != XFA_Element::Variables && | 
|  | elementType != XFA_Element::Draw) { | 
|  | XFA_EventErrorAccumulate( | 
|  | &iRet, ExecEventActivityByDeepFirst(pNode, eEventType, bIsFormReady, | 
|  | bRecursive)); | 
|  | } | 
|  | } | 
|  | } | 
|  | if (!pFormNode->IsWidgetReady()) { | 
|  | return iRet; | 
|  | } | 
|  |  | 
|  | CXFA_EventParam eParam(eEventType); | 
|  | eParam.is_form_ready_ = bIsFormReady; | 
|  |  | 
|  | XFA_EventErrorAccumulate(&iRet, XFA_ProcessEvent(this, pFormNode, &eParam)); | 
|  | return iRet; | 
|  | } | 
|  |  | 
|  | CXFA_FFWidget* CXFA_FFDocView::GetWidgetByName(const WideString& wsName, | 
|  | CXFA_FFWidget* pRefWidget) { | 
|  | if (!IsValidXMLNameString(wsName)) { | 
|  | return nullptr; | 
|  | } | 
|  | CFXJSE_Engine* pScriptContext = doc_->GetXFADoc()->GetScriptContext(); | 
|  | CXFA_Node* pRefNode = nullptr; | 
|  | if (pRefWidget) { | 
|  | CXFA_Node* node = pRefWidget->GetNode(); | 
|  | pRefNode = node->IsWidgetReady() ? node : nullptr; | 
|  | } | 
|  | WideString wsExpression = (!pRefNode ? L"$form." : L"") + wsName; | 
|  | std::optional<CFXJSE_Engine::ResolveResult> maybeResult = | 
|  | pScriptContext->ResolveObjects( | 
|  | pRefNode, wsExpression.AsStringView(), | 
|  | Mask<XFA_ResolveFlag>{ | 
|  | XFA_ResolveFlag::kChildren, XFA_ResolveFlag::kProperties, | 
|  | XFA_ResolveFlag::kSiblings, XFA_ResolveFlag::kParent}); | 
|  | if (!maybeResult.has_value()) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | if (maybeResult.value().type == CFXJSE_Engine::ResolveResult::Type::kNodes) { | 
|  | CXFA_Node* pNode = maybeResult.value().objects.front()->AsNode(); | 
|  | if (pNode && pNode->IsWidgetReady()) { | 
|  | return GetWidgetForNode(pNode); | 
|  | } | 
|  | } | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | void CXFA_FFDocView::OnPageViewEvent(CXFA_ViewLayoutItem* pSender, | 
|  | CXFA_FFDoc::PageViewEvent eEvent) { | 
|  | CXFA_FFPageView* pFFPageView = pSender ? pSender->GetPageView() : nullptr; | 
|  | doc_->OnPageViewEvent(pFFPageView, eEvent); | 
|  | } | 
|  |  | 
|  | void CXFA_FFDocView::InvalidateRect(CXFA_FFPageView* pPageView, | 
|  | const CFX_RectF& rtInvalidate) { | 
|  | doc_->InvalidateRect(pPageView, rtInvalidate); | 
|  | } | 
|  |  | 
|  | bool CXFA_FFDocView::RunLayout() { | 
|  | LockUpdate(); | 
|  | in_layout_status_ = true; | 
|  |  | 
|  | CXFA_LayoutProcessor* pProcessor = GetLayoutProcessor(); | 
|  | if (!pProcessor->IncrementLayout() && pProcessor->StartLayout() < 100) { | 
|  | pProcessor->DoLayout(); | 
|  | UnlockUpdate(); | 
|  | in_layout_status_ = false; | 
|  | doc_->OnPageViewEvent(nullptr, CXFA_FFDoc::PageViewEvent::kStopLayout); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | in_layout_status_ = false; | 
|  | doc_->OnPageViewEvent(nullptr, CXFA_FFDoc::PageViewEvent::kStopLayout); | 
|  | UnlockUpdate(); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void CXFA_FFDocView::RunSubformIndexChange() { | 
|  | std::set<CXFA_Node*> seen; | 
|  | while (!index_changed_subforms_.empty()) { | 
|  | CXFA_Node* pSubformNode = index_changed_subforms_.front(); | 
|  | index_changed_subforms_.pop_front(); | 
|  | bool bInserted = seen.insert(pSubformNode).second; | 
|  | if (!bInserted || !pSubformNode->IsWidgetReady()) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | CXFA_EventParam eParam(XFA_EVENT_IndexChange); | 
|  | pSubformNode->ProcessEvent(this, XFA_AttributeValue::IndexChange, &eParam); | 
|  | } | 
|  | } | 
|  |  | 
|  | void CXFA_FFDocView::AddNewFormNode(CXFA_Node* pNode) { | 
|  | new_added_nodes_.push_back(pNode); | 
|  | InitLayout(pNode); | 
|  | } | 
|  |  | 
|  | void CXFA_FFDocView::AddIndexChangedSubform(CXFA_Subform* pNode) { | 
|  | if (!pdfium::Contains(index_changed_subforms_, pNode)) { | 
|  | index_changed_subforms_.push_back(pNode); | 
|  | } | 
|  | } | 
|  |  | 
|  | void CXFA_FFDocView::RunDocClose() { | 
|  | CXFA_Node* pRootItem = | 
|  | ToNode(doc_->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Form)); | 
|  | if (!pRootItem) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | ExecEventActivityByDeepFirst(pRootItem, XFA_EVENT_DocClose, false, true); | 
|  | } | 
|  |  | 
|  | void CXFA_FFDocView::AddCalculateNode(CXFA_Node* node) { | 
|  | CXFA_Node* pCurrentNode = | 
|  | !calculate_nodes_.empty() ? calculate_nodes_.back() : nullptr; | 
|  | if (pCurrentNode != node) { | 
|  | calculate_nodes_.push_back(node); | 
|  | } | 
|  | } | 
|  |  | 
|  | void CXFA_FFDocView::AddCalculateNodeNotify(CXFA_Node* pNodeChange) { | 
|  | CJX_Object::CalcData* pGlobalData = pNodeChange->JSObject()->GetCalcData(); | 
|  | if (!pGlobalData) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | for (auto& pResult : pGlobalData->globals_) { | 
|  | if (!pResult->HasRemovedChildren() && pResult->IsWidgetReady()) { | 
|  | AddCalculateNode(pResult); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | size_t CXFA_FFDocView::RunCalculateRecursive(size_t index) { | 
|  | while (index < calculate_nodes_.size()) { | 
|  | CXFA_Node* node = calculate_nodes_[index]; | 
|  |  | 
|  | AddCalculateNodeNotify(node); | 
|  | size_t recurse = node->JSObject()->GetCalcRecursionCount() + 1; | 
|  | node->JSObject()->SetCalcRecursionCount(recurse); | 
|  | if (recurse > 11) { | 
|  | break; | 
|  | } | 
|  | if (node->ProcessCalculate(this) == XFA_EventError::kSuccess && | 
|  | node->IsWidgetReady()) { | 
|  | AddValidateNode(node); | 
|  | } | 
|  |  | 
|  | index = RunCalculateRecursive(++index); | 
|  | } | 
|  | return index; | 
|  | } | 
|  |  | 
|  | XFA_EventError CXFA_FFDocView::RunCalculateWidgets() { | 
|  | if (!doc_->IsCalculationsEnabled()) { | 
|  | return XFA_EventError::kDisabled; | 
|  | } | 
|  |  | 
|  | if (!calculate_nodes_.empty()) { | 
|  | RunCalculateRecursive(0); | 
|  | } | 
|  |  | 
|  | for (CXFA_Node* node : calculate_nodes_) { | 
|  | node->JSObject()->SetCalcRecursionCount(0); | 
|  | } | 
|  |  | 
|  | calculate_nodes_.clear(); | 
|  | return XFA_EventError::kSuccess; | 
|  | } | 
|  |  | 
|  | void CXFA_FFDocView::AddValidateNode(CXFA_Node* node) { | 
|  | if (!pdfium::Contains(validate_nodes_, node)) { | 
|  | validate_nodes_.push_back(node); | 
|  | } | 
|  | } | 
|  |  | 
|  | void CXFA_FFDocView::InitCalculate(CXFA_Node* pNode) { | 
|  | ExecEventActivityByDeepFirst(pNode, XFA_EVENT_InitCalculate, false, true); | 
|  | } | 
|  |  | 
|  | void CXFA_FFDocView::ProcessValueChanged(CXFA_Node* node) { | 
|  | AddValidateNode(node); | 
|  | AddCalculateNode(node); | 
|  | RunCalculateWidgets(); | 
|  | RunValidate(); | 
|  | } | 
|  |  | 
|  | void CXFA_FFDocView::InitValidate(CXFA_Node* pNode) { | 
|  | if (!doc_->IsValidationsEnabled()) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | ExecEventActivityByDeepFirst(pNode, XFA_EVENT_Validate, false, true); | 
|  | validate_nodes_.clear(); | 
|  | } | 
|  |  | 
|  | void CXFA_FFDocView::RunValidate() { | 
|  | if (!doc_->IsValidationsEnabled()) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | while (!validate_nodes_.empty()) { | 
|  | CXFA_Node* node = validate_nodes_.front(); | 
|  | validate_nodes_.pop_front(); | 
|  | if (!node->HasRemovedChildren()) { | 
|  | node->ProcessValidate(this, 0); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | bool CXFA_FFDocView::RunEventLayoutReady() { | 
|  | CXFA_Node* pRootItem = | 
|  | ToNode(doc_->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Form)); | 
|  | if (!pRootItem) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | ExecEventActivityByDeepFirst(pRootItem, XFA_EVENT_Ready, false, true); | 
|  | RunLayout(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void CXFA_FFDocView::RunBindItems() { | 
|  | while (!bind_items_.empty()) { | 
|  | CXFA_BindItems* item = bind_items_.front(); | 
|  | bind_items_.pop_front(); | 
|  | if (item->HasRemovedChildren()) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | CXFA_Node* pWidgetNode = item->GetParent(); | 
|  | if (!pWidgetNode || !pWidgetNode->IsWidgetReady()) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | CFXJSE_Engine* pScriptContext = | 
|  | pWidgetNode->GetDocument()->GetScriptContext(); | 
|  | WideString wsRef = item->GetRef(); | 
|  | std::optional<CFXJSE_Engine::ResolveResult> maybeRS = | 
|  | pScriptContext->ResolveObjects( | 
|  | pWidgetNode, wsRef.AsStringView(), | 
|  | Mask<XFA_ResolveFlag>{ | 
|  | XFA_ResolveFlag::kChildren, XFA_ResolveFlag::kProperties, | 
|  | XFA_ResolveFlag::kSiblings, XFA_ResolveFlag::kParent, | 
|  | XFA_ResolveFlag::kALL}); | 
|  | pWidgetNode->DeleteItem(-1, false, false); | 
|  | if (!maybeRS.has_value() || | 
|  | maybeRS.value().type != CFXJSE_Engine::ResolveResult::Type::kNodes || | 
|  | maybeRS.value().objects.empty()) { | 
|  | continue; | 
|  | } | 
|  | WideString wsValueRef = item->GetValueRef(); | 
|  | WideString wsLabelRef = item->GetLabelRef(); | 
|  | const bool bUseValue = wsLabelRef.IsEmpty() || wsLabelRef == wsValueRef; | 
|  | const bool bLabelUseContent = | 
|  | wsLabelRef.IsEmpty() || wsLabelRef.EqualsASCII("$"); | 
|  | const bool bValueUseContent = | 
|  | wsValueRef.IsEmpty() || wsValueRef.EqualsASCII("$"); | 
|  | WideString wsValue; | 
|  | WideString wsLabel; | 
|  | uint32_t uValueHash = FX_HashCode_GetW(wsValueRef.AsStringView()); | 
|  | for (auto& refObject : maybeRS.value().objects) { | 
|  | CXFA_Node* refNode = refObject->AsNode(); | 
|  | if (!refNode) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (bValueUseContent) { | 
|  | wsValue = refNode->JSObject()->GetContent(false); | 
|  | } else { | 
|  | CXFA_Node* nodeValue = refNode->GetFirstChildByName(uValueHash); | 
|  | wsValue = nodeValue ? nodeValue->JSObject()->GetContent(false) | 
|  | : refNode->JSObject()->GetContent(false); | 
|  | } | 
|  |  | 
|  | if (!bUseValue) { | 
|  | if (bLabelUseContent) { | 
|  | wsLabel = refNode->JSObject()->GetContent(false); | 
|  | } else { | 
|  | CXFA_Node* nodeLabel = | 
|  | refNode->GetFirstChildByName(wsLabelRef.AsStringView()); | 
|  | if (nodeLabel) { | 
|  | wsLabel = nodeLabel->JSObject()->GetContent(false); | 
|  | } | 
|  | } | 
|  | } else { | 
|  | wsLabel = wsValue; | 
|  | } | 
|  | pWidgetNode->InsertItem(wsLabel, wsValue, false); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void CXFA_FFDocView::SetChangeMark() { | 
|  | if (status_ != LayoutStatus::kEnd) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | doc_->SetChangeMark(); | 
|  | } | 
|  |  | 
|  | CXFA_Node* CXFA_FFDocView::GetRootSubform() { | 
|  | CXFA_Node* pFormPacketNode = | 
|  | ToNode(doc_->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Form)); | 
|  | if (!pFormPacketNode) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | return pFormPacketNode->GetFirstChildByClass<CXFA_Subform>( | 
|  | XFA_Element::Subform); | 
|  | } |