| // 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) |
| : m_pDocView(pDocView) { |
| m_pDocView->LockUpdate(); |
| } |
| |
| CXFA_FFDocView::UpdateScope::~UpdateScope() { |
| m_pDocView->UnlockUpdate(); |
| m_pDocView->UpdateDocView(); |
| } |
| |
| CXFA_FFDocView::CXFA_FFDocView(CXFA_FFDoc* pDoc) : m_pDoc(pDoc) {} |
| |
| CXFA_FFDocView::~CXFA_FFDocView() = default; |
| |
| void CXFA_FFDocView::Trace(cppgc::Visitor* visitor) const { |
| visitor->Trace(m_pDoc); |
| visitor->Trace(m_pWidgetHandler); |
| visitor->Trace(m_pFocusNode); |
| visitor->Trace(m_pFocusWidget); |
| ContainerTrace(visitor, m_ValidateNodes); |
| ContainerTrace(visitor, m_CalculateNodes); |
| ContainerTrace(visitor, m_NewAddedNodes); |
| ContainerTrace(visitor, m_BindItems); |
| ContainerTrace(visitor, m_IndexChangedSubforms); |
| } |
| |
| 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() { |
| m_iStatus = LayoutStatus::kStart; |
| m_pDoc->GetXFADoc()->DoProtoMerge(); |
| m_pDoc->GetXFADoc()->DoDataMerge(); |
| |
| int32_t iStatus = GetLayoutProcessor()->StartLayout(); |
| if (iStatus < 0) |
| return iStatus; |
| |
| CXFA_Node* pRootItem = |
| ToNode(m_pDoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Form)); |
| if (!pRootItem) |
| return iStatus; |
| |
| InitLayout(pRootItem); |
| InitCalculate(pRootItem); |
| InitValidate(pRootItem); |
| |
| ExecEventActivityByDeepFirst(pRootItem, XFA_EVENT_Ready, true, true); |
| m_iStatus = LayoutStatus::kStart; |
| return iStatus; |
| } |
| |
| int32_t CXFA_FFDocView::DoLayout() { |
| int32_t iStatus = GetLayoutProcessor()->DoLayout(); |
| if (iStatus != 100) |
| return iStatus; |
| |
| m_iStatus = LayoutStatus::kDoing; |
| return iStatus; |
| } |
| |
| void CXFA_FFDocView::StopLayout() { |
| CXFA_Node* pRootItem = |
| ToNode(m_pDoc->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); |
| |
| m_CalculateNodes.clear(); |
| if (m_pFocusNode && !m_pFocusWidget) |
| SetFocusNode(m_pFocusNode); |
| |
| m_iStatus = LayoutStatus::kEnd; |
| } |
| |
| void CXFA_FFDocView::AddNullTestMsg(const WideString& msg) { |
| m_NullTestMsgArray.push_back(msg); |
| } |
| |
| void CXFA_FFDocView::ShowNullTestMsg() { |
| int32_t iCount = fxcrt::CollectionSize<int32_t>(m_NullTestMsgArray); |
| CXFA_FFApp* pApp = m_pDoc->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 += m_NullTestMsgArray[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)); |
| } |
| m_NullTestMsgArray.clear(); |
| } |
| |
| void CXFA_FFDocView::UpdateDocView() { |
| if (IsUpdateLocked()) |
| return; |
| |
| LockUpdate(); |
| while (!m_NewAddedNodes.empty()) { |
| CXFA_Node* pNode = m_NewAddedNodes.front(); |
| m_NewAddedNodes.pop_front(); |
| InitCalculate(pNode); |
| InitValidate(pNode); |
| ExecEventActivityByDeepFirst(pNode, XFA_EVENT_Ready, true, true); |
| } |
| |
| RunSubformIndexChange(); |
| RunCalculateWidgets(); |
| RunValidate(); |
| |
| ShowNullTestMsg(); |
| |
| if (RunLayout() && m_bLayoutEvent) |
| RunEventLayoutReady(); |
| |
| m_bLayoutEvent = false; |
| m_CalculateNodes.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(m_pDoc->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) { |
| m_bLayoutEvent = 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) |
| m_pDoc->SetChangeMark(); |
| } |
| |
| CXFA_FFWidget* CXFA_FFDocView::GetWidgetForNode(CXFA_Node* node) { |
| return GetFFWidget( |
| ToContentLayoutItem(GetLayoutProcessor()->GetLayoutItem(node))); |
| } |
| |
| CXFA_FFWidgetHandler* CXFA_FFDocView::GetWidgetHandler() { |
| if (!m_pWidgetHandler) { |
| m_pWidgetHandler = cppgc::MakeGarbageCollected<CXFA_FFWidgetHandler>( |
| m_pDoc->GetHeap()->GetAllocationHandle(), this); |
| } |
| return m_pWidgetHandler; |
| } |
| |
| bool CXFA_FFDocView::SetFocus(CXFA_FFWidget* pNewFocus) { |
| if (pNewFocus == m_pFocusWidget) |
| return false; |
| |
| if (m_pFocusWidget) { |
| CXFA_ContentLayoutItem* pItem = m_pFocusWidget->GetLayoutItem(); |
| if (pItem->TestStatusBits(XFA_WidgetStatus::kVisible) && |
| !pItem->TestStatusBits(XFA_WidgetStatus::kFocused)) { |
| if (!m_pFocusWidget->IsLoaded()) |
| m_pFocusWidget->LoadWidget(); |
| if (!m_pFocusWidget->OnSetFocus(m_pFocusWidget)) |
| m_pFocusWidget.Clear(); |
| } |
| } |
| if (m_pFocusWidget) { |
| if (!m_pFocusWidget->OnKillFocus(pNewFocus)) |
| return false; |
| } |
| |
| if (pNewFocus) { |
| if (pNewFocus->GetLayoutItem()->TestStatusBits( |
| XFA_WidgetStatus::kVisible)) { |
| if (!pNewFocus->IsLoaded()) |
| pNewFocus->LoadWidget(); |
| if (!pNewFocus->OnSetFocus(m_pFocusWidget)) |
| pNewFocus = nullptr; |
| } |
| } |
| if (pNewFocus) { |
| CXFA_Node* node = pNewFocus->GetNode(); |
| m_pFocusNode = node->IsWidgetReady() ? node : nullptr; |
| m_pFocusWidget = pNewFocus; |
| } else { |
| m_pFocusNode.Clear(); |
| m_pFocusWidget.Clear(); |
| } |
| return true; |
| } |
| |
| void CXFA_FFDocView::SetFocusNode(CXFA_Node* node) { |
| CXFA_FFWidget* pNewFocus = node ? GetWidgetForNode(node) : nullptr; |
| if (!SetFocus(pNewFocus)) |
| return; |
| |
| m_pFocusNode = node; |
| if (m_iStatus != LayoutStatus::kEnd) |
| return; |
| |
| m_pDoc->SetFocusWidget(m_pFocusWidget); |
| } |
| |
| void CXFA_FFDocView::DeleteLayoutItem(CXFA_FFWidget* pWidget) { |
| if (m_pFocusNode != pWidget->GetNode()) |
| return; |
| |
| m_pFocusNode.Clear(); |
| m_pFocusWidget.Clear(); |
| } |
| |
| static XFA_EventError XFA_ProcessEvent(CXFA_FFDocView* pDocView, |
| CXFA_Node* pNode, |
| CXFA_EventParam* pParam) { |
| if (!pParam || pParam->m_eType == XFA_EVENT_Unknown) |
| return XFA_EventError::kNotExist; |
| if (pNode && pNode->GetElementType() == XFA_Element::Draw) |
| return XFA_EventError::kNotExist; |
| |
| switch (pParam->m_eType) { |
| 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->m_eType], |
| 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.m_bIsFormReady = 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.m_bIsFormReady = 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 = m_pDoc->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; |
| m_pDoc->OnPageViewEvent(pFFPageView, eEvent); |
| } |
| |
| void CXFA_FFDocView::InvalidateRect(CXFA_FFPageView* pPageView, |
| const CFX_RectF& rtInvalidate) { |
| m_pDoc->InvalidateRect(pPageView, rtInvalidate); |
| } |
| |
| bool CXFA_FFDocView::RunLayout() { |
| LockUpdate(); |
| m_bInLayoutStatus = true; |
| |
| CXFA_LayoutProcessor* pProcessor = GetLayoutProcessor(); |
| if (!pProcessor->IncrementLayout() && pProcessor->StartLayout() < 100) { |
| pProcessor->DoLayout(); |
| UnlockUpdate(); |
| m_bInLayoutStatus = false; |
| m_pDoc->OnPageViewEvent(nullptr, CXFA_FFDoc::PageViewEvent::kStopLayout); |
| return true; |
| } |
| |
| m_bInLayoutStatus = false; |
| m_pDoc->OnPageViewEvent(nullptr, CXFA_FFDoc::PageViewEvent::kStopLayout); |
| UnlockUpdate(); |
| return false; |
| } |
| |
| void CXFA_FFDocView::RunSubformIndexChange() { |
| std::set<CXFA_Node*> seen; |
| while (!m_IndexChangedSubforms.empty()) { |
| CXFA_Node* pSubformNode = m_IndexChangedSubforms.front(); |
| m_IndexChangedSubforms.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) { |
| m_NewAddedNodes.push_back(pNode); |
| InitLayout(pNode); |
| } |
| |
| void CXFA_FFDocView::AddIndexChangedSubform(CXFA_Subform* pNode) { |
| if (!pdfium::Contains(m_IndexChangedSubforms, pNode)) |
| m_IndexChangedSubforms.push_back(pNode); |
| } |
| |
| void CXFA_FFDocView::RunDocClose() { |
| CXFA_Node* pRootItem = |
| ToNode(m_pDoc->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 = |
| !m_CalculateNodes.empty() ? m_CalculateNodes.back() : nullptr; |
| if (pCurrentNode != node) |
| m_CalculateNodes.push_back(node); |
| } |
| |
| void CXFA_FFDocView::AddCalculateNodeNotify(CXFA_Node* pNodeChange) { |
| CJX_Object::CalcData* pGlobalData = pNodeChange->JSObject()->GetCalcData(); |
| if (!pGlobalData) |
| return; |
| |
| for (auto& pResult : pGlobalData->m_Globals) { |
| if (!pResult->HasRemovedChildren() && pResult->IsWidgetReady()) |
| AddCalculateNode(pResult); |
| } |
| } |
| |
| size_t CXFA_FFDocView::RunCalculateRecursive(size_t index) { |
| while (index < m_CalculateNodes.size()) { |
| CXFA_Node* node = m_CalculateNodes[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 (!m_pDoc->IsCalculationsEnabled()) |
| return XFA_EventError::kDisabled; |
| |
| if (!m_CalculateNodes.empty()) |
| RunCalculateRecursive(0); |
| |
| for (CXFA_Node* node : m_CalculateNodes) |
| node->JSObject()->SetCalcRecursionCount(0); |
| |
| m_CalculateNodes.clear(); |
| return XFA_EventError::kSuccess; |
| } |
| |
| void CXFA_FFDocView::AddValidateNode(CXFA_Node* node) { |
| if (!pdfium::Contains(m_ValidateNodes, node)) |
| m_ValidateNodes.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(); |
| } |
| |
| bool CXFA_FFDocView::InitValidate(CXFA_Node* pNode) { |
| if (!m_pDoc->IsValidationsEnabled()) |
| return false; |
| |
| ExecEventActivityByDeepFirst(pNode, XFA_EVENT_Validate, false, true); |
| m_ValidateNodes.clear(); |
| return true; |
| } |
| |
| bool CXFA_FFDocView::RunValidate() { |
| if (!m_pDoc->IsValidationsEnabled()) |
| return false; |
| |
| while (!m_ValidateNodes.empty()) { |
| CXFA_Node* node = m_ValidateNodes.front(); |
| m_ValidateNodes.pop_front(); |
| if (!node->HasRemovedChildren()) |
| node->ProcessValidate(this, 0); |
| } |
| return true; |
| } |
| |
| bool CXFA_FFDocView::RunEventLayoutReady() { |
| CXFA_Node* pRootItem = |
| ToNode(m_pDoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Form)); |
| if (!pRootItem) |
| return false; |
| |
| ExecEventActivityByDeepFirst(pRootItem, XFA_EVENT_Ready, false, true); |
| RunLayout(); |
| return true; |
| } |
| |
| void CXFA_FFDocView::RunBindItems() { |
| while (!m_BindItems.empty()) { |
| CXFA_BindItems* item = m_BindItems.front(); |
| m_BindItems.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 (m_iStatus != LayoutStatus::kEnd) |
| return; |
| |
| m_pDoc->SetChangeMark(); |
| } |
| |
| CXFA_Node* CXFA_FFDocView::GetRootSubform() { |
| CXFA_Node* pFormPacketNode = |
| ToNode(m_pDoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Form)); |
| if (!pFormPacketNode) |
| return nullptr; |
| |
| return pFormPacketNode->GetFirstChildByClass<CXFA_Subform>( |
| XFA_Element::Subform); |
| } |