xref: /aosp_15_r20/external/pdfium/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
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/retain_ptr.h"
17 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
18 #include "fpdfsdk/cpdfsdk_helpers.h"
19 #include "fpdfsdk/cpdfsdk_interactiveform.h"
20 #include "fpdfsdk/cpdfsdk_pageview.h"
21 #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
22 #include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
23 #include "third_party/base/check.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(
507             reinterpret_cast<const unsigned short*>(bs.c_str()),
508             bs.GetLength() / sizeof(unsigned short));
509         ByteString bPath = wPath.ToUTF8();
510         static const char kFormat[] =
511             "\n<pdf href=\"%s\" xmlns=\"http://ns.adobe.com/xdp/pdf/\"/>";
512         ByteString content = ByteString::Format(kFormat, bPath.c_str());
513         fileWrite->WriteString(content.AsStringView());
514       }
515       auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(std::move(pStream));
516       pAcc->LoadAllDataFiltered();
517       fileWrite->WriteBlock(pAcc->GetSpan());
518     }
519   }
520   fileWrite->Flush();
521 }
522 
GotoURL(CXFA_FFDoc * hDoc,const WideString & wsURL)523 void CPDFXFA_DocEnvironment::GotoURL(CXFA_FFDoc* hDoc,
524                                      const WideString& wsURL) {
525   if (hDoc != m_pContext->GetXFADoc())
526     return;
527 
528   if (m_pContext->GetFormType() != FormType::kXFAFull)
529     return;
530 
531   CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
532   if (!pFormFillEnv)
533     return;
534 
535   pFormFillEnv->GotoURL(wsURL);
536 }
537 
IsValidationsEnabled(const CXFA_FFDoc * hDoc) const538 bool CPDFXFA_DocEnvironment::IsValidationsEnabled(
539     const CXFA_FFDoc* hDoc) const {
540   if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv())
541     return false;
542 
543   auto* pForm = m_pContext->GetFormFillEnv()->GetInteractiveForm();
544   return pForm->IsXfaValidationsEnabled();
545 }
546 
SetValidationsEnabled(CXFA_FFDoc * hDoc,bool bEnabled)547 void CPDFXFA_DocEnvironment::SetValidationsEnabled(CXFA_FFDoc* hDoc,
548                                                    bool bEnabled) {
549   if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv())
550     return;
551 
552   m_pContext->GetFormFillEnv()->GetInteractiveForm()->XfaSetValidationsEnabled(
553       bEnabled);
554 }
555 
SetFocusWidget(CXFA_FFDoc * hDoc,CXFA_FFWidget * hWidget)556 void CPDFXFA_DocEnvironment::SetFocusWidget(CXFA_FFDoc* hDoc,
557                                             CXFA_FFWidget* hWidget) {
558   if (hDoc != m_pContext->GetXFADoc())
559     return;
560 
561   if (!hWidget) {
562     ObservedPtr<CPDFSDK_Annot> pNull;
563     m_pContext->GetFormFillEnv()->SetFocusAnnot(pNull);
564     return;
565   }
566 
567   int pageViewCount = m_pContext->GetFormFillEnv()->GetPageViewCount();
568   for (int i = 0; i < pageViewCount; i++) {
569     CPDFSDK_PageView* pPageView =
570         m_pContext->GetFormFillEnv()->GetPageViewAtIndex(i);
571     if (!pPageView)
572       continue;
573 
574     ObservedPtr<CPDFSDK_Annot> pAnnot(pPageView->GetAnnotForFFWidget(hWidget));
575     if (pAnnot) {
576       m_pContext->GetFormFillEnv()->SetFocusAnnot(pAnnot);
577       break;
578     }
579   }
580 }
581 
Print(CXFA_FFDoc * hDoc,int32_t nStartPage,int32_t nEndPage,Mask<XFA_PrintOpt> dwOptions)582 void CPDFXFA_DocEnvironment::Print(CXFA_FFDoc* hDoc,
583                                    int32_t nStartPage,
584                                    int32_t nEndPage,
585                                    Mask<XFA_PrintOpt> dwOptions) {
586   if (hDoc != m_pContext->GetXFADoc())
587     return;
588 
589   CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
590   if (!pFormFillEnv || !pFormFillEnv->GetFormFillInfo() ||
591       !pFormFillEnv->GetFormFillInfo()->m_pJsPlatform ||
592       !pFormFillEnv->GetFormFillInfo()->m_pJsPlatform->Doc_print) {
593     return;
594   }
595 
596   pFormFillEnv->GetFormFillInfo()->m_pJsPlatform->Doc_print(
597       pFormFillEnv->GetFormFillInfo()->m_pJsPlatform,
598       !!(dwOptions & XFA_PrintOpt::kShowDialog), nStartPage, nEndPage,
599       !!(dwOptions & XFA_PrintOpt::kCanCancel),
600       !!(dwOptions & XFA_PrintOpt::kShrinkPage),
601       !!(dwOptions & XFA_PrintOpt::kAsImage),
602       !!(dwOptions & XFA_PrintOpt::kReverseOrder),
603       !!(dwOptions & XFA_PrintOpt::kPrintAnnot));
604 }
605 
GetHighlightColor(const CXFA_FFDoc * hDoc) const606 FX_ARGB CPDFXFA_DocEnvironment::GetHighlightColor(
607     const CXFA_FFDoc* hDoc) const {
608   if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv())
609     return 0;
610 
611   CPDFSDK_InteractiveForm* pForm =
612       m_pContext->GetFormFillEnv()->GetInteractiveForm();
613   return AlphaAndColorRefToArgb(pForm->GetHighlightAlpha(),
614                                 pForm->GetHighlightColor(FormFieldType::kXFA));
615 }
616 
GetIJSRuntime(const CXFA_FFDoc * hDoc) const617 IJS_Runtime* CPDFXFA_DocEnvironment::GetIJSRuntime(
618     const CXFA_FFDoc* hDoc) const {
619   if (hDoc != m_pContext->GetXFADoc())
620     return nullptr;
621 
622   CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
623   return pFormFillEnv ? pFormFillEnv->GetIJSRuntime() : nullptr;
624 }
625 
GetXMLDoc() const626 CFX_XMLDocument* CPDFXFA_DocEnvironment::GetXMLDoc() const {
627   return m_pContext->GetXMLDoc();
628 }
629 
OpenLinkedFile(CXFA_FFDoc * hDoc,const WideString & wsLink)630 RetainPtr<IFX_SeekableReadStream> CPDFXFA_DocEnvironment::OpenLinkedFile(
631     CXFA_FFDoc* hDoc,
632     const WideString& wsLink) {
633   CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
634   if (!pFormFillEnv)
635     return nullptr;
636 
637   ByteString bs = wsLink.ToUTF16LE();
638   FPDF_FILEHANDLER* pFileHandler =
639       pFormFillEnv->OpenFile(0, AsFPDFWideString(&bs), "rb");
640   if (!pFileHandler)
641     return nullptr;
642 
643   return MakeSeekableStream(pFileHandler);
644 }
645 
646 #ifdef PDF_XFA_ELEMENT_SUBMIT_ENABLED
Submit(CXFA_FFDoc * hDoc,CXFA_Submit * submit)647 bool CPDFXFA_DocEnvironment::Submit(CXFA_FFDoc* hDoc, CXFA_Submit* submit) {
648   if (!OnBeforeNotifySubmit() || !m_pContext->GetXFADocView())
649     return false;
650 
651   m_pContext->GetXFADocView()->UpdateDocView();
652   bool ret = SubmitInternal(hDoc, submit);
653   OnAfterNotifySubmit();
654   return ret;
655 }
656 
MailToInfo(WideString & csURL,WideString & csToAddress,WideString & csCCAddress,WideString & csBCCAddress,WideString & csSubject,WideString & csMsg)657 bool CPDFXFA_DocEnvironment::MailToInfo(WideString& csURL,
658                                         WideString& csToAddress,
659                                         WideString& csCCAddress,
660                                         WideString& csBCCAddress,
661                                         WideString& csSubject,
662                                         WideString& csMsg) {
663   WideString srcURL = csURL;
664   srcURL.TrimLeft();
665   if (srcURL.Left(7).CompareNoCase(L"mailto:") != 0)
666     return false;
667 
668   auto pos = srcURL.Find(L'?');
669 
670   {
671     WideString tmp;
672     if (!pos.has_value()) {
673       pos = srcURL.Find(L'@');
674       if (!pos.has_value())
675         return false;
676 
677       tmp = srcURL.Right(csURL.GetLength() - 7);
678     } else {
679       tmp = srcURL.Left(pos.value());
680       tmp = tmp.Right(tmp.GetLength() - 7);
681     }
682     tmp.Trim();
683     csToAddress = std::move(tmp);
684   }
685 
686   srcURL = srcURL.Right(srcURL.GetLength() - (pos.value() + 1));
687   while (!srcURL.IsEmpty()) {
688     srcURL.Trim();
689     pos = srcURL.Find(L'&');
690     WideString tmp = (!pos.has_value()) ? srcURL : srcURL.Left(pos.value());
691     tmp.Trim();
692     if (tmp.GetLength() >= 3 && tmp.Left(3).CompareNoCase(L"cc=") == 0) {
693       tmp = tmp.Right(tmp.GetLength() - 3);
694       if (!csCCAddress.IsEmpty())
695         csCCAddress += L';';
696       csCCAddress += tmp;
697     } else if (tmp.GetLength() >= 4 &&
698                tmp.Left(4).CompareNoCase(L"bcc=") == 0) {
699       tmp = tmp.Right(tmp.GetLength() - 4);
700       if (!csBCCAddress.IsEmpty())
701         csBCCAddress += L';';
702       csBCCAddress += tmp;
703     } else if (tmp.GetLength() >= 8 &&
704                tmp.Left(8).CompareNoCase(L"subject=") == 0) {
705       tmp = tmp.Right(tmp.GetLength() - 8);
706       csSubject += tmp;
707     } else if (tmp.GetLength() >= 5 &&
708                tmp.Left(5).CompareNoCase(L"body=") == 0) {
709       tmp = tmp.Right(tmp.GetLength() - 5);
710       csMsg += tmp;
711     }
712     srcURL = pos.has_value()
713                  ? srcURL.Right(csURL.GetLength() - (pos.value() + 1))
714                  : WideString();
715   }
716   csToAddress.Replace(L",", L";");
717   csCCAddress.Replace(L",", L";");
718   csBCCAddress.Replace(L",", L";");
719   return true;
720 }
721 
ExportSubmitFile(FPDF_FILEHANDLER * pFileHandler,int fileType,FPDF_DWORD encodeType,FPDF_DWORD flag)722 bool CPDFXFA_DocEnvironment::ExportSubmitFile(FPDF_FILEHANDLER* pFileHandler,
723                                               int fileType,
724                                               FPDF_DWORD encodeType,
725                                               FPDF_DWORD flag) {
726   if (!m_pContext->GetXFADocView())
727     return false;
728 
729   CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
730   if (!pFormFillEnv)
731     return false;
732 
733   CXFA_FFDoc* ffdoc = m_pContext->GetXFADocView()->GetDoc();
734   RetainPtr<IFX_SeekableStream> fileStream = MakeSeekableStream(pFileHandler);
735   if (fileType == FXFA_SAVEAS_XML) {
736     fileStream->WriteString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n");
737     ffdoc->SavePackage(
738         ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Data)),
739         fileStream);
740     return true;
741   }
742 
743   if (fileType != FXFA_SAVEAS_XDP)
744     return true;
745 
746   if (!flag) {
747     flag = FXFA_CONFIG | FXFA_TEMPLATE | FXFA_LOCALESET | FXFA_DATASETS |
748            FXFA_XMPMETA | FXFA_XFDF | FXFA_FORM;
749   }
750   if (!m_pContext->GetPDFDoc()) {
751     fileStream->Flush();
752     return false;
753   }
754 
755   const CPDF_Dictionary* pRoot = m_pContext->GetPDFDoc()->GetRoot();
756   if (!pRoot) {
757     fileStream->Flush();
758     return false;
759   }
760 
761   RetainPtr<const CPDF_Dictionary> pAcroForm = pRoot->GetDictFor("AcroForm");
762   if (!pAcroForm) {
763     fileStream->Flush();
764     return false;
765   }
766 
767   RetainPtr<const CPDF_Array> pArray = ToArray(pAcroForm->GetObjectFor("XFA"));
768   if (!pArray) {
769     fileStream->Flush();
770     return false;
771   }
772 
773   for (size_t i = 1; i < pArray->size(); i += 2) {
774     RetainPtr<const CPDF_Object> pPDFObj = pArray->GetObjectAt(i);
775     RetainPtr<const CPDF_Object> pPrePDFObj = pArray->GetObjectAt(i - 1);
776     if (!pPrePDFObj->IsString())
777       continue;
778     if (!pPDFObj->IsReference())
779       continue;
780 
781     RetainPtr<const CPDF_Object> pDirectObj = pPDFObj->GetDirect();
782     if (!pDirectObj->IsStream())
783       continue;
784     ByteString bsType = pPrePDFObj->GetString();
785     if (bsType == "config" && !(flag & FXFA_CONFIG))
786       continue;
787     if (bsType == "template" && !(flag & FXFA_TEMPLATE))
788       continue;
789     if (bsType == "localeSet" && !(flag & FXFA_LOCALESET))
790       continue;
791     if (bsType == "datasets" && !(flag & FXFA_DATASETS))
792       continue;
793     if (bsType == "xmpmeta" && !(flag & FXFA_XMPMETA))
794       continue;
795     if (bsType == "xfdf" && !(flag & FXFA_XFDF))
796       continue;
797     if (bsType == "form" && !(flag & FXFA_FORM))
798       continue;
799 
800     if (bsType == "form") {
801       ffdoc->SavePackage(
802           ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Form)),
803           fileStream);
804     } else if (pPrePDFObj->GetString() == "datasets") {
805       ffdoc->SavePackage(
806           ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Datasets)),
807           fileStream);
808     }
809   }
810   return true;
811 }
812 
ToXFAContentFlags(WideString csSrcContent,FPDF_DWORD & flag)813 void CPDFXFA_DocEnvironment::ToXFAContentFlags(WideString csSrcContent,
814                                                FPDF_DWORD& flag) {
815   if (csSrcContent.Contains(L" config "))
816     flag |= FXFA_CONFIG;
817   if (csSrcContent.Contains(L" template "))
818     flag |= FXFA_TEMPLATE;
819   if (csSrcContent.Contains(L" localeSet "))
820     flag |= FXFA_LOCALESET;
821   if (csSrcContent.Contains(L" datasets "))
822     flag |= FXFA_DATASETS;
823   if (csSrcContent.Contains(L" xmpmeta "))
824     flag |= FXFA_XMPMETA;
825   if (csSrcContent.Contains(L" xfdf "))
826     flag |= FXFA_XFDF;
827   if (csSrcContent.Contains(L" form "))
828     flag |= FXFA_FORM;
829   if (flag == 0) {
830     flag = FXFA_CONFIG | FXFA_TEMPLATE | FXFA_LOCALESET | FXFA_DATASETS |
831            FXFA_XMPMETA | FXFA_XFDF | FXFA_FORM;
832   }
833 }
834 
OnBeforeNotifySubmit()835 bool CPDFXFA_DocEnvironment::OnBeforeNotifySubmit() {
836   if (!m_pContext->ContainsXFAForm())
837     return true;
838 
839   CXFA_FFDocView* docView = m_pContext->GetXFADocView();
840   if (!docView)
841     return true;
842 
843   CXFA_FFWidgetHandler* pWidgetHandler = docView->GetWidgetHandler();
844   if (!pWidgetHandler)
845     return true;
846 
847   auto it = docView->CreateReadyNodeIterator();
848   if (it) {
849     CXFA_EventParam Param;
850     Param.m_eType = XFA_EVENT_PreSubmit;
851     while (CXFA_Node* pNode = it->MoveToNext())
852       pWidgetHandler->ProcessEvent(pNode, &Param);
853   }
854 
855   it = docView->CreateReadyNodeIterator();
856   if (!it)
857     return true;
858 
859   (void)it->MoveToNext();
860   CXFA_Node* pNode = it->MoveToNext();
861 
862   while (pNode) {
863     if (pNode->ProcessValidate(docView, -1) == XFA_EventError::kError) {
864       CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
865       if (!pFormFillEnv)
866         return false;
867 
868       pFormFillEnv->JS_appAlert(WideString::FromDefANSI(IDS_XFA_Validate_Input),
869                                 WideString(), JSPLATFORM_ALERT_BUTTON_OK,
870                                 JSPLATFORM_ALERT_ICON_WARNING);
871       return false;
872     }
873     pNode = it->MoveToNext();
874   }
875 
876   docView->UpdateDocView();
877   return true;
878 }
879 
OnAfterNotifySubmit()880 void CPDFXFA_DocEnvironment::OnAfterNotifySubmit() {
881   if (!m_pContext->ContainsXFAForm())
882     return;
883 
884   if (!m_pContext->GetXFADocView())
885     return;
886 
887   CXFA_FFWidgetHandler* pWidgetHandler =
888       m_pContext->GetXFADocView()->GetWidgetHandler();
889   if (!pWidgetHandler)
890     return;
891 
892   auto it = m_pContext->GetXFADocView()->CreateReadyNodeIterator();
893   if (!it)
894     return;
895 
896   CXFA_EventParam Param;
897   Param.m_eType = XFA_EVENT_PostSubmit;
898   CXFA_Node* pNode = it->MoveToNext();
899   while (pNode) {
900     pWidgetHandler->ProcessEvent(pNode, &Param);
901     pNode = it->MoveToNext();
902   }
903   m_pContext->GetXFADocView()->UpdateDocView();
904 }
905 
SubmitInternal(CXFA_FFDoc * hDoc,CXFA_Submit * submit)906 bool CPDFXFA_DocEnvironment::SubmitInternal(CXFA_FFDoc* hDoc,
907                                             CXFA_Submit* submit) {
908   CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
909   if (!pFormFillEnv)
910     return false;
911 
912   WideString csURL = submit->GetSubmitTarget();
913   if (csURL.IsEmpty()) {
914     pFormFillEnv->JS_appAlert(WideString::FromDefANSI("Submit cancelled."),
915                               WideString(), JSPLATFORM_ALERT_BUTTON_OK,
916                               JSPLATFORM_ALERT_ICON_ASTERISK);
917     return false;
918   }
919 
920   FPDF_FILEHANDLER* pFileHandler = nullptr;
921   int fileFlag = -1;
922   switch (submit->GetSubmitFormat()) {
923     case XFA_AttributeValue::Xdp: {
924       WideString csContent = submit->GetSubmitXDPContent();
925       csContent.Trim();
926 
927       WideString space = WideString::FromDefANSI(" ");
928       csContent = space + csContent + space;
929       FPDF_DWORD flag = 0;
930       if (submit->IsSubmitEmbedPDF())
931         flag |= FXFA_PDF;
932 
933       ToXFAContentFlags(csContent, flag);
934       pFileHandler = pFormFillEnv->OpenFile(FXFA_SAVEAS_XDP, nullptr, "wb");
935       fileFlag = FXFA_SAVEAS_XDP;
936       ExportSubmitFile(pFileHandler, FXFA_SAVEAS_XDP, 0, flag);
937       break;
938     }
939     case XFA_AttributeValue::Xml:
940       pFileHandler = pFormFillEnv->OpenFile(FXFA_SAVEAS_XML, nullptr, "wb");
941       fileFlag = FXFA_SAVEAS_XML;
942       ExportSubmitFile(pFileHandler, FXFA_SAVEAS_XML, 0, FXFA_XFA_ALL);
943       break;
944     case XFA_AttributeValue::Pdf:
945       break;
946     case XFA_AttributeValue::Urlencoded:
947       pFileHandler = pFormFillEnv->OpenFile(FXFA_SAVEAS_XML, nullptr, "wb");
948       fileFlag = FXFA_SAVEAS_XML;
949       ExportSubmitFile(pFileHandler, FXFA_SAVEAS_XML, 0, FXFA_XFA_ALL);
950       break;
951     default:
952       return false;
953   }
954   if (!pFileHandler)
955     return false;
956 
957   if (csURL.Left(7).CompareNoCase(L"mailto:") == 0) {
958     WideString csToAddress;
959     WideString csCCAddress;
960     WideString csBCCAddress;
961     WideString csSubject;
962     WideString csMsg;
963     if (!MailToInfo(csURL, csToAddress, csCCAddress, csBCCAddress, csSubject,
964                     csMsg)) {
965       return false;
966     }
967     ByteString bsTo = WideString(csToAddress).ToUTF16LE();
968     ByteString bsCC = WideString(csCCAddress).ToUTF16LE();
969     ByteString bsBcc = WideString(csBCCAddress).ToUTF16LE();
970     ByteString bsSubject = WideString(csSubject).ToUTF16LE();
971     ByteString bsMsg = WideString(csMsg).ToUTF16LE();
972     pFormFillEnv->EmailTo(pFileHandler, AsFPDFWideString(&bsTo),
973                           AsFPDFWideString(&bsSubject), AsFPDFWideString(&bsCC),
974                           AsFPDFWideString(&bsBcc), AsFPDFWideString(&bsMsg));
975     return true;
976   }
977 
978   // HTTP or FTP
979   ByteString bs = csURL.ToUTF16LE();
980   pFormFillEnv->UploadTo(pFileHandler, fileFlag, AsFPDFWideString(&bs));
981   return true;
982 }
983 #endif  // PDF_XFA_ELEMENT_SUBMIT_ENABLED
984