1 // Copyright 2014 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 "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
8
9 #include <algorithm>
10 #include <utility>
11
12 #include "core/fpdfapi/parser/cpdf_array.h"
13 #include "core/fpdfapi/parser/cpdf_dictionary.h"
14 #include "core/fpdfapi/parser/cpdf_document.h"
15 #include "core/fpdfapi/parser/cpdf_seekablemultistream.h"
16 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
17 #include "fpdfsdk/cpdfsdk_pageview.h"
18 #include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
19 #include "fxjs/cjs_runtime.h"
20 #include "fxjs/ijs_runtime.h"
21 #include "public/fpdf_formfill.h"
22 #include "third_party/base/ptr_util.h"
23 #include "third_party/base/stl_util.h"
24 #include "xfa/fxfa/cxfa_eventparam.h"
25 #include "xfa/fxfa/cxfa_ffapp.h"
26 #include "xfa/fxfa/cxfa_ffdoc.h"
27 #include "xfa/fxfa/cxfa_ffdocview.h"
28 #include "xfa/fxfa/cxfa_ffpageview.h"
29 #include "xfa/fxfa/cxfa_ffwidgethandler.h"
30 #include "xfa/fxfa/cxfa_fontmgr.h"
31 #include "xfa/fxfa/cxfa_readynodeiterator.h"
32
33 namespace {
34
IsValidAlertButton(int type)35 bool IsValidAlertButton(int type) {
36 return type == JSPLATFORM_ALERT_BUTTON_OK ||
37 type == JSPLATFORM_ALERT_BUTTON_OKCANCEL ||
38 type == JSPLATFORM_ALERT_BUTTON_YESNO ||
39 type == JSPLATFORM_ALERT_BUTTON_YESNOCANCEL;
40 }
41
IsValidAlertIcon(int type)42 bool IsValidAlertIcon(int type) {
43 return type == JSPLATFORM_ALERT_ICON_ERROR ||
44 type == JSPLATFORM_ALERT_ICON_WARNING ||
45 type == JSPLATFORM_ALERT_ICON_QUESTION ||
46 type == JSPLATFORM_ALERT_ICON_STATUS ||
47 type == JSPLATFORM_ALERT_ICON_ASTERISK;
48 }
49
CreateXFAMultiStream(const CPDF_Document * pPDFDoc)50 RetainPtr<CPDF_SeekableMultiStream> CreateXFAMultiStream(
51 const CPDF_Document* pPDFDoc) {
52 const CPDF_Dictionary* pRoot = pPDFDoc->GetRoot();
53 if (!pRoot)
54 return nullptr;
55
56 const CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm");
57 if (!pAcroForm)
58 return nullptr;
59
60 const CPDF_Object* pElementXFA = pAcroForm->GetDirectObjectFor("XFA");
61 if (!pElementXFA)
62 return nullptr;
63
64 std::vector<const CPDF_Stream*> xfaStreams;
65 if (pElementXFA->IsArray()) {
66 const CPDF_Array* pXFAArray = pElementXFA->AsArray();
67 for (size_t i = 0; i < pXFAArray->size() / 2; i++) {
68 if (const CPDF_Stream* pStream = pXFAArray->GetStreamAt(i * 2 + 1))
69 xfaStreams.push_back(pStream);
70 }
71 } else if (pElementXFA->IsStream()) {
72 xfaStreams.push_back(pElementXFA->AsStream());
73 }
74 if (xfaStreams.empty())
75 return nullptr;
76
77 return pdfium::MakeRetain<CPDF_SeekableMultiStream>(xfaStreams);
78 }
79
80 } // namespace
81
CPDFXFA_Context(CPDF_Document * pPDFDoc)82 CPDFXFA_Context::CPDFXFA_Context(CPDF_Document* pPDFDoc)
83 : m_pPDFDoc(pPDFDoc),
84 m_pXFAApp(pdfium::MakeUnique<CXFA_FFApp>(this)),
85 m_DocEnv(this) {
86 ASSERT(m_pPDFDoc);
87 }
88
~CPDFXFA_Context()89 CPDFXFA_Context::~CPDFXFA_Context() {
90 m_nLoadStatus = FXFA_LOADSTATUS_CLOSING;
91
92 // Must happen before we remove the form fill environment.
93 CloseXFADoc();
94
95 if (m_pFormFillEnv) {
96 m_pFormFillEnv->ClearAllFocusedAnnots();
97 // Once we're deleted the FormFillEnvironment will point at a bad underlying
98 // doc so we need to reset it ...
99 m_pFormFillEnv->GetPDFDocument()->SetExtension(nullptr);
100 m_pFormFillEnv.Reset();
101 }
102
103 m_nLoadStatus = FXFA_LOADSTATUS_CLOSED;
104 }
105
CloseXFADoc()106 void CPDFXFA_Context::CloseXFADoc() {
107 if (!m_pXFADoc)
108 return;
109
110 m_pXFADocView = nullptr;
111 m_pXFADoc.reset();
112 }
113
SetFormFillEnv(CPDFSDK_FormFillEnvironment * pFormFillEnv)114 void CPDFXFA_Context::SetFormFillEnv(
115 CPDFSDK_FormFillEnvironment* pFormFillEnv) {
116 // The layout data can have pointers back into the script context. That
117 // context will be different if the form fill environment closes, so, force
118 // the layout data to clear.
119 if (m_pXFADoc && m_pXFADoc->GetXFADoc()) {
120 // The CPDF_XFADocView has a pointer to the CXFA_LayoutProcessor which is
121 // owned by the CXFA_Document. The Layout Processor will be freed with the
122 // ClearLayoutData() call. Make sure the doc view has already released the
123 // pointer.
124 if (m_pXFADocView)
125 m_pXFADocView->ResetLayoutProcessor();
126
127 m_pXFADoc->GetXFADoc()->ClearLayoutData();
128 }
129
130 m_pFormFillEnv.Reset(pFormFillEnv);
131 }
132
LoadXFADoc()133 bool CPDFXFA_Context::LoadXFADoc() {
134 m_nLoadStatus = FXFA_LOADSTATUS_LOADING;
135 m_XFAPageList.clear();
136
137 auto stream = CreateXFAMultiStream(m_pPDFDoc.Get());
138 if (!stream) {
139 FXSYS_SetLastError(FPDF_ERR_XFALOAD);
140 return false;
141 }
142
143 m_pXFADoc = CXFA_FFDoc::CreateAndOpen(m_pXFAApp.get(), &m_DocEnv,
144 m_pPDFDoc.Get(), stream);
145 if (!m_pXFADoc) {
146 FXSYS_SetLastError(FPDF_ERR_XFALOAD);
147 return false;
148 }
149
150 CJS_Runtime* actual_runtime = GetCJSRuntime(); // Null if a stub.
151 if (!actual_runtime) {
152 FXSYS_SetLastError(FPDF_ERR_XFALOAD);
153 return false;
154 }
155
156 m_pXFADoc->GetXFADoc()->InitScriptContext(actual_runtime);
157 if (m_pXFADoc->GetFormType() == FormType::kXFAFull)
158 m_FormType = FormType::kXFAFull;
159 else
160 m_FormType = FormType::kXFAForeground;
161
162 m_pXFADocView = m_pXFADoc->CreateDocView();
163 if (m_pXFADocView->StartLayout() < 0) {
164 CloseXFADoc();
165 FXSYS_SetLastError(FPDF_ERR_XFALAYOUT);
166 return false;
167 }
168
169 m_pXFADocView->DoLayout();
170 m_pXFADocView->StopLayout();
171 m_nLoadStatus = FXFA_LOADSTATUS_LOADED;
172 return true;
173 }
174
GetPageCount() const175 int CPDFXFA_Context::GetPageCount() const {
176 switch (m_FormType) {
177 case FormType::kNone:
178 case FormType::kAcroForm:
179 case FormType::kXFAForeground:
180 return m_pPDFDoc->GetPageCount();
181 case FormType::kXFAFull:
182 return m_pXFADoc ? m_pXFADocView->CountPageViews() : 0;
183 }
184 NOTREACHED();
185 return 0;
186 }
187
GetXFAPage(int page_index)188 RetainPtr<CPDFXFA_Page> CPDFXFA_Context::GetXFAPage(int page_index) {
189 if (page_index < 0)
190 return nullptr;
191
192 if (pdfium::IndexInBounds(m_XFAPageList, page_index)) {
193 if (m_XFAPageList[page_index])
194 return m_XFAPageList[page_index];
195 } else {
196 m_nPageCount = GetPageCount();
197 m_XFAPageList.resize(m_nPageCount);
198 }
199
200 auto pPage = pdfium::MakeRetain<CPDFXFA_Page>(GetPDFDoc(), page_index);
201 if (!pPage->LoadPage())
202 return nullptr;
203
204 if (pdfium::IndexInBounds(m_XFAPageList, page_index))
205 m_XFAPageList[page_index] = pPage;
206
207 return pPage;
208 }
209
GetXFAPage(CXFA_FFPageView * pPage) const210 RetainPtr<CPDFXFA_Page> CPDFXFA_Context::GetXFAPage(
211 CXFA_FFPageView* pPage) const {
212 if (!pPage)
213 return nullptr;
214
215 if (!m_pXFADoc)
216 return nullptr;
217
218 if (m_FormType != FormType::kXFAFull)
219 return nullptr;
220
221 for (auto& pTempPage : m_XFAPageList) {
222 if (pTempPage && pTempPage->GetXFAPageView() == pPage)
223 return pTempPage;
224 }
225 return nullptr;
226 }
227
GetPDFDoc() const228 CPDF_Document* CPDFXFA_Context::GetPDFDoc() const {
229 return m_pPDFDoc.Get();
230 }
231
DeletePage(int page_index)232 void CPDFXFA_Context::DeletePage(int page_index) {
233 // Delete from the document first because, if GetPage was never called for
234 // this |page_index| then |m_XFAPageList| may have size < |page_index| even
235 // if it's a valid page in the document.
236 m_pPDFDoc->DeletePage(page_index);
237
238 if (pdfium::IndexInBounds(m_XFAPageList, page_index))
239 m_XFAPageList[page_index].Reset();
240 }
241
GetUserPermissions() const242 uint32_t CPDFXFA_Context::GetUserPermissions() const {
243 // See https://bugs.chromium.org/p/pdfium/issues/detail?id=499
244 return 0xFFFFFFFF;
245 }
246
ContainsExtensionForm() const247 bool CPDFXFA_Context::ContainsExtensionForm() const {
248 return m_FormType == FormType::kXFAFull ||
249 m_FormType == FormType::kXFAForeground;
250 }
251
ContainsExtensionFullForm() const252 bool CPDFXFA_Context::ContainsExtensionFullForm() const {
253 return m_FormType == FormType::kXFAFull;
254 }
255
ContainsExtensionForegroundForm() const256 bool CPDFXFA_Context::ContainsExtensionForegroundForm() const {
257 return m_FormType == FormType::kXFAForeground;
258 }
259
ClearChangeMark()260 void CPDFXFA_Context::ClearChangeMark() {
261 if (m_pFormFillEnv)
262 m_pFormFillEnv->ClearChangeMark();
263 }
264
GetCJSRuntime() const265 CJS_Runtime* CPDFXFA_Context::GetCJSRuntime() const {
266 if (!m_pFormFillEnv)
267 return nullptr;
268
269 return m_pFormFillEnv->GetIJSRuntime()->AsCJSRuntime();
270 }
271
GetAppTitle() const272 WideString CPDFXFA_Context::GetAppTitle() const {
273 return L"PDFium";
274 }
275
GetAppName()276 WideString CPDFXFA_Context::GetAppName() {
277 return m_pFormFillEnv ? m_pFormFillEnv->FFI_GetAppName() : WideString();
278 }
279
GetLanguage()280 WideString CPDFXFA_Context::GetLanguage() {
281 return m_pFormFillEnv ? m_pFormFillEnv->GetLanguage() : WideString();
282 }
283
GetPlatform()284 WideString CPDFXFA_Context::GetPlatform() {
285 return m_pFormFillEnv ? m_pFormFillEnv->GetPlatform() : WideString();
286 }
287
Beep(uint32_t dwType)288 void CPDFXFA_Context::Beep(uint32_t dwType) {
289 if (m_pFormFillEnv)
290 m_pFormFillEnv->JS_appBeep(dwType);
291 }
292
MsgBox(const WideString & wsMessage,const WideString & wsTitle,uint32_t dwIconType,uint32_t dwButtonType)293 int32_t CPDFXFA_Context::MsgBox(const WideString& wsMessage,
294 const WideString& wsTitle,
295 uint32_t dwIconType,
296 uint32_t dwButtonType) {
297 if (!m_pFormFillEnv || m_nLoadStatus != FXFA_LOADSTATUS_LOADED)
298 return -1;
299
300 int iconType =
301 IsValidAlertIcon(dwIconType) ? dwIconType : JSPLATFORM_ALERT_ICON_DEFAULT;
302 int iButtonType = IsValidAlertButton(dwButtonType)
303 ? dwButtonType
304 : JSPLATFORM_ALERT_BUTTON_DEFAULT;
305 return m_pFormFillEnv->JS_appAlert(wsMessage, wsTitle, iButtonType, iconType);
306 }
307
Response(const WideString & wsQuestion,const WideString & wsTitle,const WideString & wsDefaultAnswer,bool bMark)308 WideString CPDFXFA_Context::Response(const WideString& wsQuestion,
309 const WideString& wsTitle,
310 const WideString& wsDefaultAnswer,
311 bool bMark) {
312 if (!m_pFormFillEnv)
313 return WideString();
314
315 int nLength = 2048;
316 std::vector<uint8_t> pBuff(nLength);
317 nLength = m_pFormFillEnv->JS_appResponse(wsQuestion, wsTitle, wsDefaultAnswer,
318 WideString(), bMark, pBuff.data(),
319 nLength);
320 if (nLength <= 0)
321 return WideString();
322
323 nLength = std::min(2046, nLength);
324 pBuff[nLength] = 0;
325 pBuff[nLength + 1] = 0;
326 return WideString::FromUTF16LE(reinterpret_cast<uint16_t*>(pBuff.data()),
327 nLength / sizeof(uint16_t));
328 }
329
DownloadURL(const WideString & wsURL)330 RetainPtr<IFX_SeekableReadStream> CPDFXFA_Context::DownloadURL(
331 const WideString& wsURL) {
332 return m_pFormFillEnv ? m_pFormFillEnv->DownloadFromURL(wsURL) : nullptr;
333 }
334
PostRequestURL(const WideString & wsURL,const WideString & wsData,const WideString & wsContentType,const WideString & wsEncode,const WideString & wsHeader,WideString & wsResponse)335 bool CPDFXFA_Context::PostRequestURL(const WideString& wsURL,
336 const WideString& wsData,
337 const WideString& wsContentType,
338 const WideString& wsEncode,
339 const WideString& wsHeader,
340 WideString& wsResponse) {
341 if (!m_pFormFillEnv)
342 return false;
343
344 wsResponse = m_pFormFillEnv->PostRequestURL(wsURL, wsData, wsContentType,
345 wsEncode, wsHeader);
346 return true;
347 }
348
PutRequestURL(const WideString & wsURL,const WideString & wsData,const WideString & wsEncode)349 bool CPDFXFA_Context::PutRequestURL(const WideString& wsURL,
350 const WideString& wsData,
351 const WideString& wsEncode) {
352 return m_pFormFillEnv &&
353 m_pFormFillEnv->PutRequestURL(wsURL, wsData, wsEncode);
354 }
355
GetTimerHandler() const356 TimerHandlerIface* CPDFXFA_Context::GetTimerHandler() const {
357 return m_pFormFillEnv ? m_pFormFillEnv->GetTimerHandler() : nullptr;
358 }
359
SaveDatasetsPackage(const RetainPtr<IFX_SeekableStream> & pStream)360 bool CPDFXFA_Context::SaveDatasetsPackage(
361 const RetainPtr<IFX_SeekableStream>& pStream) {
362 return SavePackage(pStream, XFA_HASHCODE_Datasets);
363 }
364
SaveFormPackage(const RetainPtr<IFX_SeekableStream> & pStream)365 bool CPDFXFA_Context::SaveFormPackage(
366 const RetainPtr<IFX_SeekableStream>& pStream) {
367 return SavePackage(pStream, XFA_HASHCODE_Form);
368 }
369
SavePackage(const RetainPtr<IFX_SeekableStream> & pStream,XFA_HashCode code)370 bool CPDFXFA_Context::SavePackage(const RetainPtr<IFX_SeekableStream>& pStream,
371 XFA_HashCode code) {
372 CXFA_FFDocView* pXFADocView = GetXFADocView();
373 if (!pXFADocView)
374 return false;
375
376 CXFA_FFDoc* ffdoc = pXFADocView->GetDoc();
377 return ffdoc->SavePackage(ToNode(ffdoc->GetXFADoc()->GetXFAObject(code)),
378 pStream);
379 }
380
SendPostSaveToXFADoc()381 void CPDFXFA_Context::SendPostSaveToXFADoc() {
382 if (!ContainsExtensionForm())
383 return;
384
385 CXFA_FFDocView* pXFADocView = GetXFADocView();
386 if (!pXFADocView)
387 return;
388
389 CXFA_FFWidgetHandler* pWidgetHandler = pXFADocView->GetWidgetHandler();
390 auto it = pXFADocView->CreateReadyNodeIterator();
391 while (CXFA_Node* pNode = it->MoveToNext()) {
392 CXFA_EventParam preParam;
393 preParam.m_eType = XFA_EVENT_PostSave;
394 pWidgetHandler->ProcessEvent(pNode, &preParam);
395 }
396 pXFADocView->UpdateDocView();
397 ClearChangeMark();
398 }
399
SendPreSaveToXFADoc(std::vector<RetainPtr<IFX_SeekableStream>> * fileList)400 void CPDFXFA_Context::SendPreSaveToXFADoc(
401 std::vector<RetainPtr<IFX_SeekableStream>>* fileList) {
402 if (!ContainsExtensionForm())
403 return;
404
405 CXFA_FFDocView* pXFADocView = GetXFADocView();
406 if (!pXFADocView)
407 return;
408
409 CXFA_FFWidgetHandler* pWidgetHandler = pXFADocView->GetWidgetHandler();
410 auto it = pXFADocView->CreateReadyNodeIterator();
411 while (CXFA_Node* pNode = it->MoveToNext()) {
412 CXFA_EventParam preParam;
413 preParam.m_eType = XFA_EVENT_PreSave;
414 pWidgetHandler->ProcessEvent(pNode, &preParam);
415 }
416 pXFADocView->UpdateDocView();
417 return;
418 }
419