// 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_hostpseudomodel.h" #include #include "fxjs/fxv8.h" #include "fxjs/js_resources.h" #include "fxjs/xfa/cfxjse_engine.h" #include "third_party/base/check.h" #include "v8/include/v8-object.h" #include "xfa/fxfa/cxfa_ffdoc.h" #include "xfa/fxfa/cxfa_ffnotify.h" #include "xfa/fxfa/parser/cscript_hostpseudomodel.h" #include "xfa/fxfa/parser/cxfa_node.h" namespace { size_t FilterName(WideStringView wsExpression, size_t nStart, WideString& wsFilter) { const size_t nLength = wsExpression.GetLength(); if (nStart >= nLength) return nLength; size_t nCount = 0; { // Span's lifetime must end before ReleaseBuffer() below. pdfium::span pBuf = wsFilter.GetBuffer(nLength - nStart); const wchar_t* pSrc = wsExpression.unterminated_c_str(); while (nStart < nLength) { wchar_t wCur = pSrc[nStart++]; if (wCur == ',') break; pBuf[nCount++] = wCur; } } wsFilter.ReleaseBuffer(nCount); wsFilter.Trim(); return nStart; } } // namespace const CJX_MethodSpec CJX_HostPseudoModel::MethodSpecs[] = { {"beep", beep_static}, {"documentCountInBatch", documentCountInBatch_static}, {"documentInBatch", documentInBatch_static}, {"exportData", exportData_static}, {"getFocus", getFocus_static}, {"gotoURL", gotoURL_static}, {"importData", importData_static}, {"messageBox", messageBox_static}, {"openList", openList_static}, {"pageDown", pageDown_static}, {"pageUp", pageUp_static}, {"print", print_static}, {"resetData", resetData_static}, {"response", response_static}, {"setFocus", setFocus_static}}; CJX_HostPseudoModel::CJX_HostPseudoModel(CScript_HostPseudoModel* model) : CJX_Object(model) { DefineMethods(MethodSpecs); } CJX_HostPseudoModel::~CJX_HostPseudoModel() = default; bool CJX_HostPseudoModel::DynamicTypeIs(TypeTag eType) const { return eType == static_type__ || ParentType__::DynamicTypeIs(eType); } void CJX_HostPseudoModel::appType(v8::Isolate* pIsolate, v8::Local* pValue, bool bSetting, XFA_Attribute eAttribute) { CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); if (!pNotify) return; if (bSetting) { ThrowInvalidPropertyException(pIsolate); return; } *pValue = fxv8::NewStringHelper(pIsolate, "Exchange"); } void CJX_HostPseudoModel::calculationsEnabled(v8::Isolate* pIsolate, v8::Local* pValue, bool bSetting, XFA_Attribute eAttribute) { CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); if (!pNotify) return; CXFA_FFDoc* hDoc = pNotify->GetFFDoc(); if (bSetting) { hDoc->SetCalculationsEnabled( fxv8::ReentrantToBooleanHelper(pIsolate, *pValue)); return; } *pValue = fxv8::NewBooleanHelper(pIsolate, hDoc->IsCalculationsEnabled()); } void CJX_HostPseudoModel::currentPage(v8::Isolate* pIsolate, v8::Local* pValue, bool bSetting, XFA_Attribute eAttribute) { CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); if (!pNotify) return; CXFA_FFDoc* hDoc = pNotify->GetFFDoc(); if (bSetting) { hDoc->SetCurrentPage(fxv8::ReentrantToInt32Helper(pIsolate, *pValue)); return; } *pValue = fxv8::NewNumberHelper(pIsolate, hDoc->GetCurrentPage()); } void CJX_HostPseudoModel::language(v8::Isolate* pIsolate, v8::Local* pValue, bool bSetting, XFA_Attribute eAttribute) { CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); if (!pNotify) return; if (bSetting) { ThrowException(pIsolate, WideString::FromASCII("Unable to set language value.")); return; } ByteString lang = pNotify->GetAppProvider()->GetLanguage().ToUTF8(); *pValue = fxv8::NewStringHelper(pIsolate, lang.AsStringView()); } void CJX_HostPseudoModel::numPages(v8::Isolate* pIsolate, v8::Local* pValue, bool bSetting, XFA_Attribute eAttribute) { CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); if (!pNotify) return; CXFA_FFDoc* hDoc = pNotify->GetFFDoc(); if (bSetting) { ThrowException(pIsolate, WideString::FromASCII("Unable to set numPages value.")); return; } *pValue = fxv8::NewNumberHelper(pIsolate, hDoc->CountPages()); } void CJX_HostPseudoModel::platform(v8::Isolate* pIsolate, v8::Local* pValue, bool bSetting, XFA_Attribute eAttribute) { CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); if (!pNotify) return; if (bSetting) { ThrowException(pIsolate, WideString::FromASCII("Unable to set platform value.")); return; } ByteString plat = pNotify->GetAppProvider()->GetPlatform().ToUTF8(); *pValue = fxv8::NewStringHelper(pIsolate, plat.AsStringView()); } void CJX_HostPseudoModel::title(v8::Isolate* pIsolate, v8::Local* pValue, bool bSetting, XFA_Attribute eAttribute) { if (!GetDocument()->GetScriptContext()->IsRunAtClient()) return; CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); if (!pNotify) return; CXFA_FFDoc* hDoc = pNotify->GetFFDoc(); if (bSetting) { hDoc->SetTitle(fxv8::ReentrantToWideStringHelper(pIsolate, *pValue)); return; } ByteString bsTitle = hDoc->GetTitle().ToUTF8(); *pValue = fxv8::NewStringHelper(pIsolate, bsTitle.AsStringView()); } void CJX_HostPseudoModel::validationsEnabled(v8::Isolate* pIsolate, v8::Local* pValue, bool bSetting, XFA_Attribute eAttribute) { CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); if (!pNotify) return; CXFA_FFDoc* hDoc = pNotify->GetFFDoc(); if (bSetting) { hDoc->SetValidationsEnabled( fxv8::ReentrantToBooleanHelper(pIsolate, *pValue)); return; } *pValue = fxv8::NewBooleanHelper(pIsolate, hDoc->IsValidationsEnabled()); } void CJX_HostPseudoModel::variation(v8::Isolate* pIsolate, v8::Local* pValue, bool bSetting, XFA_Attribute eAttribute) { if (!GetDocument()->GetScriptContext()->IsRunAtClient()) return; if (bSetting) { ThrowException(pIsolate, WideString::FromASCII("Unable to set variation value.")); return; } *pValue = fxv8::NewStringHelper(pIsolate, "Full"); } void CJX_HostPseudoModel::version(v8::Isolate* pIsolate, v8::Local* pValue, bool bSetting, XFA_Attribute eAttribute) { if (bSetting) { ThrowException(pIsolate, WideString::FromASCII("Unable to set version value.")); return; } *pValue = fxv8::NewStringHelper(pIsolate, "11"); } void CJX_HostPseudoModel::name(v8::Isolate* pIsolate, v8::Local* pValue, bool bSetting, XFA_Attribute eAttribute) { CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); if (!pNotify) return; if (bSetting) { ThrowInvalidPropertyException(pIsolate); return; } ByteString bsName = pNotify->GetAppProvider()->GetAppName().ToUTF8(); *pValue = fxv8::NewStringHelper(pIsolate, bsName.AsStringView()); } CJS_Result CJX_HostPseudoModel::gotoURL( CFXJSE_Engine* runtime, const std::vector>& params) { if (!runtime->IsRunAtClient()) { return CJS_Result::Success(); } if (params.size() != 1) return CJS_Result::Failure(JSMessage::kParamError); CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); if (!pNotify) return CJS_Result::Success(); pNotify->GetFFDoc()->GotoURL(runtime->ToWideString(params[0])); return CJS_Result::Success(); } CJS_Result CJX_HostPseudoModel::openList( CFXJSE_Engine* runtime, const std::vector>& params) { if (!runtime->IsRunAtClient()) { return CJS_Result::Success(); } if (params.size() != 1) return CJS_Result::Failure(JSMessage::kParamError); CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); if (!pNotify) return CJS_Result::Success(); CXFA_Node* pNode = nullptr; if (params[0]->IsObject()) { pNode = ToNode(runtime->ToXFAObject(params[0])); } else if (params[0]->IsString()) { CXFA_Object* pObject = runtime->GetThisObject(); if (!pObject) return CJS_Result::Success(); constexpr Mask kFlags = {XFA_ResolveFlag::kChildren, XFA_ResolveFlag::kParent, XFA_ResolveFlag::kSiblings}; absl::optional maybeResult = runtime->ResolveObjects( pObject, runtime->ToWideString(params[0]).AsStringView(), kFlags); if (!maybeResult.has_value() || !maybeResult.value().objects.front()->IsNode()) { return CJS_Result::Success(); } pNode = maybeResult.value().objects.front()->AsNode(); } if (pNode) pNotify->OpenDropDownList(pNode); return CJS_Result::Success(); } CJS_Result CJX_HostPseudoModel::response( CFXJSE_Engine* runtime, const std::vector>& params) { if (params.empty() || params.size() > 4) return CJS_Result::Failure(JSMessage::kParamError); CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); if (!pNotify) return CJS_Result::Success(); WideString question; if (params.size() >= 1) question = runtime->ToWideString(params[0]); WideString title; if (params.size() >= 2) title = runtime->ToWideString(params[1]); WideString defaultAnswer; if (params.size() >= 3) defaultAnswer = runtime->ToWideString(params[2]); bool mark = false; if (params.size() >= 4) mark = runtime->ToInt32(params[3]) != 0; WideString answer = pNotify->GetAppProvider()->Response(question, title, defaultAnswer, mark); return CJS_Result::Success( runtime->NewString(answer.ToUTF8().AsStringView())); } CJS_Result CJX_HostPseudoModel::documentInBatch( CFXJSE_Engine* runtime, const std::vector>& params) { return CJS_Result::Success(runtime->NewNumber(0)); } CJS_Result CJX_HostPseudoModel::resetData( CFXJSE_Engine* runtime, const std::vector>& params) { if (params.size() > 1) return CJS_Result::Failure(JSMessage::kParamError); CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); if (!pNotify) return CJS_Result::Success(); WideString expression; if (params.size() >= 1) expression = runtime->ToWideString(params[0]); if (expression.IsEmpty()) { pNotify->ResetData(nullptr); return CJS_Result::Success(); } WideString wsName; CXFA_Node* pNode = nullptr; size_t nStart = 0; const size_t nExpLength = expression.GetLength(); while (nStart < nExpLength) { nStart = FilterName(expression.AsStringView(), nStart, wsName); CXFA_Object* pObject = runtime->GetThisObject(); if (!pObject) return CJS_Result::Success(); constexpr Mask kFlags = {XFA_ResolveFlag::kChildren, XFA_ResolveFlag::kParent, XFA_ResolveFlag::kSiblings}; absl::optional maybeResult = runtime->ResolveObjects(pObject, wsName.AsStringView(), kFlags); if (!maybeResult.has_value() || !maybeResult.value().objects.front()->IsNode()) continue; pNode = maybeResult.value().objects.front()->AsNode(); pNotify->ResetData(pNode->IsWidgetReady() ? pNode : nullptr); } if (!pNode) pNotify->ResetData(nullptr); return CJS_Result::Success(); } CJS_Result CJX_HostPseudoModel::beep( CFXJSE_Engine* runtime, const std::vector>& params) { if (!runtime->IsRunAtClient()) { return CJS_Result::Success(); } if (params.size() > 1) return CJS_Result::Failure(JSMessage::kParamError); CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); if (!pNotify) return CJS_Result::Success(); uint32_t dwType = 4; if (params.size() >= 1) dwType = runtime->ToInt32(params[0]); pNotify->GetAppProvider()->Beep(dwType); return CJS_Result::Success(); } CJS_Result CJX_HostPseudoModel::setFocus( CFXJSE_Engine* runtime, const std::vector>& params) { if (!runtime->IsRunAtClient()) { return CJS_Result::Success(); } if (params.size() != 1) return CJS_Result::Failure(JSMessage::kParamError); CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); if (!pNotify) return CJS_Result::Success(); CXFA_Node* pNode = nullptr; if (params.size() >= 1) { if (params[0]->IsObject()) { pNode = ToNode(runtime->ToXFAObject(params[0])); } else if (params[0]->IsString()) { CXFA_Object* pObject = runtime->GetThisObject(); if (!pObject) return CJS_Result::Success(); constexpr Mask kFlags = {XFA_ResolveFlag::kChildren, XFA_ResolveFlag::kParent, XFA_ResolveFlag::kSiblings}; absl::optional maybeResult = runtime->ResolveObjects( pObject, runtime->ToWideString(params[0]).AsStringView(), kFlags); if (!maybeResult.has_value() || !maybeResult.value().objects.front()->IsNode()) { return CJS_Result::Success(); } pNode = maybeResult.value().objects.front()->AsNode(); } } pNotify->SetFocusWidgetNode(pNode); return CJS_Result::Success(); } CJS_Result CJX_HostPseudoModel::getFocus( CFXJSE_Engine* runtime, const std::vector>& params) { CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); if (!pNotify) return CJS_Result::Success(); CXFA_Node* pNode = pNotify->GetFocusWidgetNode(); if (!pNode) return CJS_Result::Success(); return CJS_Result::Success(runtime->GetOrCreateJSBindingFromMap(pNode)); } CJS_Result CJX_HostPseudoModel::messageBox( CFXJSE_Engine* runtime, const std::vector>& params) { if (!runtime->IsRunAtClient()) { return CJS_Result::Success(); } if (params.empty() || params.size() > 4) return CJS_Result::Failure(JSMessage::kParamError); CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); if (!pNotify) return CJS_Result::Success(); WideString message; if (params.size() >= 1) message = runtime->ToWideString(params[0]); WideString title; if (params.size() >= 2) title = runtime->ToWideString(params[1]); uint32_t messageType = static_cast(AlertIcon::kDefault); if (params.size() >= 3) { messageType = runtime->ToInt32(params[2]); if (messageType > static_cast(AlertIcon::kStatus)) messageType = static_cast(AlertIcon::kDefault); } uint32_t buttonType = static_cast(AlertButton::kDefault); if (params.size() >= 4) { buttonType = runtime->ToInt32(params[3]); if (buttonType > static_cast(AlertButton::kYesNoCancel)) buttonType = static_cast(AlertButton::kDefault); } int32_t iValue = pNotify->GetAppProvider()->MsgBox(message, title, messageType, buttonType); return CJS_Result::Success(runtime->NewNumber(iValue)); } CJS_Result CJX_HostPseudoModel::documentCountInBatch( CFXJSE_Engine* runtime, const std::vector>& params) { return CJS_Result::Success(runtime->NewNumber(0)); } CJS_Result CJX_HostPseudoModel::print( CFXJSE_Engine* runtime, const std::vector>& params) { if (!runtime->IsRunAtClient()) { return CJS_Result::Success(); } if (params.size() != 8) return CJS_Result::Failure(JSMessage::kParamError); CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); if (!pNotify) return CJS_Result::Success(); Mask dwOptions; if (runtime->ToBoolean(params[0])) dwOptions |= XFA_PrintOpt::kShowDialog; if (runtime->ToBoolean(params[3])) dwOptions |= XFA_PrintOpt::kCanCancel; if (runtime->ToBoolean(params[4])) dwOptions |= XFA_PrintOpt::kShrinkPage; if (runtime->ToBoolean(params[5])) dwOptions |= XFA_PrintOpt::kAsImage; if (runtime->ToBoolean(params[6])) dwOptions |= XFA_PrintOpt::kReverseOrder; if (runtime->ToBoolean(params[7])) dwOptions |= XFA_PrintOpt::kPrintAnnot; int32_t nStartPage = runtime->ToInt32(params[1]); int32_t nEndPage = runtime->ToInt32(params[2]); pNotify->GetFFDoc()->Print(nStartPage, nEndPage, dwOptions); return CJS_Result::Success(); } CJS_Result CJX_HostPseudoModel::importData( CFXJSE_Engine* runtime, const std::vector>& params) { if (params.empty() || params.size() > 1) return CJS_Result::Failure(JSMessage::kParamError); return CJS_Result::Success(); } CJS_Result CJX_HostPseudoModel::exportData( CFXJSE_Engine* runtime, const std::vector>& params) { if (params.empty() || params.size() > 2) return CJS_Result::Failure(JSMessage::kParamError); CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); if (!pNotify) return CJS_Result::Success(); WideString filePath; if (params.size() >= 1) filePath = runtime->ToWideString(params[0]); bool XDP = true; if (params.size() >= 2) XDP = runtime->ToBoolean(params[1]); pNotify->GetFFDoc()->ExportData(filePath, XDP); return CJS_Result::Success(); } CJS_Result CJX_HostPseudoModel::pageUp( CFXJSE_Engine* runtime, const std::vector>& params) { CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); if (!pNotify) return CJS_Result::Success(); CXFA_FFDoc* hDoc = pNotify->GetFFDoc(); int32_t nCurPage = hDoc->GetCurrentPage(); if (nCurPage <= 1) return CJS_Result::Success(); hDoc->SetCurrentPage(nCurPage - 1); return CJS_Result::Success(); } CJS_Result CJX_HostPseudoModel::pageDown( CFXJSE_Engine* runtime, const std::vector>& params) { CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); if (!pNotify) return CJS_Result::Success(); CXFA_FFDoc* hDoc = pNotify->GetFFDoc(); int32_t nCurPage = hDoc->GetCurrentPage(); int32_t nPageCount = hDoc->CountPages(); if (!nPageCount || nCurPage == nPageCount) return CJS_Result::Success(); int32_t nNewPage = 0; if (nCurPage >= nPageCount) nNewPage = nPageCount - 1; else nNewPage = nCurPage + 1; hDoc->SetCurrentPage(nNewPage); return CJS_Result::Success(); }