• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(), nullptr, 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 = m_pContext->GetFormFillEnv()->GetPageView(i);
554     if (!pPageView)
555       continue;
556 
557     ObservedPtr<CPDFSDK_Annot> pAnnot(pPageView->GetAnnotByXFAWidget(hWidget));
558     if (pAnnot) {
559       m_pContext->GetFormFillEnv()->SetFocusAnnot(&pAnnot);
560       break;
561     }
562   }
563 }
564 
Print(CXFA_FFDoc * hDoc,int32_t nStartPage,int32_t nEndPage,uint32_t dwOptions)565 void CPDFXFA_DocEnvironment::Print(CXFA_FFDoc* hDoc,
566                                    int32_t nStartPage,
567                                    int32_t nEndPage,
568                                    uint32_t dwOptions) {
569   if (hDoc != m_pContext->GetXFADoc())
570     return;
571 
572   CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
573   if (!pFormFillEnv || !pFormFillEnv->GetFormFillInfo() ||
574       !pFormFillEnv->GetFormFillInfo()->m_pJsPlatform ||
575       !pFormFillEnv->GetFormFillInfo()->m_pJsPlatform->Doc_print) {
576     return;
577   }
578 
579   pFormFillEnv->GetFormFillInfo()->m_pJsPlatform->Doc_print(
580       pFormFillEnv->GetFormFillInfo()->m_pJsPlatform,
581       dwOptions & XFA_PRINTOPT_ShowDialog, nStartPage, nEndPage,
582       dwOptions & XFA_PRINTOPT_CanCancel, dwOptions & XFA_PRINTOPT_ShrinkPage,
583       dwOptions & XFA_PRINTOPT_AsImage, dwOptions & XFA_PRINTOPT_ReverseOrder,
584       dwOptions & XFA_PRINTOPT_PrintAnnot);
585 }
586 
GetHighlightColor(CXFA_FFDoc * hDoc)587 FX_ARGB CPDFXFA_DocEnvironment::GetHighlightColor(CXFA_FFDoc* hDoc) {
588   if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv())
589     return 0;
590 
591   CPDFSDK_InteractiveForm* pForm =
592       m_pContext->GetFormFillEnv()->GetInteractiveForm();
593   return AlphaAndColorRefToArgb(pForm->GetHighlightAlpha(),
594                                 pForm->GetHighlightColor(FormFieldType::kXFA));
595 }
596 
GetIJSRuntime(CXFA_FFDoc * hDoc) const597 IJS_Runtime* CPDFXFA_DocEnvironment::GetIJSRuntime(CXFA_FFDoc* hDoc) const {
598   if (hDoc != m_pContext->GetXFADoc())
599     return nullptr;
600 
601   CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
602   return pFormFillEnv ? pFormFillEnv->GetIJSRuntime() : nullptr;
603 }
604 
OpenLinkedFile(CXFA_FFDoc * hDoc,const WideString & wsLink)605 RetainPtr<IFX_SeekableReadStream> CPDFXFA_DocEnvironment::OpenLinkedFile(
606     CXFA_FFDoc* hDoc,
607     const WideString& wsLink) {
608   CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
609   if (!pFormFillEnv)
610     return nullptr;
611 
612   ByteString bs = wsLink.ToUTF16LE();
613   FPDF_FILEHANDLER* pFileHandler =
614       pFormFillEnv->OpenFile(0, AsFPDFWideString(&bs), "rb");
615   if (!pFileHandler)
616     return nullptr;
617 
618   return MakeSeekableStream(pFileHandler);
619 }
620 
621 #ifdef PDF_XFA_ELEMENT_SUBMIT_ENABLED
Submit(CXFA_FFDoc * hDoc,CXFA_Submit * submit)622 bool CPDFXFA_DocEnvironment::Submit(CXFA_FFDoc* hDoc, CXFA_Submit* submit) {
623   if (!NotifySubmit(true) || !m_pContext->GetXFADocView())
624     return false;
625 
626   m_pContext->GetXFADocView()->UpdateDocView();
627   bool ret = SubmitInternal(hDoc, submit);
628   NotifySubmit(false);
629   return ret;
630 }
631 
MailToInfo(WideString & csURL,WideString & csToAddress,WideString & csCCAddress,WideString & csBCCAddress,WideString & csSubject,WideString & csMsg)632 bool CPDFXFA_DocEnvironment::MailToInfo(WideString& csURL,
633                                         WideString& csToAddress,
634                                         WideString& csCCAddress,
635                                         WideString& csBCCAddress,
636                                         WideString& csSubject,
637                                         WideString& csMsg) {
638   WideString srcURL = csURL;
639   srcURL.TrimLeft();
640   if (srcURL.Left(7).CompareNoCase(L"mailto:") != 0)
641     return false;
642 
643   auto pos = srcURL.Find(L'?');
644 
645   {
646     WideString tmp;
647     if (!pos.has_value()) {
648       pos = srcURL.Find(L'@');
649       if (!pos.has_value())
650         return false;
651 
652       tmp = srcURL.Right(csURL.GetLength() - 7);
653     } else {
654       tmp = srcURL.Left(pos.value());
655       tmp = tmp.Right(tmp.GetLength() - 7);
656     }
657     tmp.Trim();
658     csToAddress = std::move(tmp);
659   }
660 
661   srcURL = srcURL.Right(srcURL.GetLength() - (pos.value() + 1));
662   while (!srcURL.IsEmpty()) {
663     srcURL.Trim();
664     pos = srcURL.Find(L'&');
665     WideString tmp = (!pos.has_value()) ? srcURL : srcURL.Left(pos.value());
666     tmp.Trim();
667     if (tmp.GetLength() >= 3 && tmp.Left(3).CompareNoCase(L"cc=") == 0) {
668       tmp = tmp.Right(tmp.GetLength() - 3);
669       if (!csCCAddress.IsEmpty())
670         csCCAddress += L';';
671       csCCAddress += tmp;
672     } else if (tmp.GetLength() >= 4 &&
673                tmp.Left(4).CompareNoCase(L"bcc=") == 0) {
674       tmp = tmp.Right(tmp.GetLength() - 4);
675       if (!csBCCAddress.IsEmpty())
676         csBCCAddress += L';';
677       csBCCAddress += tmp;
678     } else if (tmp.GetLength() >= 8 &&
679                tmp.Left(8).CompareNoCase(L"subject=") == 0) {
680       tmp = tmp.Right(tmp.GetLength() - 8);
681       csSubject += tmp;
682     } else if (tmp.GetLength() >= 5 &&
683                tmp.Left(5).CompareNoCase(L"body=") == 0) {
684       tmp = tmp.Right(tmp.GetLength() - 5);
685       csMsg += tmp;
686     }
687     srcURL = pos.has_value()
688                  ? srcURL.Right(csURL.GetLength() - (pos.value() + 1))
689                  : WideString();
690   }
691   csToAddress.Replace(L",", L";");
692   csCCAddress.Replace(L",", L";");
693   csBCCAddress.Replace(L",", L";");
694   return true;
695 }
696 
ExportSubmitFile(FPDF_FILEHANDLER * pFileHandler,int fileType,FPDF_DWORD encodeType,FPDF_DWORD flag)697 bool CPDFXFA_DocEnvironment::ExportSubmitFile(FPDF_FILEHANDLER* pFileHandler,
698                                               int fileType,
699                                               FPDF_DWORD encodeType,
700                                               FPDF_DWORD flag) {
701   if (!m_pContext->GetXFADocView())
702     return false;
703 
704   CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
705   if (!pFormFillEnv)
706     return false;
707 
708   CXFA_FFDoc* ffdoc = m_pContext->GetXFADocView()->GetDoc();
709   RetainPtr<IFX_SeekableStream> fileStream = MakeSeekableStream(pFileHandler);
710   if (fileType == FXFA_SAVEAS_XML) {
711     fileStream->WriteString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n");
712     ffdoc->SavePackage(
713         ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Data)),
714         fileStream);
715     return true;
716   }
717 
718   if (fileType != FXFA_SAVEAS_XDP)
719     return true;
720 
721   if (!flag) {
722     flag = FXFA_CONFIG | FXFA_TEMPLATE | FXFA_LOCALESET | FXFA_DATASETS |
723            FXFA_XMPMETA | FXFA_XFDF | FXFA_FORM;
724   }
725   if (!m_pContext->GetPDFDoc()) {
726     fileStream->Flush();
727     return false;
728   }
729 
730   const CPDF_Dictionary* pRoot = m_pContext->GetPDFDoc()->GetRoot();
731   if (!pRoot) {
732     fileStream->Flush();
733     return false;
734   }
735 
736   const CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm");
737   if (!pAcroForm) {
738     fileStream->Flush();
739     return false;
740   }
741 
742   const CPDF_Array* pArray = ToArray(pAcroForm->GetObjectFor("XFA"));
743   if (!pArray) {
744     fileStream->Flush();
745     return false;
746   }
747 
748   for (size_t i = 1; i < pArray->size(); i += 2) {
749     const CPDF_Object* pPDFObj = pArray->GetObjectAt(i);
750     const CPDF_Object* pPrePDFObj = pArray->GetObjectAt(i - 1);
751     if (!pPrePDFObj->IsString())
752       continue;
753     if (!pPDFObj->IsReference())
754       continue;
755 
756     const CPDF_Object* pDirectObj = pPDFObj->GetDirect();
757     if (!pDirectObj->IsStream())
758       continue;
759     ByteString bsType = pPrePDFObj->GetString();
760     if (bsType == "config" && !(flag & FXFA_CONFIG))
761       continue;
762     if (bsType == "template" && !(flag & FXFA_TEMPLATE))
763       continue;
764     if (bsType == "localeSet" && !(flag & FXFA_LOCALESET))
765       continue;
766     if (bsType == "datasets" && !(flag & FXFA_DATASETS))
767       continue;
768     if (bsType == "xmpmeta" && !(flag & FXFA_XMPMETA))
769       continue;
770     if (bsType == "xfdf" && !(flag & FXFA_XFDF))
771       continue;
772     if (bsType == "form" && !(flag & FXFA_FORM))
773       continue;
774 
775     if (bsType == "form") {
776       ffdoc->SavePackage(
777           ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Form)),
778           fileStream);
779     } else if (pPrePDFObj->GetString() == "datasets") {
780       ffdoc->SavePackage(
781           ToNode(ffdoc->GetXFADoc()->GetXFAObject(XFA_HASHCODE_Datasets)),
782           fileStream);
783     }
784   }
785   return true;
786 }
787 
ToXFAContentFlags(WideString csSrcContent,FPDF_DWORD & flag)788 void CPDFXFA_DocEnvironment::ToXFAContentFlags(WideString csSrcContent,
789                                                FPDF_DWORD& flag) {
790   if (csSrcContent.Contains(L" config "))
791     flag |= FXFA_CONFIG;
792   if (csSrcContent.Contains(L" template "))
793     flag |= FXFA_TEMPLATE;
794   if (csSrcContent.Contains(L" localeSet "))
795     flag |= FXFA_LOCALESET;
796   if (csSrcContent.Contains(L" datasets "))
797     flag |= FXFA_DATASETS;
798   if (csSrcContent.Contains(L" xmpmeta "))
799     flag |= FXFA_XMPMETA;
800   if (csSrcContent.Contains(L" xfdf "))
801     flag |= FXFA_XFDF;
802   if (csSrcContent.Contains(L" form "))
803     flag |= FXFA_FORM;
804   if (flag == 0) {
805     flag = FXFA_CONFIG | FXFA_TEMPLATE | FXFA_LOCALESET | FXFA_DATASETS |
806            FXFA_XMPMETA | FXFA_XFDF | FXFA_FORM;
807   }
808 }
809 
OnBeforeNotifySubmit()810 bool CPDFXFA_DocEnvironment::OnBeforeNotifySubmit() {
811   if (!m_pContext->ContainsXFAForm())
812     return true;
813 
814   CXFA_FFDocView* docView = m_pContext->GetXFADocView();
815   if (!docView)
816     return true;
817 
818   CXFA_FFWidgetHandler* pWidgetHandler = docView->GetWidgetHandler();
819   if (!pWidgetHandler)
820     return true;
821 
822   auto it = docView->CreateReadyNodeIterator();
823   if (it) {
824     CXFA_EventParam Param;
825     Param.m_eType = XFA_EVENT_PreSubmit;
826     while (CXFA_Node* pNode = it->MoveToNext())
827       pWidgetHandler->ProcessEvent(pNode, &Param);
828   }
829 
830   it = docView->CreateReadyNodeIterator();
831   if (!it)
832     return true;
833 
834   (void)it->MoveToNext();
835   CXFA_Node* pNode = it->MoveToNext();
836 
837   while (pNode) {
838     if (pNode->ProcessValidate(docView, -1) == XFA_EventError::kError) {
839       CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
840       if (!pFormFillEnv)
841         return false;
842 
843       pFormFillEnv->JS_appAlert(WideString::FromDefANSI(IDS_XFA_Validate_Input),
844                                 WideString(), JSPLATFORM_ALERT_BUTTON_OK,
845                                 JSPLATFORM_ALERT_ICON_WARNING);
846       return false;
847     }
848     pNode = it->MoveToNext();
849   }
850 
851   docView->UpdateDocView();
852   return true;
853 }
854 
OnAfterNotifySubmit()855 void CPDFXFA_DocEnvironment::OnAfterNotifySubmit() {
856   if (!m_pContext->ContainsXFAForm())
857     return;
858 
859   if (!m_pContext->GetXFADocView())
860     return;
861 
862   CXFA_FFWidgetHandler* pWidgetHandler =
863       m_pContext->GetXFADocView()->GetWidgetHandler();
864   if (!pWidgetHandler)
865     return;
866 
867   auto it = m_pContext->GetXFADocView()->CreateReadyNodeIterator();
868   if (!it)
869     return;
870 
871   CXFA_EventParam Param;
872   Param.m_eType = XFA_EVENT_PostSubmit;
873   CXFA_Node* pNode = it->MoveToNext();
874   while (pNode) {
875     pWidgetHandler->ProcessEvent(pNode, &Param);
876     pNode = it->MoveToNext();
877   }
878   m_pContext->GetXFADocView()->UpdateDocView();
879 }
880 
NotifySubmit(bool bPrevOrPost)881 bool CPDFXFA_DocEnvironment::NotifySubmit(bool bPrevOrPost) {
882   if (bPrevOrPost)
883     return OnBeforeNotifySubmit();
884 
885   OnAfterNotifySubmit();
886   return true;
887 }
888 
SubmitInternal(CXFA_FFDoc * hDoc,CXFA_Submit * submit)889 bool CPDFXFA_DocEnvironment::SubmitInternal(CXFA_FFDoc* hDoc,
890                                             CXFA_Submit* submit) {
891   CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv();
892   if (!pFormFillEnv)
893     return false;
894 
895   WideString csURL = submit->GetSubmitTarget();
896   if (csURL.IsEmpty()) {
897     pFormFillEnv->JS_appAlert(WideString::FromDefANSI("Submit cancelled."),
898                               WideString(), JSPLATFORM_ALERT_BUTTON_OK,
899                               JSPLATFORM_ALERT_ICON_ASTERISK);
900     return false;
901   }
902 
903   FPDF_FILEHANDLER* pFileHandler = nullptr;
904   int fileFlag = -1;
905   switch (submit->GetSubmitFormat()) {
906     case XFA_AttributeValue::Xdp: {
907       WideString csContent = submit->GetSubmitXDPContent();
908       csContent.Trim();
909 
910       WideString space = WideString::FromDefANSI(" ");
911       csContent = space + csContent + space;
912       FPDF_DWORD flag = 0;
913       if (submit->IsSubmitEmbedPDF())
914         flag |= FXFA_PDF;
915 
916       ToXFAContentFlags(csContent, flag);
917       pFileHandler = pFormFillEnv->OpenFile(FXFA_SAVEAS_XDP, nullptr, "wb");
918       fileFlag = FXFA_SAVEAS_XDP;
919       ExportSubmitFile(pFileHandler, FXFA_SAVEAS_XDP, 0, flag);
920       break;
921     }
922     case XFA_AttributeValue::Xml:
923       pFileHandler = pFormFillEnv->OpenFile(FXFA_SAVEAS_XML, nullptr, "wb");
924       fileFlag = FXFA_SAVEAS_XML;
925       ExportSubmitFile(pFileHandler, FXFA_SAVEAS_XML, 0, FXFA_XFA_ALL);
926       break;
927     case XFA_AttributeValue::Pdf:
928       break;
929     case XFA_AttributeValue::Urlencoded:
930       pFileHandler = pFormFillEnv->OpenFile(FXFA_SAVEAS_XML, nullptr, "wb");
931       fileFlag = FXFA_SAVEAS_XML;
932       ExportSubmitFile(pFileHandler, FXFA_SAVEAS_XML, 0, FXFA_XFA_ALL);
933       break;
934     default:
935       return false;
936   }
937   if (!pFileHandler)
938     return false;
939 
940   if (csURL.Left(7).CompareNoCase(L"mailto:") == 0) {
941     WideString csToAddress;
942     WideString csCCAddress;
943     WideString csBCCAddress;
944     WideString csSubject;
945     WideString csMsg;
946     if (!MailToInfo(csURL, csToAddress, csCCAddress, csBCCAddress, csSubject,
947                     csMsg)) {
948       return false;
949     }
950     ByteString bsTo = WideString(csToAddress).ToUTF16LE();
951     ByteString bsCC = WideString(csCCAddress).ToUTF16LE();
952     ByteString bsBcc = WideString(csBCCAddress).ToUTF16LE();
953     ByteString bsSubject = WideString(csSubject).ToUTF16LE();
954     ByteString bsMsg = WideString(csMsg).ToUTF16LE();
955     pFormFillEnv->EmailTo(pFileHandler, AsFPDFWideString(&bsTo),
956                           AsFPDFWideString(&bsSubject), AsFPDFWideString(&bsCC),
957                           AsFPDFWideString(&bsBcc), AsFPDFWideString(&bsMsg));
958     return true;
959   }
960 
961   // HTTP or FTP
962   ByteString bs = csURL.ToUTF16LE();
963   pFormFillEnv->UploadTo(pFileHandler, fileFlag, AsFPDFWideString(&bs));
964   return true;
965 }
966 #endif  // PDF_XFA_ELEMENT_SUBMIT_ENABLED
967