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