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