• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 The PDFium Authors
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 "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
8 
9 #include <stdint.h>
10 
11 #include <algorithm>
12 #include <utility>
13 
14 #include "core/fpdfapi/parser/cpdf_array.h"
15 #include "core/fpdfapi/parser/cpdf_dictionary.h"
16 #include "core/fpdfapi/parser/cpdf_document.h"
17 #include "core/fpdfapi/parser/cpdf_seekablemultistream.h"
18 #include "core/fxcrt/autonuller.h"
19 #include "core/fxcrt/fixed_zeroed_data_vector.h"
20 #include "core/fxcrt/stl_util.h"
21 #include "core/fxcrt/xml/cfx_xmldocument.h"
22 #include "core/fxcrt/xml/cfx_xmlparser.h"
23 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
24 #include "fpdfsdk/cpdfsdk_pageview.h"
25 #include "fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.h"
26 #include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
27 #include "fxbarcode/BC_Library.h"
28 #include "fxjs/cjs_runtime.h"
29 #include "fxjs/ijs_runtime.h"
30 #include "public/fpdf_formfill.h"
31 #include "third_party/base/check.h"
32 #include "v8/include/cppgc/allocation.h"
33 #include "xfa/fgas/font/cfgas_gemodule.h"
34 #include "xfa/fxfa/cxfa_eventparam.h"
35 #include "xfa/fxfa/cxfa_ffapp.h"
36 #include "xfa/fxfa/cxfa_ffdoc.h"
37 #include "xfa/fxfa/cxfa_ffdocview.h"
38 #include "xfa/fxfa/cxfa_ffpageview.h"
39 #include "xfa/fxfa/cxfa_ffwidgethandler.h"
40 #include "xfa/fxfa/cxfa_fontmgr.h"
41 #include "xfa/fxfa/cxfa_readynodeiterator.h"
42 
43 namespace {
44 
IsValidAlertButton(int type)45 bool IsValidAlertButton(int type) {
46   return type == JSPLATFORM_ALERT_BUTTON_OK ||
47          type == JSPLATFORM_ALERT_BUTTON_OKCANCEL ||
48          type == JSPLATFORM_ALERT_BUTTON_YESNO ||
49          type == JSPLATFORM_ALERT_BUTTON_YESNOCANCEL;
50 }
51 
IsValidAlertIcon(int type)52 bool IsValidAlertIcon(int type) {
53   return type == JSPLATFORM_ALERT_ICON_ERROR ||
54          type == JSPLATFORM_ALERT_ICON_WARNING ||
55          type == JSPLATFORM_ALERT_ICON_QUESTION ||
56          type == JSPLATFORM_ALERT_ICON_STATUS ||
57          type == JSPLATFORM_ALERT_ICON_ASTERISK;
58 }
59 
CreateXFAMultiStream(const CPDF_Document * pPDFDoc)60 RetainPtr<CPDF_SeekableMultiStream> CreateXFAMultiStream(
61     const CPDF_Document* pPDFDoc) {
62   const CPDF_Dictionary* pRoot = pPDFDoc->GetRoot();
63   if (!pRoot)
64     return nullptr;
65 
66   RetainPtr<const CPDF_Dictionary> pAcroForm = pRoot->GetDictFor("AcroForm");
67   if (!pAcroForm)
68     return nullptr;
69 
70   RetainPtr<const CPDF_Object> pElementXFA =
71       pAcroForm->GetDirectObjectFor("XFA");
72   if (!pElementXFA)
73     return nullptr;
74 
75   std::vector<RetainPtr<const CPDF_Stream>> xfa_streams;
76   if (pElementXFA->IsArray()) {
77     const CPDF_Array* pXFAArray = pElementXFA->AsArray();
78     for (size_t i = 0; i < pXFAArray->size() / 2; i++) {
79       RetainPtr<const CPDF_Stream> pStream = pXFAArray->GetStreamAt(i * 2 + 1);
80       if (pStream)
81         xfa_streams.push_back(std::move(pStream));
82     }
83   } else if (pElementXFA->IsStream()) {
84     xfa_streams.push_back(ToStream(pElementXFA));
85   }
86   if (xfa_streams.empty())
87     return nullptr;
88 
89   return pdfium::MakeRetain<CPDF_SeekableMultiStream>(std::move(xfa_streams));
90 }
91 
92 }  // namespace
93 
CPDFXFA_ModuleInit()94 void CPDFXFA_ModuleInit() {
95   CFGAS_GEModule::Create();
96   BC_Library_Init();
97 }
98 
CPDFXFA_ModuleDestroy()99 void CPDFXFA_ModuleDestroy() {
100   BC_Library_Destroy();
101   CFGAS_GEModule::Destroy();
102 }
103 
CPDFXFA_Context(CPDF_Document * pPDFDoc)104 CPDFXFA_Context::CPDFXFA_Context(CPDF_Document* pPDFDoc)
105     : m_pPDFDoc(pPDFDoc),
106       m_pDocEnv(std::make_unique<CPDFXFA_DocEnvironment>(this)),
107       m_pGCHeap(FXGC_CreateHeap()) {
108   DCHECK(m_pPDFDoc);
109 
110   // There might not be a heap when JS not initialized.
111   if (m_pGCHeap) {
112     m_pXFAApp = cppgc::MakeGarbageCollected<CXFA_FFApp>(
113         m_pGCHeap->GetAllocationHandle(), this);
114   }
115 }
116 
~CPDFXFA_Context()117 CPDFXFA_Context::~CPDFXFA_Context() {
118   m_nLoadStatus = LoadStatus::kClosing;
119   if (m_pFormFillEnv)
120     m_pFormFillEnv->ClearAllFocusedAnnots();
121 }
122 
SetFormFillEnv(CPDFSDK_FormFillEnvironment * pFormFillEnv)123 void CPDFXFA_Context::SetFormFillEnv(
124     CPDFSDK_FormFillEnvironment* pFormFillEnv) {
125   // The layout data can have pointers back into the script context. That
126   // context will be different if the form fill environment closes, so, force
127   // the layout data to clear.
128   if (m_pXFADoc && m_pXFADoc->GetXFADoc()) {
129     m_pXFADoc->GetXFADoc()->ClearLayoutData();
130     m_pXFADocView.Clear();
131     m_pXFADoc.Clear();
132     m_pXFAApp.Clear();
133     FXGC_ForceGarbageCollection(m_pGCHeap.get());
134   }
135   m_pFormFillEnv.Reset(pFormFillEnv);
136 }
137 
LoadXFADoc()138 bool CPDFXFA_Context::LoadXFADoc() {
139   m_nLoadStatus = LoadStatus::kLoading;
140   m_XFAPageList.clear();
141 
142   CJS_Runtime* actual_runtime = GetCJSRuntime();  // Null if a stub.
143   if (!actual_runtime) {
144     FXSYS_SetLastError(FPDF_ERR_XFALOAD);
145     return false;
146   }
147 
148   auto stream = CreateXFAMultiStream(m_pPDFDoc);
149   if (!stream) {
150     FXSYS_SetLastError(FPDF_ERR_XFALOAD);
151     return false;
152   }
153 
154   CFX_XMLParser parser(stream);
155   m_pXML = parser.Parse();
156   if (!m_pXML) {
157     FXSYS_SetLastError(FPDF_ERR_XFALOAD);
158     return false;
159   }
160 
161   AutoNuller<cppgc::Persistent<CXFA_FFDoc>> doc_nuller(&m_pXFADoc);
162   m_pXFADoc = cppgc::MakeGarbageCollected<CXFA_FFDoc>(
163       m_pGCHeap->GetAllocationHandle(), m_pXFAApp, m_pDocEnv.get(), m_pPDFDoc,
164       m_pGCHeap.get());
165 
166   if (!m_pXFADoc->OpenDoc(m_pXML.get())) {
167     FXSYS_SetLastError(FPDF_ERR_XFALOAD);
168     return false;
169   }
170 
171   if (!m_pXFAApp->LoadFWLTheme(m_pXFADoc)) {
172     FXSYS_SetLastError(FPDF_ERR_XFALAYOUT);
173     return false;
174   }
175 
176   m_pXFADoc->GetXFADoc()->InitScriptContext(actual_runtime);
177   if (m_pXFADoc->GetFormType() == FormType::kXFAFull)
178     m_FormType = FormType::kXFAFull;
179   else
180     m_FormType = FormType::kXFAForeground;
181 
182   AutoNuller<cppgc::Persistent<CXFA_FFDocView>> view_nuller(&m_pXFADocView);
183   m_pXFADocView = m_pXFADoc->CreateDocView();
184 
185   if (m_pXFADocView->StartLayout() < 0) {
186     m_pXFADoc->GetXFADoc()->ClearLayoutData();
187     FXGC_ForceGarbageCollection(m_pGCHeap.get());
188     FXSYS_SetLastError(FPDF_ERR_XFALAYOUT);
189     return false;
190   }
191 
192   m_pXFADocView->DoLayout();
193   m_pXFADocView->StopLayout();
194 
195   view_nuller.AbandonNullification();
196   doc_nuller.AbandonNullification();
197   m_nLoadStatus = LoadStatus::kLoaded;
198   return true;
199 }
200 
GetPageCount() const201 int CPDFXFA_Context::GetPageCount() const {
202   switch (m_FormType) {
203     case FormType::kNone:
204     case FormType::kAcroForm:
205     case FormType::kXFAForeground:
206       return m_pPDFDoc->GetPageCount();
207     case FormType::kXFAFull:
208       return m_pXFADoc ? m_pXFADocView->CountPageViews() : 0;
209   }
210 }
211 
GetOrCreateXFAPage(int page_index)212 RetainPtr<CPDFXFA_Page> CPDFXFA_Context::GetOrCreateXFAPage(int page_index) {
213   if (page_index < 0)
214     return nullptr;
215 
216   if (fxcrt::IndexInBounds(m_XFAPageList, page_index)) {
217     if (m_XFAPageList[page_index])
218       return m_XFAPageList[page_index];
219   } else {
220     m_nPageCount = GetPageCount();
221     m_XFAPageList.resize(m_nPageCount);
222   }
223 
224   auto pPage = pdfium::MakeRetain<CPDFXFA_Page>(GetPDFDoc(), page_index);
225   if (!pPage->LoadPage())
226     return nullptr;
227 
228   if (fxcrt::IndexInBounds(m_XFAPageList, page_index))
229     m_XFAPageList[page_index] = pPage;
230 
231   return pPage;
232 }
233 
GetXFAPage(int page_index)234 RetainPtr<CPDFXFA_Page> CPDFXFA_Context::GetXFAPage(int page_index) {
235   if (!fxcrt::IndexInBounds(m_XFAPageList, page_index))
236     return nullptr;
237 
238   return m_XFAPageList[page_index];
239 }
240 
GetXFAPage(CXFA_FFPageView * pPage) const241 RetainPtr<CPDFXFA_Page> CPDFXFA_Context::GetXFAPage(
242     CXFA_FFPageView* pPage) const {
243   if (!pPage)
244     return nullptr;
245 
246   if (!m_pXFADoc)
247     return nullptr;
248 
249   if (m_FormType != FormType::kXFAFull)
250     return nullptr;
251 
252   for (auto& pTempPage : m_XFAPageList) {
253     if (pTempPage && pTempPage->GetXFAPageView() == pPage)
254       return pTempPage;
255   }
256   return nullptr;
257 }
258 
DeletePage(int page_index)259 void CPDFXFA_Context::DeletePage(int page_index) {
260   // Delete from the document first because, if GetPage was never called for
261   // this |page_index| then |m_XFAPageList| may have size < |page_index| even
262   // if it's a valid page in the document.
263   m_pPDFDoc->DeletePage(page_index);
264 
265   if (fxcrt::IndexInBounds(m_XFAPageList, page_index))
266     m_XFAPageList[page_index].Reset();
267 }
268 
GetUserPermissions() const269 uint32_t CPDFXFA_Context::GetUserPermissions() const {
270   // See https://bugs.chromium.org/p/pdfium/issues/detail?id=499
271   return 0xFFFFFFFF;
272 }
273 
ContainsExtensionForm() const274 bool CPDFXFA_Context::ContainsExtensionForm() const {
275   return m_FormType == FormType::kXFAFull ||
276          m_FormType == FormType::kXFAForeground;
277 }
278 
ContainsExtensionFullForm() const279 bool CPDFXFA_Context::ContainsExtensionFullForm() const {
280   return m_FormType == FormType::kXFAFull;
281 }
282 
ContainsExtensionForegroundForm() const283 bool CPDFXFA_Context::ContainsExtensionForegroundForm() const {
284   return m_FormType == FormType::kXFAForeground;
285 }
286 
ClearChangeMark()287 void CPDFXFA_Context::ClearChangeMark() {
288   if (m_pFormFillEnv)
289     m_pFormFillEnv->ClearChangeMark();
290 }
291 
GetCJSRuntime() const292 CJS_Runtime* CPDFXFA_Context::GetCJSRuntime() const {
293   if (!m_pFormFillEnv)
294     return nullptr;
295 
296   return m_pFormFillEnv->GetIJSRuntime()->AsCJSRuntime();
297 }
298 
GetAppTitle() const299 WideString CPDFXFA_Context::GetAppTitle() const {
300   return L"PDFium";
301 }
302 
GetAppName()303 WideString CPDFXFA_Context::GetAppName() {
304   return m_pFormFillEnv ? m_pFormFillEnv->FFI_GetAppName() : WideString();
305 }
306 
GetLanguage()307 WideString CPDFXFA_Context::GetLanguage() {
308   return m_pFormFillEnv ? m_pFormFillEnv->GetLanguage() : WideString();
309 }
310 
GetPlatform()311 WideString CPDFXFA_Context::GetPlatform() {
312   return m_pFormFillEnv ? m_pFormFillEnv->GetPlatform() : WideString();
313 }
314 
Beep(uint32_t dwType)315 void CPDFXFA_Context::Beep(uint32_t dwType) {
316   if (m_pFormFillEnv)
317     m_pFormFillEnv->JS_appBeep(dwType);
318 }
319 
MsgBox(const WideString & wsMessage,const WideString & wsTitle,uint32_t dwIconType,uint32_t dwButtonType)320 int32_t CPDFXFA_Context::MsgBox(const WideString& wsMessage,
321                                 const WideString& wsTitle,
322                                 uint32_t dwIconType,
323                                 uint32_t dwButtonType) {
324   if (!m_pFormFillEnv || m_nLoadStatus != LoadStatus::kLoaded)
325     return -1;
326 
327   int iconType =
328       IsValidAlertIcon(dwIconType) ? dwIconType : JSPLATFORM_ALERT_ICON_DEFAULT;
329   int iButtonType = IsValidAlertButton(dwButtonType)
330                         ? dwButtonType
331                         : JSPLATFORM_ALERT_BUTTON_DEFAULT;
332   return m_pFormFillEnv->JS_appAlert(wsMessage, wsTitle, iButtonType, iconType);
333 }
334 
Response(const WideString & wsQuestion,const WideString & wsTitle,const WideString & wsDefaultAnswer,bool bMark)335 WideString CPDFXFA_Context::Response(const WideString& wsQuestion,
336                                      const WideString& wsTitle,
337                                      const WideString& wsDefaultAnswer,
338                                      bool bMark) {
339   if (!m_pFormFillEnv)
340     return WideString();
341 
342   constexpr int kMaxWideChars = 1024;
343   FixedZeroedDataVector<uint16_t> buffer(kMaxWideChars);
344   pdfium::span<uint16_t> buffer_span = buffer.writable_span();
345   int byte_length = m_pFormFillEnv->JS_appResponse(
346       wsQuestion, wsTitle, wsDefaultAnswer, WideString(), bMark,
347       pdfium::as_writable_bytes(buffer_span));
348   if (byte_length <= 0)
349     return WideString();
350 
351   buffer_span = buffer_span.first(
352       std::min<size_t>(kMaxWideChars, byte_length / sizeof(uint16_t)));
353   return WideString::FromUTF16LE(buffer_span.data(), buffer_span.size());
354 }
355 
DownloadURL(const WideString & wsURL)356 RetainPtr<IFX_SeekableReadStream> CPDFXFA_Context::DownloadURL(
357     const WideString& wsURL) {
358   return m_pFormFillEnv ? m_pFormFillEnv->DownloadFromURL(wsURL) : nullptr;
359 }
360 
PostRequestURL(const WideString & wsURL,const WideString & wsData,const WideString & wsContentType,const WideString & wsEncode,const WideString & wsHeader,WideString & wsResponse)361 bool CPDFXFA_Context::PostRequestURL(const WideString& wsURL,
362                                      const WideString& wsData,
363                                      const WideString& wsContentType,
364                                      const WideString& wsEncode,
365                                      const WideString& wsHeader,
366                                      WideString& wsResponse) {
367   if (!m_pFormFillEnv)
368     return false;
369 
370   wsResponse = m_pFormFillEnv->PostRequestURL(wsURL, wsData, wsContentType,
371                                               wsEncode, wsHeader);
372   return true;
373 }
374 
PutRequestURL(const WideString & wsURL,const WideString & wsData,const WideString & wsEncode)375 bool CPDFXFA_Context::PutRequestURL(const WideString& wsURL,
376                                     const WideString& wsData,
377                                     const WideString& wsEncode) {
378   return m_pFormFillEnv &&
379          m_pFormFillEnv->PutRequestURL(wsURL, wsData, wsEncode);
380 }
381 
GetTimerHandler() const382 CFX_Timer::HandlerIface* CPDFXFA_Context::GetTimerHandler() const {
383   return m_pFormFillEnv ? m_pFormFillEnv->GetTimerHandler() : nullptr;
384 }
385 
GetGCHeap() const386 cppgc::Heap* CPDFXFA_Context::GetGCHeap() const {
387   return m_pGCHeap.get();
388 }
389 
SaveDatasetsPackage(const RetainPtr<IFX_SeekableStream> & pStream)390 bool CPDFXFA_Context::SaveDatasetsPackage(
391     const RetainPtr<IFX_SeekableStream>& pStream) {
392   return SavePackage(pStream, XFA_HASHCODE_Datasets);
393 }
394 
SaveFormPackage(const RetainPtr<IFX_SeekableStream> & pStream)395 bool CPDFXFA_Context::SaveFormPackage(
396     const RetainPtr<IFX_SeekableStream>& pStream) {
397   return SavePackage(pStream, XFA_HASHCODE_Form);
398 }
399 
SavePackage(const RetainPtr<IFX_SeekableStream> & pStream,XFA_HashCode code)400 bool CPDFXFA_Context::SavePackage(const RetainPtr<IFX_SeekableStream>& pStream,
401                                   XFA_HashCode code) {
402   CXFA_FFDocView* pXFADocView = GetXFADocView();
403   if (!pXFADocView)
404     return false;
405 
406   CXFA_FFDoc* ffdoc = pXFADocView->GetDoc();
407   return ffdoc->SavePackage(ToNode(ffdoc->GetXFADoc()->GetXFAObject(code)),
408                             pStream);
409 }
410 
SendPostSaveToXFADoc()411 void CPDFXFA_Context::SendPostSaveToXFADoc() {
412   if (!ContainsExtensionForm())
413     return;
414 
415   CXFA_FFDocView* pXFADocView = GetXFADocView();
416   if (!pXFADocView)
417     return;
418 
419   CXFA_FFWidgetHandler* pWidgetHandler = pXFADocView->GetWidgetHandler();
420   CXFA_ReadyNodeIterator it(pXFADocView->GetRootSubform());
421   while (CXFA_Node* pNode = it.MoveToNext()) {
422     CXFA_EventParam preParam;
423     preParam.m_eType = XFA_EVENT_PostSave;
424     preParam.m_bTargeted = false;
425     pWidgetHandler->ProcessEvent(pNode, &preParam);
426   }
427   pXFADocView->UpdateDocView();
428   ClearChangeMark();
429 }
430 
SendPreSaveToXFADoc(std::vector<RetainPtr<IFX_SeekableStream>> * fileList)431 void CPDFXFA_Context::SendPreSaveToXFADoc(
432     std::vector<RetainPtr<IFX_SeekableStream>>* fileList) {
433   if (!ContainsExtensionForm())
434     return;
435 
436   CXFA_FFDocView* pXFADocView = GetXFADocView();
437   if (!pXFADocView)
438     return;
439 
440   CXFA_FFWidgetHandler* pWidgetHandler = pXFADocView->GetWidgetHandler();
441   CXFA_ReadyNodeIterator it(pXFADocView->GetRootSubform());
442   while (CXFA_Node* pNode = it.MoveToNext()) {
443     CXFA_EventParam preParam;
444     preParam.m_eType = XFA_EVENT_PreSave;
445     preParam.m_bTargeted = false;
446     pWidgetHandler->ProcessEvent(pNode, &preParam);
447   }
448   pXFADocView->UpdateDocView();
449   return;
450 }
451