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 <utility>
10
11 #include "core/fpdfapi/parser/cpdf_document.h"
12 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
13 #include "fpdfsdk/cpdfsdk_interform.h"
14 #include "fpdfsdk/cpdfsdk_pageview.h"
15 #include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
16 #include "fpdfsdk/fpdfxfa/cxfa_fwladaptertimermgr.h"
17 #include "fpdfsdk/fsdk_define.h"
18 #include "fpdfsdk/javascript/cjs_runtime.h"
19 #include "fpdfsdk/javascript/ijs_runtime.h"
20 #include "public/fpdf_formfill.h"
21 #include "third_party/base/ptr_util.h"
22 #include "third_party/base/stl_util.h"
23 #include "xfa/fxfa/cxfa_eventparam.h"
24 #include "xfa/fxfa/xfa_ffapp.h"
25 #include "xfa/fxfa/xfa_ffdoc.h"
26 #include "xfa/fxfa/xfa_ffdocview.h"
27 #include "xfa/fxfa/xfa_ffpageview.h"
28 #include "xfa/fxfa/xfa_ffwidgethandler.h"
29 #include "xfa/fxfa/xfa_fontmgr.h"
30
31 #ifndef _WIN32
32 extern void SetLastError(int err);
33 extern int GetLastError();
34 #endif
35
CPDFXFA_Context(std::unique_ptr<CPDF_Document> pPDFDoc)36 CPDFXFA_Context::CPDFXFA_Context(std::unique_ptr<CPDF_Document> pPDFDoc)
37 : m_iDocType(DOCTYPE_PDF),
38 m_pPDFDoc(std::move(pPDFDoc)),
39 m_pFormFillEnv(nullptr),
40 m_pXFADocView(nullptr),
41 m_nLoadStatus(FXFA_LOADSTATUS_PRELOAD),
42 m_nPageCount(0),
43 m_DocEnv(this) {
44 m_pXFAApp = pdfium::MakeUnique<CXFA_FFApp>(this);
45 m_pXFAApp->SetDefaultFontMgr(pdfium::MakeUnique<CXFA_DefFontMgr>());
46 }
47
~CPDFXFA_Context()48 CPDFXFA_Context::~CPDFXFA_Context() {
49 m_nLoadStatus = FXFA_LOADSTATUS_CLOSING;
50
51 // Must happen before we remove the form fill environment.
52 CloseXFADoc();
53
54 if (m_pFormFillEnv) {
55 m_pFormFillEnv->ClearAllFocusedAnnots();
56 // Once we're deleted the FormFillEnvironment will point at a bad underlying
57 // doc so we need to reset it ...
58 m_pFormFillEnv->ResetXFADocument();
59 m_pFormFillEnv = nullptr;
60 }
61
62 m_nLoadStatus = FXFA_LOADSTATUS_CLOSED;
63 }
64
CloseXFADoc()65 void CPDFXFA_Context::CloseXFADoc() {
66 if (!m_pXFADoc)
67 return;
68 m_pXFADoc->CloseDoc();
69 m_pXFADoc.reset();
70 m_pXFADocView = nullptr;
71 }
72
SetFormFillEnv(CPDFSDK_FormFillEnvironment * pFormFillEnv)73 void CPDFXFA_Context::SetFormFillEnv(
74 CPDFSDK_FormFillEnvironment* pFormFillEnv) {
75 // The layout data can have pointers back into the script context. That
76 // context will be different if the form fill environment closes, so, force
77 // the layout data to clear.
78 if (m_pXFADoc && m_pXFADoc->GetXFADoc())
79 m_pXFADoc->GetXFADoc()->ClearLayoutData();
80
81 m_pFormFillEnv = pFormFillEnv;
82 }
83
LoadXFADoc()84 bool CPDFXFA_Context::LoadXFADoc() {
85 m_nLoadStatus = FXFA_LOADSTATUS_LOADING;
86 if (!m_pPDFDoc)
87 return false;
88
89 m_XFAPageList.clear();
90
91 CXFA_FFApp* pApp = GetXFAApp();
92 if (!pApp)
93 return false;
94
95 m_pXFADoc = pApp->CreateDoc(&m_DocEnv, m_pPDFDoc.get());
96 if (!m_pXFADoc) {
97 SetLastError(FPDF_ERR_XFALOAD);
98 return false;
99 }
100
101 CXFA_FFDocHandler* pDocHandler = pApp->GetDocHandler();
102 if (!pDocHandler) {
103 SetLastError(FPDF_ERR_XFALOAD);
104 return false;
105 }
106
107 m_pXFADoc->StartLoad();
108 int iStatus = m_pXFADoc->DoLoad(nullptr);
109 if (iStatus != XFA_PARSESTATUS_Done) {
110 CloseXFADoc();
111 SetLastError(FPDF_ERR_XFALOAD);
112 return false;
113 }
114 m_pXFADoc->StopLoad();
115 m_pXFADoc->GetXFADoc()->InitScriptContext(GetJSERuntime());
116
117 if (m_pXFADoc->GetDocType() == XFA_DOCTYPE_Dynamic)
118 m_iDocType = DOCTYPE_DYNAMIC_XFA;
119 else
120 m_iDocType = DOCTYPE_STATIC_XFA;
121
122 m_pXFADocView = m_pXFADoc->CreateDocView(XFA_DOCVIEW_View);
123 if (m_pXFADocView->StartLayout() < 0) {
124 CloseXFADoc();
125 SetLastError(FPDF_ERR_XFALAYOUT);
126 return false;
127 }
128
129 m_pXFADocView->DoLayout(nullptr);
130 m_pXFADocView->StopLayout();
131 m_nLoadStatus = FXFA_LOADSTATUS_LOADED;
132
133 return true;
134 }
135
GetPageCount() const136 int CPDFXFA_Context::GetPageCount() const {
137 if (!m_pPDFDoc && !m_pXFADoc)
138 return 0;
139
140 switch (m_iDocType) {
141 case DOCTYPE_PDF:
142 case DOCTYPE_STATIC_XFA:
143 if (m_pPDFDoc)
144 return m_pPDFDoc->GetPageCount();
145 case DOCTYPE_DYNAMIC_XFA:
146 if (m_pXFADoc)
147 return m_pXFADocView->CountPageViews();
148 default:
149 return 0;
150 }
151 }
152
GetXFAPage(int page_index)153 CPDFXFA_Page* CPDFXFA_Context::GetXFAPage(int page_index) {
154 if (page_index < 0)
155 return nullptr;
156
157 CPDFXFA_Page* pPage = nullptr;
158 int nCount = pdfium::CollectionSize<int>(m_XFAPageList);
159 if (nCount > 0 && page_index < nCount) {
160 pPage = m_XFAPageList[page_index];
161 if (pPage) {
162 pPage->Retain();
163 return pPage;
164 }
165 } else {
166 m_nPageCount = GetPageCount();
167 m_XFAPageList.resize(m_nPageCount);
168 }
169
170 pPage = new CPDFXFA_Page(this, page_index);
171 if (!pPage->LoadPage()) {
172 pPage->Release();
173 return nullptr;
174 }
175 if (page_index >= 0 &&
176 page_index < pdfium::CollectionSize<int>(m_XFAPageList)) {
177 m_XFAPageList[page_index] = pPage;
178 }
179 return pPage;
180 }
181
GetXFAPage(CXFA_FFPageView * pPage) const182 CPDFXFA_Page* CPDFXFA_Context::GetXFAPage(CXFA_FFPageView* pPage) const {
183 if (!pPage)
184 return nullptr;
185
186 if (!m_pXFADoc)
187 return nullptr;
188
189 if (m_iDocType != DOCTYPE_DYNAMIC_XFA)
190 return nullptr;
191
192 for (CPDFXFA_Page* pTempPage : m_XFAPageList) {
193 if (pTempPage && pTempPage->GetXFAPageView() == pPage)
194 return pTempPage;
195 }
196 return nullptr;
197 }
198
DeletePage(int page_index)199 void CPDFXFA_Context::DeletePage(int page_index) {
200 // Delete from the document first because, if GetPage was never called for
201 // this |page_index| then |m_XFAPageList| may have size < |page_index| even
202 // if it's a valid page in the document.
203 if (m_pPDFDoc)
204 m_pPDFDoc->DeletePage(page_index);
205
206 if (page_index < 0 ||
207 page_index >= pdfium::CollectionSize<int>(m_XFAPageList)) {
208 return;
209 }
210 if (CPDFXFA_Page* pPage = m_XFAPageList[page_index])
211 pPage->Release();
212 }
213
RemovePage(CPDFXFA_Page * page)214 void CPDFXFA_Context::RemovePage(CPDFXFA_Page* page) {
215 int page_index = page->GetPageIndex();
216 if (page_index >= 0 &&
217 page_index < pdfium::CollectionSize<int>(m_XFAPageList)) {
218 m_XFAPageList[page_index] = nullptr;
219 }
220 }
221
ClearChangeMark()222 void CPDFXFA_Context::ClearChangeMark() {
223 if (m_pFormFillEnv)
224 m_pFormFillEnv->ClearChangeMark();
225 }
226
GetJSERuntime() const227 v8::Isolate* CPDFXFA_Context::GetJSERuntime() const {
228 if (!m_pFormFillEnv)
229 return nullptr;
230
231 // XFA requires V8, if we have V8 then we have a CJS_Runtime and not the stub.
232 CJS_Runtime* runtime =
233 static_cast<CJS_Runtime*>(m_pFormFillEnv->GetJSRuntime());
234 return runtime->GetIsolate();
235 }
236
GetAppTitle() const237 CFX_WideString CPDFXFA_Context::GetAppTitle() const {
238 return L"PDFium";
239 }
240
GetAppName()241 CFX_WideString CPDFXFA_Context::GetAppName() {
242 return m_pFormFillEnv ? m_pFormFillEnv->FFI_GetAppName() : L"";
243 }
244
GetLanguage()245 CFX_WideString CPDFXFA_Context::GetLanguage() {
246 return m_pFormFillEnv ? m_pFormFillEnv->GetLanguage() : L"";
247 }
248
GetPlatform()249 CFX_WideString CPDFXFA_Context::GetPlatform() {
250 return m_pFormFillEnv ? m_pFormFillEnv->GetPlatform() : L"";
251 }
252
Beep(uint32_t dwType)253 void CPDFXFA_Context::Beep(uint32_t dwType) {
254 if (m_pFormFillEnv)
255 m_pFormFillEnv->JS_appBeep(dwType);
256 }
257
MsgBox(const CFX_WideString & wsMessage,const CFX_WideString & wsTitle,uint32_t dwIconType,uint32_t dwButtonType)258 int32_t CPDFXFA_Context::MsgBox(const CFX_WideString& wsMessage,
259 const CFX_WideString& wsTitle,
260 uint32_t dwIconType,
261 uint32_t dwButtonType) {
262 if (!m_pFormFillEnv)
263 return -1;
264
265 uint32_t iconType = 0;
266 int iButtonType = 0;
267 switch (dwIconType) {
268 case XFA_MBICON_Error:
269 iconType |= 0;
270 break;
271 case XFA_MBICON_Warning:
272 iconType |= 1;
273 break;
274 case XFA_MBICON_Question:
275 iconType |= 2;
276 break;
277 case XFA_MBICON_Status:
278 iconType |= 3;
279 break;
280 }
281 switch (dwButtonType) {
282 case XFA_MB_OK:
283 iButtonType |= 0;
284 break;
285 case XFA_MB_OKCancel:
286 iButtonType |= 1;
287 break;
288 case XFA_MB_YesNo:
289 iButtonType |= 2;
290 break;
291 case XFA_MB_YesNoCancel:
292 iButtonType |= 3;
293 break;
294 }
295 int32_t iRet = m_pFormFillEnv->JS_appAlert(wsMessage.c_str(), wsTitle.c_str(),
296 iButtonType, iconType);
297 switch (iRet) {
298 case 1:
299 return XFA_IDOK;
300 case 2:
301 return XFA_IDCancel;
302 case 3:
303 return XFA_IDNo;
304 case 4:
305 return XFA_IDYes;
306 }
307 return XFA_IDYes;
308 }
309
Response(const CFX_WideString & wsQuestion,const CFX_WideString & wsTitle,const CFX_WideString & wsDefaultAnswer,bool bMark)310 CFX_WideString CPDFXFA_Context::Response(const CFX_WideString& wsQuestion,
311 const CFX_WideString& wsTitle,
312 const CFX_WideString& wsDefaultAnswer,
313 bool bMark) {
314 CFX_WideString wsAnswer;
315 if (!m_pFormFillEnv)
316 return wsAnswer;
317
318 int nLength = 2048;
319 char* pBuff = new char[nLength];
320 nLength = m_pFormFillEnv->JS_appResponse(wsQuestion.c_str(), wsTitle.c_str(),
321 wsDefaultAnswer.c_str(), nullptr,
322 bMark, pBuff, nLength);
323 if (nLength > 0) {
324 nLength = nLength > 2046 ? 2046 : nLength;
325 pBuff[nLength] = 0;
326 pBuff[nLength + 1] = 0;
327 wsAnswer = CFX_WideString::FromUTF16LE(
328 reinterpret_cast<const unsigned short*>(pBuff),
329 nLength / sizeof(unsigned short));
330 }
331 delete[] pBuff;
332 return wsAnswer;
333 }
334
DownloadURL(const CFX_WideString & wsURL)335 CFX_RetainPtr<IFX_SeekableReadStream> CPDFXFA_Context::DownloadURL(
336 const CFX_WideString& wsURL) {
337 return m_pFormFillEnv ? m_pFormFillEnv->DownloadFromURL(wsURL.c_str())
338 : nullptr;
339 }
340
PostRequestURL(const CFX_WideString & wsURL,const CFX_WideString & wsData,const CFX_WideString & wsContentType,const CFX_WideString & wsEncode,const CFX_WideString & wsHeader,CFX_WideString & wsResponse)341 bool CPDFXFA_Context::PostRequestURL(const CFX_WideString& wsURL,
342 const CFX_WideString& wsData,
343 const CFX_WideString& wsContentType,
344 const CFX_WideString& wsEncode,
345 const CFX_WideString& wsHeader,
346 CFX_WideString& wsResponse) {
347 if (!m_pFormFillEnv)
348 return false;
349
350 wsResponse = m_pFormFillEnv->PostRequestURL(
351 wsURL.c_str(), wsData.c_str(), wsContentType.c_str(), wsEncode.c_str(),
352 wsHeader.c_str());
353 return true;
354 }
355
PutRequestURL(const CFX_WideString & wsURL,const CFX_WideString & wsData,const CFX_WideString & wsEncode)356 bool CPDFXFA_Context::PutRequestURL(const CFX_WideString& wsURL,
357 const CFX_WideString& wsData,
358 const CFX_WideString& wsEncode) {
359 return m_pFormFillEnv &&
360 m_pFormFillEnv->PutRequestURL(wsURL.c_str(), wsData.c_str(),
361 wsEncode.c_str());
362 }
363
GetTimerMgr()364 IFWL_AdapterTimerMgr* CPDFXFA_Context::GetTimerMgr() {
365 CXFA_FWLAdapterTimerMgr* pAdapter = nullptr;
366 if (m_pFormFillEnv)
367 pAdapter = new CXFA_FWLAdapterTimerMgr(m_pFormFillEnv);
368 return pAdapter;
369 }
370