1 // Copyright 2016 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_docenvironment.h"
8 
9 #include <memory>
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_stream.h"
15 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
16 #include "core/fpdfapi/parser/cpdf_string.h"
17 #include "core/fxcrt/retain_ptr.h"
18 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
19 #include "fpdfsdk/cpdfsdk_helpers.h"
20 #include "fpdfsdk/cpdfsdk_interactiveform.h"
21 #include "fpdfsdk/cpdfsdk_pageview.h"
22 #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
23 #include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
24 #include "xfa/fxfa/cxfa_ffdocview.h"
25 #include "xfa/fxfa/cxfa_ffwidget.h"
26 #include "xfa/fxfa/cxfa_ffwidgethandler.h"
27 #include "xfa/fxfa/cxfa_readynodeiterator.h"
28 #include "xfa/fxfa/parser/cxfa_node.h"
29 #include "xfa/fxfa/parser/cxfa_submit.h"
30 
31 #define IDS_XFA_Validate_Input                                          \
32   "At least one required field was empty. Please fill in the required " \
33   "fields\r\n(highlighted) before continuing."
34 
35 // submit
36 #define FXFA_CONFIG 0x00000001
37 #define FXFA_TEMPLATE 0x00000010
38 #define FXFA_LOCALESET 0x00000100
39 #define FXFA_DATASETS 0x00001000
40 #define FXFA_XMPMETA 0x00010000
41 #define FXFA_XFDF 0x00100000
42 #define FXFA_FORM 0x01000000
43 #define FXFA_PDF 0x10000000
44 #define FXFA_XFA_ALL 0x01111111
45 
CPDFXFA_DocEnvironment(CPDFXFA_Context * pContext)46 CPDFXFA_DocEnvironment::CPDFXFA_DocEnvironment(CPDFXFA_Context* pContext)
47     : m_pContext(pContext) {
48   ASSERT(m_pContext);
49 }
50 
~CPDFXFA_DocEnvironment()51 CPDFXFA_DocEnvironment::~CPDFXFA_DocEnvironment() {}
52 
SetChangeMark(CXFA_FFDoc * hDoc)53 void CPDFXFA_DocEnvironment::SetChangeMark(CXFA_FFDoc* hDoc) {
54   if (hDoc == m_pContext->GetXFADoc() && m_pContext->GetFormFillEnv())
55     m_pContext->GetFormFillEnv()->SetChangeMark();
56 }
57 
InvalidateRect(CXFA_FFPageView * pPageView,const CFX_RectF & rt)58 void CPDFXFA_DocEnvironment::InvalidateRect(CXFA_FFPageView* pPageView,
59                                             const CFX_RectF& rt) {
60   if (!m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv())
61     return;
62 
63   if (m_pContext->GetFormType() != FormType::kXFAFull)
64     return;
65 
66   RetainPtr<CPDFXFA_Page> pPage = m_pContext->GetXFAPage(pPageView);
67   if (!pPage)
68     return;
69 
70   CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
71   if (!pFormFillEnv)
72     return;
73 
74   pFormFillEnv->Invalidate(pPage.Get(), rt.ToFloatRect().ToFxRect());
75 }
76 
DisplayCaret(CXFA_FFWidget * hWidget,bool bVisible,const CFX_RectF * pRtAnchor)77 void CPDFXFA_DocEnvironment::DisplayCaret(CXFA_FFWidget* hWidget,
78                                           bool bVisible,
79                                           const CFX_RectF* pRtAnchor) {
80   if (!hWidget || !pRtAnchor || !m_pContext->GetXFADoc() ||
81       !m_pContext->GetFormFillEnv() || !m_pContext->GetXFADocView())
82     return;
83 
84   if (m_pContext->GetFormType() != FormType::kXFAFull)
85     return;
86 
87   CXFA_FFWidgetHandler* pWidgetHandler =
88       m_pContext->GetXFADocView()->GetWidgetHandler();
89   if (!pWidgetHandler)
90     return;
91 
92   CXFA_FFPageView* pPageView = hWidget->GetPageView();
93   if (!pPageView)
94     return;
95 
96   RetainPtr<CPDFXFA_Page> pPage = m_pContext->GetXFAPage(pPageView);
97   if (!pPage)
98     return;
99 
100   CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
101   if (!pFormFillEnv)
102     return;
103 
104   CFX_FloatRect rcCaret = pRtAnchor->ToFloatRect();
105   pFormFillEnv->DisplayCaret(pPage.Get(), bVisible, rcCaret.left, rcCaret.top,
106                              rcCaret.right, rcCaret.bottom);
107 }
108 
GetPopupPos(CXFA_FFWidget * hWidget,float fMinPopup,float fMaxPopup,const CFX_RectF & rtAnchor,CFX_RectF * pPopupRect)109 bool CPDFXFA_DocEnvironment::GetPopupPos(CXFA_FFWidget* hWidget,
110                                          float fMinPopup,
111                                          float fMaxPopup,
112                                          const CFX_RectF& rtAnchor,
113                                          CFX_RectF* pPopupRect) {
114   if (!hWidget)
115     return false;
116 
117   CXFA_FFPageView* pXFAPageView = hWidget->GetPageView();
118   if (!pXFAPageView)
119     return false;
120 
121   RetainPtr<CPDFXFA_Page> pPage = m_pContext->GetXFAPage(pXFAPageView);
122   if (!pPage)
123     return false;
124 
125   CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
126   if (!pFormFillEnv)
127     return false;
128 
129   FS_RECTF page_view_rect = pFormFillEnv->GetPageViewRect(pPage.Get());
130   int nRotate = hWidget->GetNode()->GetRotate();
131 
132   int space_available_below_anchor;
133   int space_available_above_anchor;
134   switch (nRotate) {
135     case 0:
136     default: {
137       space_available_below_anchor =
138           static_cast<int>(page_view_rect.bottom - rtAnchor.bottom());
139       space_available_above_anchor =
140           static_cast<int>(rtAnchor.top - page_view_rect.top);
141 
142       if (rtAnchor.left < page_view_rect.left)
143         pPopupRect->left += page_view_rect.left - rtAnchor.left;
144       if (rtAnchor.right() > page_view_rect.right)
145         pPopupRect->left -= rtAnchor.right() - page_view_rect.right;
146       break;
147     }
148     case 90: {
149       space_available_below_anchor =
150           static_cast<int>(page_view_rect.right - rtAnchor.right());
151       space_available_above_anchor =
152           static_cast<int>(rtAnchor.left - page_view_rect.left);
153 
154       if (rtAnchor.bottom() > page_view_rect.bottom)
155         pPopupRect->left += rtAnchor.bottom() - page_view_rect.bottom;
156       if (rtAnchor.top < page_view_rect.top)
157         pPopupRect->left -= page_view_rect.top - rtAnchor.top;
158       break;
159     }
160     case 180: {
161       space_available_below_anchor =
162           static_cast<int>(rtAnchor.top - page_view_rect.top);
163       space_available_above_anchor =
164           static_cast<int>(page_view_rect.bottom - rtAnchor.bottom());
165 
166       if (rtAnchor.right() > page_view_rect.right)
167         pPopupRect->left += rtAnchor.right() - page_view_rect.right;
168       if (rtAnchor.left < page_view_rect.left)
169         pPopupRect->left -= page_view_rect.left - rtAnchor.left;
170       break;
171     }
172     case 270: {
173       space_available_below_anchor =
174           static_cast<int>(rtAnchor.left - page_view_rect.left);
175       space_available_above_anchor =
176           static_cast<int>(page_view_rect.right - rtAnchor.right());
177 
178       if (rtAnchor.top < page_view_rect.top)
179         pPopupRect->left += page_view_rect.top - rtAnchor.top;
180       if (rtAnchor.bottom() > page_view_rect.bottom)
181         pPopupRect->left -= rtAnchor.bottom() - page_view_rect.bottom;
182       break;
183     }
184   }
185 
186   // If there is no space on either side, the popup can't be rendered.
187   if (space_available_below_anchor <= 0 && space_available_above_anchor <= 0)
188     return false;
189 
190   // Determine whether to draw above or below the anchor.
191   bool draw_below_anchor;
192   if (space_available_below_anchor <= 0)
193     draw_below_anchor = false;
194   else if (space_available_above_anchor <= 0)
195     draw_below_anchor = true;
196   else if (space_available_below_anchor > space_available_above_anchor)
197     draw_below_anchor = true;
198   else
199     draw_below_anchor = false;
200 
201   int space_available = (draw_below_anchor ? space_available_below_anchor
202                                            : space_available_above_anchor);
203 
204   // Set the popup height and y position according to what was decided above.
205   float popup_height;
206   if (space_available < fMinPopup)
207     popup_height = fMinPopup;
208   else if (space_available > fMaxPopup)
209     popup_height = fMaxPopup;
210   else
211     popup_height = static_cast<float>(space_available);
212 
213   switch (nRotate) {
214     case 0:
215     case 180: {
216       if (draw_below_anchor)
217         pPopupRect->top = rtAnchor.height;
218       else
219         pPopupRect->top = -popup_height;
220       break;
221     }
222     case 90:
223     case 270: {
224       if (draw_below_anchor)
225         pPopupRect->top = rtAnchor.width;
226       else
227         pPopupRect->top = -popup_height;
228       break;
229     }
230     default:
231       break;
232   }
233 
234   pPopupRect->height = popup_height;
235   return true;
236 }
237 
PopupMenu(CXFA_FFWidget * hWidget,const CFX_PointF & ptPopup)238 bool CPDFXFA_DocEnvironment::PopupMenu(CXFA_FFWidget* hWidget,
239                                        const CFX_PointF& ptPopup) {
240   if (!hWidget)
241     return false;
242 
243   CXFA_FFPageView* pXFAPageView = hWidget->GetPageView();
244   if (!pXFAPageView)
245     return false;
246 
247   RetainPtr<CPDFXFA_Page> pPage = m_pContext->GetXFAPage(pXFAPageView);
248   if (!pPage)
249     return false;
250 
251   CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
252   if (!pFormFillEnv)
253     return false;
254 
255   int menuFlag = 0;
256   if (hWidget->CanUndo())
257     menuFlag |= FXFA_MENU_UNDO;
258   if (hWidget->CanRedo())
259     menuFlag |= FXFA_MENU_REDO;
260   if (hWidget->CanPaste())
261     menuFlag |= FXFA_MENU_PASTE;
262   if (hWidget->CanCopy())
263     menuFlag |= FXFA_MENU_COPY;
264   if (hWidget->CanCut())
265     menuFlag |= FXFA_MENU_CUT;
266   if (hWidget->CanSelectAll())
267     menuFlag |= FXFA_MENU_SELECTALL;
268 
269   return pFormFillEnv->PopupMenu(pPage.Get(), menuFlag, ptPopup);
270 }
271 
PageViewEvent(CXFA_FFPageView * pPageView,uint32_t dwFlags)272 void CPDFXFA_DocEnvironment::PageViewEvent(CXFA_FFPageView* pPageView,
273                                            uint32_t dwFlags) {
274   CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
275   if (!pFormFillEnv)
276     return;
277 
278   if (m_pContext->GetLoadStatus() == FXFA_LOADSTATUS_LOADING ||
279       m_pContext->GetLoadStatus() == FXFA_LOADSTATUS_CLOSING ||
280       XFA_PAGEVIEWEVENT_StopLayout != dwFlags)
281     return;
282 
283   int nNewCount = m_pContext->GetPageCount();
284   if (nNewCount == m_pContext->GetOriginalPageCount())
285     return;
286 
287   CXFA_FFDocView* pXFADocView = m_pContext->GetXFADocView();
288   if (!pXFADocView)
289     return;
290 
291   for (int iPageIter = 0; iPageIter < m_pContext->GetOriginalPageCount();
292        iPageIter++) {
293     RetainPtr<CPDFXFA_Page> pPage = (*m_pContext->GetXFAPageList())[iPageIter];
294     if (!pPage)
295       continue;
296 
297     m_pContext->GetFormFillEnv()->RemovePageView(pPage.Get());
298     pPage->SetXFAPageViewIndex(iPageIter);
299   }
300 
301   int flag = (nNewCount < m_pContext->GetOriginalPageCount())
302                  ? FXFA_PAGEVIEWEVENT_POSTREMOVED
303                  : FXFA_PAGEVIEWEVENT_POSTADDED;
304   int count = abs(nNewCount - m_pContext->GetOriginalPageCount());
305   m_pContext->SetOriginalPageCount(nNewCount);
306   pFormFillEnv->PageEvent(count, flag);
307 }
308 
WidgetPostAdd(CXFA_FFWidget * hWidget)309 void CPDFXFA_DocEnvironment::WidgetPostAdd(CXFA_FFWidget* hWidget) {
310   if (m_pContext->GetFormType() != FormType::kXFAFull || !hWidget)
311     return;
312 
313   CXFA_FFPageView* pPageView = hWidget->GetPageView();
314   if (!pPageView)
315     return;
316 
317   RetainPtr<CPDFXFA_Page> pXFAPage = m_pContext->GetXFAPage(pPageView);
318   if (!pXFAPage)
319     return;
320 
321   m_pContext->GetFormFillEnv()
322       ->GetPageView(pXFAPage.Get(), true)
323       ->AddAnnot(hWidget);
324 }
325 
WidgetPreRemove(CXFA_FFWidget * hWidget)326 void CPDFXFA_DocEnvironment::WidgetPreRemove(CXFA_FFWidget* hWidget) {
327   if (m_pContext->GetFormType() != FormType::kXFAFull || !hWidget)
328     return;
329 
330   CXFA_FFPageView* pPageView = hWidget->GetPageView();
331   if (!pPageView)
332     return;
333 
334   RetainPtr<CPDFXFA_Page> pXFAPage = m_pContext->GetXFAPage(pPageView);
335   if (!pXFAPage)
336     return;
337 
338   CPDFSDK_PageView* pSdkPageView =
339       m_pContext->GetFormFillEnv()->GetPageView(pXFAPage.Get(), true);
340   CPDFSDK_Annot* pAnnot = pSdkPageView->GetAnnotByXFAWidget(hWidget);
341   if (pAnnot)
342     pSdkPageView->DeleteAnnot(pAnnot);
343 }
344 
CountPages(CXFA_FFDoc * hDoc)345 int32_t CPDFXFA_DocEnvironment::CountPages(CXFA_FFDoc* hDoc) {
346   if (hDoc == m_pContext->GetXFADoc() && m_pContext->GetFormFillEnv())
347     return m_pContext->GetPageCount();
348   return 0;
349 }
350 
GetCurrentPage(CXFA_FFDoc * hDoc)351 int32_t CPDFXFA_DocEnvironment::GetCurrentPage(CXFA_FFDoc* hDoc) {
352   if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv())
353     return -1;
354 
355   if (m_pContext->GetFormType() != FormType::kXFAFull)
356     return -1;
357 
358   CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
359   return pFormFillEnv ? pFormFillEnv->GetCurrentPageIndex() : -1;
360 }
361 
SetCurrentPage(CXFA_FFDoc * hDoc,int32_t iCurPage)362 void CPDFXFA_DocEnvironment::SetCurrentPage(CXFA_FFDoc* hDoc,
363                                             int32_t iCurPage) {
364   if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv() ||
365       !m_pContext->ContainsExtensionForm() || iCurPage < 0 ||
366       iCurPage >= m_pContext->GetFormFillEnv()->GetPageCount()) {
367     return;
368   }
369 
370   CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
371   if (!pFormFillEnv)
372     return;
373 
374   pFormFillEnv->SetCurrentPage(iCurPage);
375 }
376 
IsCalculationsEnabled(CXFA_FFDoc * hDoc)377 bool CPDFXFA_DocEnvironment::IsCalculationsEnabled(CXFA_FFDoc* hDoc) {
378   if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv())
379     return false;
380   auto* pForm = m_pContext->GetFormFillEnv()->GetInteractiveForm();
381   return pForm->IsXfaCalculateEnabled();
382 }
383 
SetCalculationsEnabled(CXFA_FFDoc * hDoc,bool bEnabled)384 void CPDFXFA_DocEnvironment::SetCalculationsEnabled(CXFA_FFDoc* hDoc,
385                                                     bool bEnabled) {
386   if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv())
387     return;
388   m_pContext->GetFormFillEnv()->GetInteractiveForm()->XfaEnableCalculate(
389       bEnabled);
390 }
391 
GetTitle(CXFA_FFDoc * hDoc,WideString & wsTitle)392 void CPDFXFA_DocEnvironment::GetTitle(CXFA_FFDoc* hDoc, WideString& wsTitle) {
393   if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetPDFDoc())
394     return;
395 
396   const CPDF_Dictionary* pInfoDict = m_pContext->GetPDFDoc()->GetInfo();
397   if (!pInfoDict)
398     return;
399 
400   ByteString csTitle = pInfoDict->GetStringFor("Title");
401   wsTitle = WideString::FromDefANSI(csTitle.AsStringView());
402 }
403 
SetTitle(CXFA_FFDoc * hDoc,const WideString & wsTitle)404 void CPDFXFA_DocEnvironment::SetTitle(CXFA_FFDoc* hDoc,
405                                       const WideString& wsTitle) {
406   if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetPDFDoc())
407     return;
408 
409   CPDF_Dictionary* pInfoDict = m_pContext->GetPDFDoc()->GetInfo();
410   if (pInfoDict)
411     pInfoDict->SetNewFor<CPDF_String>("Title", wsTitle);
412 }
413 
ExportData(CXFA_FFDoc * hDoc,const WideString & wsFilePath,bool bXDP)414 void CPDFXFA_DocEnvironment::ExportData(CXFA_FFDoc* hDoc,
415                                         const WideString& wsFilePath,
416                                         bool bXDP) {
417   if (hDoc != m_pContext->GetXFADoc())
418     return;
419 
420   if (!m_pContext->ContainsExtensionForm())
421     return;
422 
423   CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
424   if (!pFormFillEnv)
425     return;
426 
427   int fileType = bXDP ? FXFA_SAVEAS_XDP : FXFA_SAVEAS_XML;
428   ByteString bs = wsFilePath.ToUTF16LE();
429   if (wsFilePath.IsEmpty()) {
430     if (!pFormFillEnv->GetFormFillInfo() ||
431         !pFormFillEnv->GetFormFillInfo()->m_pJsPlatform) {
432       return;
433     }
434 
435     WideString filepath = pFormFillEnv->JS_fieldBrowse();
436     bs = filepath.ToUTF16LE();
437   }
438   FPDF_FILEHANDLER* pFileHandler = pFormFillEnv->OpenFile(
439       bXDP ? FXFA_SAVEAS_XDP : FXFA_SAVEAS_XML, AsFPDFWideString(&bs), "wb");
440   if (!pFileHandler)
441     return;
442 
443   RetainPtr<IFX_SeekableStream> fileWrite = MakeSeekableStream(pFileHandler);
444   if (fileType == FXFA_SAVEAS_XML) {
445     fileWrite->WriteString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n");
446     CXFA_FFDoc* ffdoc = m_pContext->GetXFADocView()->GetDoc();
447     ffdoc->SavePackage(
448         ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Data)), fileWrite);
449   } else if (fileType == FXFA_SAVEAS_XDP) {
450     if (!m_pContext->GetPDFDoc())
451       return;
452 
453     const CPDF_Dictionary* pRoot = m_pContext->GetPDFDoc()->GetRoot();
454     if (!pRoot)
455       return;
456 
457     const CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm");
458     if (!pAcroForm)
459       return;
460 
461     const CPDF_Array* pArray = ToArray(pAcroForm->GetObjectFor("XFA"));
462     if (!pArray)
463       return;
464 
465     for (size_t i = 1; i < pArray->size(); i += 2) {
466       const CPDF_Object* pPDFObj = pArray->GetObjectAt(i);
467       const CPDF_Object* pPrePDFObj = pArray->GetObjectAt(i - 1);
468       if (!pPrePDFObj->IsString())
469         continue;
470       if (!pPDFObj->IsReference())
471         continue;
472 
473       const CPDF_Stream* pStream = ToStream(pPDFObj->GetDirect());
474       if (!pStream)
475         continue;
476       if (pPrePDFObj->GetString() == "form") {
477         CXFA_FFDoc* ffdoc = m_pContext->GetXFADocView()->GetDoc();
478         ffdoc->SavePackage(
479             ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Form)),
480             fileWrite);
481         continue;
482       }
483       if (pPrePDFObj->GetString() == "datasets") {
484         CXFA_FFDoc* ffdoc = m_pContext->GetXFADocView()->GetDoc();
485         ffdoc->SavePackage(
486             ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Datasets)),
487             fileWrite);
488         continue;
489       }
490       if (i == pArray->size() - 1) {
491         WideString wPath = WideString::FromUTF16LE(
492             reinterpret_cast<const unsigned short*>(bs.c_str()),
493             bs.GetLength() / sizeof(unsigned short));
494         ByteString bPath = wPath.ToUTF8();
495         static const char kFormat[] =
496             "\n<pdf href=\"%s\" xmlns=\"http://ns.adobe.com/xdp/pdf/\"/>";
497         ByteString content = ByteString::Format(kFormat, bPath.c_str());
498         fileWrite->WriteString(content.AsStringView());
499       }
500       auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pStream);
501       pAcc->LoadAllDataFiltered();
502       fileWrite->WriteBlock(pAcc->GetData(), pAcc->GetSize());
503     }
504   }
505   fileWrite->Flush();
506 }
507 
GotoURL(CXFA_FFDoc * hDoc,const WideString & wsURL)508 void CPDFXFA_DocEnvironment::GotoURL(CXFA_FFDoc* hDoc,
509                                      const WideString& wsURL) {
510   if (hDoc != m_pContext->GetXFADoc())
511     return;
512 
513   if (m_pContext->GetFormType() != FormType::kXFAFull)
514     return;
515 
516   CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
517   if (!pFormFillEnv)
518     return;
519 
520   pFormFillEnv->GotoURL(wsURL);
521 }
522 
IsValidationsEnabled(CXFA_FFDoc * hDoc)523 bool CPDFXFA_DocEnvironment::IsValidationsEnabled(CXFA_FFDoc* hDoc) {
524   if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv())
525     return false;
526 
527   auto* pForm = m_pContext->GetFormFillEnv()->GetInteractiveForm();
528   return pForm->IsXfaValidationsEnabled();
529 }
530 
SetValidationsEnabled(CXFA_FFDoc * hDoc,bool bEnabled)531 void CPDFXFA_DocEnvironment::SetValidationsEnabled(CXFA_FFDoc* hDoc,
532                                                    bool bEnabled) {
533   if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv())
534     return;
535 
536   m_pContext->GetFormFillEnv()->GetInteractiveForm()->XfaSetValidationsEnabled(
537       bEnabled);
538 }
539 
SetFocusWidget(CXFA_FFDoc * hDoc,CXFA_FFWidget * hWidget)540 void CPDFXFA_DocEnvironment::SetFocusWidget(CXFA_FFDoc* hDoc,
541                                             CXFA_FFWidget* hWidget) {
542   if (hDoc != m_pContext->GetXFADoc())
543     return;
544 
545   if (!hWidget) {
546     ObservedPtr<CPDFSDK_Annot> pNull;
547     m_pContext->GetFormFillEnv()->SetFocusAnnot(&pNull);
548     return;
549   }
550 
551   int pageViewCount = m_pContext->GetFormFillEnv()->GetPageViewCount();
552   for (int i = 0; i < pageViewCount; i++) {
553     CPDFSDK_PageView* pPageView =
554         m_pContext->GetFormFillEnv()->GetPageViewAtIndex(i);
555     if (!pPageView)
556       continue;
557 
558     ObservedPtr<CPDFSDK_Annot> pAnnot(pPageView->GetAnnotByXFAWidget(hWidget));
559     if (pAnnot) {
560       m_pContext->GetFormFillEnv()->SetFocusAnnot(&pAnnot);
561       break;
562     }
563   }
564 }
565 
Print(CXFA_FFDoc * hDoc,int32_t nStartPage,int32_t nEndPage,uint32_t dwOptions)566 void CPDFXFA_DocEnvironment::Print(CXFA_FFDoc* hDoc,
567                                    int32_t nStartPage,
568                                    int32_t nEndPage,
569                                    uint32_t dwOptions) {
570   if (hDoc != m_pContext->GetXFADoc())
571     return;
572 
573   CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
574   if (!pFormFillEnv || !pFormFillEnv->GetFormFillInfo() ||
575       !pFormFillEnv->GetFormFillInfo()->m_pJsPlatform ||
576       !pFormFillEnv->GetFormFillInfo()->m_pJsPlatform->Doc_print) {
577     return;
578   }
579 
580   pFormFillEnv->GetFormFillInfo()->m_pJsPlatform->Doc_print(
581       pFormFillEnv->GetFormFillInfo()->m_pJsPlatform,
582       dwOptions & XFA_PRINTOPT_ShowDialog, nStartPage, nEndPage,
583       dwOptions & XFA_PRINTOPT_CanCancel, dwOptions & XFA_PRINTOPT_ShrinkPage,
584       dwOptions & XFA_PRINTOPT_AsImage, dwOptions & XFA_PRINTOPT_ReverseOrder,
585       dwOptions & XFA_PRINTOPT_PrintAnnot);
586 }
587 
GetHighlightColor(CXFA_FFDoc * hDoc)588 FX_ARGB CPDFXFA_DocEnvironment::GetHighlightColor(CXFA_FFDoc* hDoc) {
589   if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv())
590     return 0;
591 
592   CPDFSDK_InteractiveForm* pForm =
593       m_pContext->GetFormFillEnv()->GetInteractiveForm();
594   return AlphaAndColorRefToArgb(pForm->GetHighlightAlpha(),
595                                 pForm->GetHighlightColor(FormFieldType::kXFA));
596 }
597 
GetIJSRuntime(CXFA_FFDoc * hDoc) const598 IJS_Runtime* CPDFXFA_DocEnvironment::GetIJSRuntime(CXFA_FFDoc* hDoc) const {
599   if (hDoc != m_pContext->GetXFADoc())
600     return nullptr;
601 
602   CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
603   return pFormFillEnv ? pFormFillEnv->GetIJSRuntime() : nullptr;
604 }
605 
OpenLinkedFile(CXFA_FFDoc * hDoc,const WideString & wsLink)606 RetainPtr<IFX_SeekableReadStream> CPDFXFA_DocEnvironment::OpenLinkedFile(
607     CXFA_FFDoc* hDoc,
608     const WideString& wsLink) {
609   CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
610   if (!pFormFillEnv)
611     return nullptr;
612 
613   ByteString bs = wsLink.ToUTF16LE();
614   FPDF_FILEHANDLER* pFileHandler =
615       pFormFillEnv->OpenFile(0, AsFPDFWideString(&bs), "rb");
616   if (!pFileHandler)
617     return nullptr;
618 
619   return MakeSeekableStream(pFileHandler);
620 }
621 
622 #ifdef PDF_XFA_ELEMENT_SUBMIT_ENABLED
Submit(CXFA_FFDoc * hDoc,CXFA_Submit * submit)623 bool CPDFXFA_DocEnvironment::Submit(CXFA_FFDoc* hDoc, CXFA_Submit* submit) {
624   if (!NotifySubmit(true) || !m_pContext->GetXFADocView())
625     return false;
626 
627   m_pContext->GetXFADocView()->UpdateDocView();
628   bool ret = SubmitInternal(hDoc, submit);
629   NotifySubmit(false);
630   return ret;
631 }
632 
MailToInfo(WideString & csURL,WideString & csToAddress,WideString & csCCAddress,WideString & csBCCAddress,WideString & csSubject,WideString & csMsg)633 bool CPDFXFA_DocEnvironment::MailToInfo(WideString& csURL,
634                                         WideString& csToAddress,
635                                         WideString& csCCAddress,
636                                         WideString& csBCCAddress,
637                                         WideString& csSubject,
638                                         WideString& csMsg) {
639   WideString srcURL = csURL;
640   srcURL.TrimLeft();
641   if (srcURL.Left(7).CompareNoCase(L"mailto:") != 0)
642     return false;
643 
644   auto pos = srcURL.Find(L'?');
645 
646   {
647     WideString tmp;
648     if (!pos.has_value()) {
649       pos = srcURL.Find(L'@');
650       if (!pos.has_value())
651         return false;
652 
653       tmp = srcURL.Right(csURL.GetLength() - 7);
654     } else {
655       tmp = srcURL.Left(pos.value());
656       tmp = tmp.Right(tmp.GetLength() - 7);
657     }
658     tmp.Trim();
659     csToAddress = std::move(tmp);
660   }
661 
662   srcURL = srcURL.Right(srcURL.GetLength() - (pos.value() + 1));
663   while (!srcURL.IsEmpty()) {
664     srcURL.Trim();
665     pos = srcURL.Find(L'&');
666     WideString tmp = (!pos.has_value()) ? srcURL : srcURL.Left(pos.value());
667     tmp.Trim();
668     if (tmp.GetLength() >= 3 && tmp.Left(3).CompareNoCase(L"cc=") == 0) {
669       tmp = tmp.Right(tmp.GetLength() - 3);
670       if (!csCCAddress.IsEmpty())
671         csCCAddress += L';';
672       csCCAddress += tmp;
673     } else if (tmp.GetLength() >= 4 &&
674                tmp.Left(4).CompareNoCase(L"bcc=") == 0) {
675       tmp = tmp.Right(tmp.GetLength() - 4);
676       if (!csBCCAddress.IsEmpty())
677         csBCCAddress += L';';
678       csBCCAddress += tmp;
679     } else if (tmp.GetLength() >= 8 &&
680                tmp.Left(8).CompareNoCase(L"subject=") == 0) {
681       tmp = tmp.Right(tmp.GetLength() - 8);
682       csSubject += tmp;
683     } else if (tmp.GetLength() >= 5 &&
684                tmp.Left(5).CompareNoCase(L"body=") == 0) {
685       tmp = tmp.Right(tmp.GetLength() - 5);
686       csMsg += tmp;
687     }
688     srcURL = pos.has_value()
689                  ? srcURL.Right(csURL.GetLength() - (pos.value() + 1))
690                  : WideString();
691   }
692   csToAddress.Replace(L",", L";");
693   csCCAddress.Replace(L",", L";");
694   csBCCAddress.Replace(L",", L";");
695   return true;
696 }
697 
ExportSubmitFile(FPDF_FILEHANDLER * pFileHandler,int fileType,FPDF_DWORD encodeType,FPDF_DWORD flag)698 bool CPDFXFA_DocEnvironment::ExportSubmitFile(FPDF_FILEHANDLER* pFileHandler,
699                                               int fileType,
700                                               FPDF_DWORD encodeType,
701                                               FPDF_DWORD flag) {
702   if (!m_pContext->GetXFADocView())
703     return false;
704 
705   CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
706   if (!pFormFillEnv)
707     return false;
708 
709   CXFA_FFDoc* ffdoc = m_pContext->GetXFADocView()->GetDoc();
710   RetainPtr<IFX_SeekableStream> fileStream = MakeSeekableStream(pFileHandler);
711   if (fileType == FXFA_SAVEAS_XML) {
712     fileStream->WriteString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n");
713     ffdoc->SavePackage(
714         ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Data)),
715         fileStream);
716     return true;
717   }
718 
719   if (fileType != FXFA_SAVEAS_XDP)
720     return true;
721 
722   if (!flag) {
723     flag = FXFA_CONFIG | FXFA_TEMPLATE | FXFA_LOCALESET | FXFA_DATASETS |
724            FXFA_XMPMETA | FXFA_XFDF | FXFA_FORM;
725   }
726   if (!m_pContext->GetPDFDoc()) {
727     fileStream->Flush();
728     return false;
729   }
730 
731   const CPDF_Dictionary* pRoot = m_pContext->GetPDFDoc()->GetRoot();
732   if (!pRoot) {
733     fileStream->Flush();
734     return false;
735   }
736 
737   const CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm");
738   if (!pAcroForm) {
739     fileStream->Flush();
740     return false;
741   }
742 
743   const CPDF_Array* pArray = ToArray(pAcroForm->GetObjectFor("XFA"));
744   if (!pArray) {
745     fileStream->Flush();
746     return false;
747   }
748 
749   for (size_t i = 1; i < pArray->size(); i += 2) {
750     const CPDF_Object* pPDFObj = pArray->GetObjectAt(i);
751     const CPDF_Object* pPrePDFObj = pArray->GetObjectAt(i - 1);
752     if (!pPrePDFObj->IsString())
753       continue;
754     if (!pPDFObj->IsReference())
755       continue;
756 
757     const CPDF_Object* pDirectObj = pPDFObj->GetDirect();
758     if (!pDirectObj->IsStream())
759       continue;
760     ByteString bsType = pPrePDFObj->GetString();
761     if (bsType == "config" && !(flag & FXFA_CONFIG))
762       continue;
763     if (bsType == "template" && !(flag & FXFA_TEMPLATE))
764       continue;
765     if (bsType == "localeSet" && !(flag & FXFA_LOCALESET))
766       continue;
767     if (bsType == "datasets" && !(flag & FXFA_DATASETS))
768       continue;
769     if (bsType == "xmpmeta" && !(flag & FXFA_XMPMETA))
770       continue;
771     if (bsType == "xfdf" && !(flag & FXFA_XFDF))
772       continue;
773     if (bsType == "form" && !(flag & FXFA_FORM))
774       continue;
775 
776     if (bsType == "form") {
777       ffdoc->SavePackage(
778           ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Form)),
779           fileStream);
780     } else if (pPrePDFObj->GetString() == "datasets") {
781       ffdoc->SavePackage(
782           ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Datasets)),
783           fileStream);
784     }
785   }
786   return true;
787 }
788 
ToXFAContentFlags(WideString csSrcContent,FPDF_DWORD & flag)789 void CPDFXFA_DocEnvironment::ToXFAContentFlags(WideString csSrcContent,
790                                                FPDF_DWORD& flag) {
791   if (csSrcContent.Contains(L" config "))
792     flag |= FXFA_CONFIG;
793   if (csSrcContent.Contains(L" template "))
794     flag |= FXFA_TEMPLATE;
795   if (csSrcContent.Contains(L" localeSet "))
796     flag |= FXFA_LOCALESET;
797   if (csSrcContent.Contains(L" datasets "))
798     flag |= FXFA_DATASETS;
799   if (csSrcContent.Contains(L" xmpmeta "))
800     flag |= FXFA_XMPMETA;
801   if (csSrcContent.Contains(L" xfdf "))
802     flag |= FXFA_XFDF;
803   if (csSrcContent.Contains(L" form "))
804     flag |= FXFA_FORM;
805   if (flag == 0) {
806     flag = FXFA_CONFIG | FXFA_TEMPLATE | FXFA_LOCALESET | FXFA_DATASETS |
807            FXFA_XMPMETA | FXFA_XFDF | FXFA_FORM;
808   }
809 }
810 
OnBeforeNotifySubmit()811 bool CPDFXFA_DocEnvironment::OnBeforeNotifySubmit() {
812   if (!m_pContext->ContainsXFAForm())
813     return true;
814 
815   CXFA_FFDocView* docView = m_pContext->GetXFADocView();
816   if (!docView)
817     return true;
818 
819   CXFA_FFWidgetHandler* pWidgetHandler = docView->GetWidgetHandler();
820   if (!pWidgetHandler)
821     return true;
822 
823   auto it = docView->CreateReadyNodeIterator();
824   if (it) {
825     CXFA_EventParam Param;
826     Param.m_eType = XFA_EVENT_PreSubmit;
827     while (CXFA_Node* pNode = it->MoveToNext())
828       pWidgetHandler->ProcessEvent(pNode, &Param);
829   }
830 
831   it = docView->CreateReadyNodeIterator();
832   if (!it)
833     return true;
834 
835   (void)it->MoveToNext();
836   CXFA_Node* pNode = it->MoveToNext();
837 
838   while (pNode) {
839     if (pNode->ProcessValidate(docView, -1) == XFA_EventError::kError) {
840       CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
841       if (!pFormFillEnv)
842         return false;
843 
844       pFormFillEnv->JS_appAlert(WideString::FromDefANSI(IDS_XFA_Validate_Input),
845                                 WideString(), JSPLATFORM_ALERT_BUTTON_OK,
846                                 JSPLATFORM_ALERT_ICON_WARNING);
847       return false;
848     }
849     pNode = it->MoveToNext();
850   }
851 
852   docView->UpdateDocView();
853   return true;
854 }
855 
OnAfterNotifySubmit()856 void CPDFXFA_DocEnvironment::OnAfterNotifySubmit() {
857   if (!m_pContext->ContainsXFAForm())
858     return;
859 
860   if (!m_pContext->GetXFADocView())
861     return;
862 
863   CXFA_FFWidgetHandler* pWidgetHandler =
864       m_pContext->GetXFADocView()->GetWidgetHandler();
865   if (!pWidgetHandler)
866     return;
867 
868   auto it = m_pContext->GetXFADocView()->CreateReadyNodeIterator();
869   if (!it)
870     return;
871 
872   CXFA_EventParam Param;
873   Param.m_eType = XFA_EVENT_PostSubmit;
874   CXFA_Node* pNode = it->MoveToNext();
875   while (pNode) {
876     pWidgetHandler->ProcessEvent(pNode, &Param);
877     pNode = it->MoveToNext();
878   }
879   m_pContext->GetXFADocView()->UpdateDocView();
880 }
881 
NotifySubmit(bool bPrevOrPost)882 bool CPDFXFA_DocEnvironment::NotifySubmit(bool bPrevOrPost) {
883   if (bPrevOrPost)
884     return OnBeforeNotifySubmit();
885 
886   OnAfterNotifySubmit();
887   return true;
888 }
889 
SubmitInternal(CXFA_FFDoc * hDoc,CXFA_Submit * submit)890 bool CPDFXFA_DocEnvironment::SubmitInternal(CXFA_FFDoc* hDoc,
891                                             CXFA_Submit* submit) {
892   CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
893   if (!pFormFillEnv)
894     return false;
895 
896   WideString csURL = submit->GetSubmitTarget();
897   if (csURL.IsEmpty()) {
898     pFormFillEnv->JS_appAlert(WideString::FromDefANSI("Submit cancelled."),
899                               WideString(), JSPLATFORM_ALERT_BUTTON_OK,
900                               JSPLATFORM_ALERT_ICON_ASTERISK);
901     return false;
902   }
903 
904   FPDF_FILEHANDLER* pFileHandler = nullptr;
905   int fileFlag = -1;
906   switch (submit->GetSubmitFormat()) {
907     case XFA_AttributeValue::Xdp: {
908       WideString csContent = submit->GetSubmitXDPContent();
909       csContent.Trim();
910 
911       WideString space = WideString::FromDefANSI(" ");
912       csContent = space + csContent + space;
913       FPDF_DWORD flag = 0;
914       if (submit->IsSubmitEmbedPDF())
915         flag |= FXFA_PDF;
916 
917       ToXFAContentFlags(csContent, flag);
918       pFileHandler = pFormFillEnv->OpenFile(FXFA_SAVEAS_XDP, nullptr, "wb");
919       fileFlag = FXFA_SAVEAS_XDP;
920       ExportSubmitFile(pFileHandler, FXFA_SAVEAS_XDP, 0, flag);
921       break;
922     }
923     case XFA_AttributeValue::Xml:
924       pFileHandler = pFormFillEnv->OpenFile(FXFA_SAVEAS_XML, nullptr, "wb");
925       fileFlag = FXFA_SAVEAS_XML;
926       ExportSubmitFile(pFileHandler, FXFA_SAVEAS_XML, 0, FXFA_XFA_ALL);
927       break;
928     case XFA_AttributeValue::Pdf:
929       break;
930     case XFA_AttributeValue::Urlencoded:
931       pFileHandler = pFormFillEnv->OpenFile(FXFA_SAVEAS_XML, nullptr, "wb");
932       fileFlag = FXFA_SAVEAS_XML;
933       ExportSubmitFile(pFileHandler, FXFA_SAVEAS_XML, 0, FXFA_XFA_ALL);
934       break;
935     default:
936       return false;
937   }
938   if (!pFileHandler)
939     return false;
940 
941   if (csURL.Left(7).CompareNoCase(L"mailto:") == 0) {
942     WideString csToAddress;
943     WideString csCCAddress;
944     WideString csBCCAddress;
945     WideString csSubject;
946     WideString csMsg;
947     if (!MailToInfo(csURL, csToAddress, csCCAddress, csBCCAddress, csSubject,
948                     csMsg)) {
949       return false;
950     }
951     ByteString bsTo = WideString(csToAddress).ToUTF16LE();
952     ByteString bsCC = WideString(csCCAddress).ToUTF16LE();
953     ByteString bsBcc = WideString(csBCCAddress).ToUTF16LE();
954     ByteString bsSubject = WideString(csSubject).ToUTF16LE();
955     ByteString bsMsg = WideString(csMsg).ToUTF16LE();
956     pFormFillEnv->EmailTo(pFileHandler, AsFPDFWideString(&bsTo),
957                           AsFPDFWideString(&bsSubject), AsFPDFWideString(&bsCC),
958                           AsFPDFWideString(&bsBcc), AsFPDFWideString(&bsMsg));
959     return true;
960   }
961 
962   // HTTP or FTP
963   ByteString bs = csURL.ToUTF16LE();
964   pFormFillEnv->UploadTo(pFileHandler, fileFlag, AsFPDFWideString(&bs));
965   return true;
966 }
967 #endif  // PDF_XFA_ELEMENT_SUBMIT_ENABLED
968