1 // Copyright 2014 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 "xfa/fxfa/cxfa_ffnotify.h"
8 
9 #include <utility>
10 
11 #include "third_party/base/ptr_util.h"
12 #include "xfa/fxfa/cxfa_ffapp.h"
13 #include "xfa/fxfa/cxfa_ffarc.h"
14 #include "xfa/fxfa/cxfa_ffbarcode.h"
15 #include "xfa/fxfa/cxfa_ffcheckbutton.h"
16 #include "xfa/fxfa/cxfa_ffcombobox.h"
17 #include "xfa/fxfa/cxfa_ffdatetimeedit.h"
18 #include "xfa/fxfa/cxfa_ffdoc.h"
19 #include "xfa/fxfa/cxfa_ffdocview.h"
20 #include "xfa/fxfa/cxfa_ffexclgroup.h"
21 #include "xfa/fxfa/cxfa_fffield.h"
22 #include "xfa/fxfa/cxfa_ffimage.h"
23 #include "xfa/fxfa/cxfa_ffimageedit.h"
24 #include "xfa/fxfa/cxfa_ffline.h"
25 #include "xfa/fxfa/cxfa_fflistbox.h"
26 #include "xfa/fxfa/cxfa_ffnumericedit.h"
27 #include "xfa/fxfa/cxfa_ffpageview.h"
28 #include "xfa/fxfa/cxfa_ffpasswordedit.h"
29 #include "xfa/fxfa/cxfa_ffpushbutton.h"
30 #include "xfa/fxfa/cxfa_ffrectangle.h"
31 #include "xfa/fxfa/cxfa_ffsignature.h"
32 #include "xfa/fxfa/cxfa_fftext.h"
33 #include "xfa/fxfa/cxfa_ffwidget.h"
34 #include "xfa/fxfa/cxfa_ffwidgethandler.h"
35 #include "xfa/fxfa/cxfa_fwladapterwidgetmgr.h"
36 #include "xfa/fxfa/cxfa_textlayout.h"
37 #include "xfa/fxfa/cxfa_textprovider.h"
38 #include "xfa/fxfa/layout/cxfa_layoutprocessor.h"
39 #include "xfa/fxfa/parser/cxfa_barcode.h"
40 #include "xfa/fxfa/parser/cxfa_binditems.h"
41 #include "xfa/fxfa/parser/cxfa_button.h"
42 #include "xfa/fxfa/parser/cxfa_checkbutton.h"
43 #include "xfa/fxfa/parser/cxfa_node.h"
44 #include "xfa/fxfa/parser/cxfa_passwordedit.h"
45 
CXFA_FFNotify(CXFA_FFDoc * pDoc)46 CXFA_FFNotify::CXFA_FFNotify(CXFA_FFDoc* pDoc) : m_pDoc(pDoc) {}
47 
~CXFA_FFNotify()48 CXFA_FFNotify::~CXFA_FFNotify() {}
49 
OnPageEvent(CXFA_ViewLayoutItem * pSender,uint32_t dwEvent)50 void CXFA_FFNotify::OnPageEvent(CXFA_ViewLayoutItem* pSender,
51                                 uint32_t dwEvent) {
52   CXFA_FFDocView* pDocView = m_pDoc->GetDocView(pSender->GetLayout());
53   if (pDocView)
54     pDocView->OnPageEvent(pSender, dwEvent);
55 }
56 
OnWidgetListItemAdded(CXFA_Node * pSender,const WideString & wsLabel,int32_t iIndex)57 void CXFA_FFNotify::OnWidgetListItemAdded(CXFA_Node* pSender,
58                                           const WideString& wsLabel,
59                                           int32_t iIndex) {
60   if (pSender->GetFFWidgetType() != XFA_FFWidgetType::kChoiceList)
61     return;
62 
63   CXFA_FFWidget* pWidget = m_pDoc->GetDocView()->GetWidgetForNode(pSender);
64   for (; pWidget; pWidget = pWidget->GetNextFFWidget()) {
65     if (pWidget->IsLoaded())
66       ToDropDown(ToField(pWidget))->InsertItem(wsLabel, iIndex);
67   }
68 }
69 
OnWidgetListItemRemoved(CXFA_Node * pSender,int32_t iIndex)70 void CXFA_FFNotify::OnWidgetListItemRemoved(CXFA_Node* pSender,
71                                             int32_t iIndex) {
72   if (pSender->GetFFWidgetType() != XFA_FFWidgetType::kChoiceList)
73     return;
74 
75   CXFA_FFWidget* pWidget = m_pDoc->GetDocView()->GetWidgetForNode(pSender);
76   for (; pWidget; pWidget = pWidget->GetNextFFWidget()) {
77     if (pWidget->IsLoaded())
78       ToDropDown(ToField(pWidget))->DeleteItem(iIndex);
79   }
80 }
81 
OnCreateViewLayoutItem(CXFA_Node * pNode)82 std::unique_ptr<CXFA_FFPageView> CXFA_FFNotify::OnCreateViewLayoutItem(
83     CXFA_Node* pNode) {
84   if (pNode->GetElementType() != XFA_Element::PageArea)
85     return nullptr;
86 
87   auto* pLayout = CXFA_LayoutProcessor::FromDocument(m_pDoc->GetXFADoc());
88   return pdfium::MakeUnique<CXFA_FFPageView>(m_pDoc->GetDocView(pLayout),
89                                              pNode);
90 }
91 
OnCreateContentLayoutItem(CXFA_Node * pNode)92 std::unique_ptr<CXFA_FFWidget> CXFA_FFNotify::OnCreateContentLayoutItem(
93     CXFA_Node* pNode) {
94   ASSERT(pNode->GetElementType() != XFA_Element::ContentArea);
95   ASSERT(pNode->GetElementType() != XFA_Element::PageArea);
96 
97   // We only need to create the widget for certain types of objects.
98   if (!pNode->HasCreatedUIWidget())
99     return nullptr;
100 
101   std::unique_ptr<CXFA_FFWidget> pWidget;
102   switch (pNode->GetFFWidgetType()) {
103     case XFA_FFWidgetType::kBarcode: {
104       CXFA_Node* child = pNode->GetUIChildNode();
105       if (child->GetElementType() != XFA_Element::Barcode)
106         return nullptr;
107 
108       pWidget = pdfium::MakeUnique<CXFA_FFBarcode>(
109           pNode, static_cast<CXFA_Barcode*>(child));
110       break;
111     }
112     case XFA_FFWidgetType::kButton: {
113       CXFA_Node* child = pNode->GetUIChildNode();
114       if (child->GetElementType() != XFA_Element::Button)
115         return nullptr;
116 
117       pWidget = pdfium::MakeUnique<CXFA_FFPushButton>(
118           pNode, static_cast<CXFA_Button*>(child));
119       break;
120     }
121     case XFA_FFWidgetType::kCheckButton: {
122       CXFA_Node* child = pNode->GetUIChildNode();
123       if (child->GetElementType() != XFA_Element::CheckButton)
124         return nullptr;
125 
126       pWidget = pdfium::MakeUnique<CXFA_FFCheckButton>(
127           pNode, static_cast<CXFA_CheckButton*>(child));
128       break;
129     }
130     case XFA_FFWidgetType::kChoiceList: {
131       if (pNode->IsListBox())
132         pWidget = pdfium::MakeUnique<CXFA_FFListBox>(pNode);
133       else
134         pWidget = pdfium::MakeUnique<CXFA_FFComboBox>(pNode);
135       break;
136     }
137     case XFA_FFWidgetType::kDateTimeEdit:
138       pWidget = pdfium::MakeUnique<CXFA_FFDateTimeEdit>(pNode);
139       break;
140     case XFA_FFWidgetType::kImageEdit:
141       pWidget = pdfium::MakeUnique<CXFA_FFImageEdit>(pNode);
142       break;
143     case XFA_FFWidgetType::kNumericEdit:
144       pWidget = pdfium::MakeUnique<CXFA_FFNumericEdit>(pNode);
145       break;
146     case XFA_FFWidgetType::kPasswordEdit: {
147       CXFA_Node* child = pNode->GetUIChildNode();
148       if (child->GetElementType() != XFA_Element::PasswordEdit)
149         return nullptr;
150 
151       pWidget = pdfium::MakeUnique<CXFA_FFPasswordEdit>(
152           pNode, static_cast<CXFA_PasswordEdit*>(child));
153       break;
154     }
155     case XFA_FFWidgetType::kSignature:
156       pWidget = pdfium::MakeUnique<CXFA_FFSignature>(pNode);
157       break;
158     case XFA_FFWidgetType::kTextEdit:
159       pWidget = pdfium::MakeUnique<CXFA_FFTextEdit>(pNode);
160       break;
161     case XFA_FFWidgetType::kArc:
162       pWidget = pdfium::MakeUnique<CXFA_FFArc>(pNode);
163       break;
164     case XFA_FFWidgetType::kLine:
165       pWidget = pdfium::MakeUnique<CXFA_FFLine>(pNode);
166       break;
167     case XFA_FFWidgetType::kRectangle:
168       pWidget = pdfium::MakeUnique<CXFA_FFRectangle>(pNode);
169       break;
170     case XFA_FFWidgetType::kText:
171       pWidget = pdfium::MakeUnique<CXFA_FFText>(pNode);
172       break;
173     case XFA_FFWidgetType::kImage:
174       pWidget = pdfium::MakeUnique<CXFA_FFImage>(pNode);
175       break;
176     case XFA_FFWidgetType::kSubform:
177       pWidget = pdfium::MakeUnique<CXFA_FFWidget>(pNode);
178       break;
179     case XFA_FFWidgetType::kExclGroup:
180       pWidget = pdfium::MakeUnique<CXFA_FFExclGroup>(pNode);
181       break;
182     case XFA_FFWidgetType::kNone:
183       return nullptr;
184   }
185   ASSERT(pWidget);
186   auto* pLayout = CXFA_LayoutProcessor::FromDocument(m_pDoc->GetXFADoc());
187   pWidget->SetDocView(m_pDoc->GetDocView(pLayout));
188   return pWidget;
189 }
190 
StartFieldDrawLayout(CXFA_Node * pItem,float * pCalcWidth,float * pCalcHeight)191 void CXFA_FFNotify::StartFieldDrawLayout(CXFA_Node* pItem,
192                                          float* pCalcWidth,
193                                          float* pCalcHeight) {
194   pItem->StartWidgetLayout(m_pDoc.Get(), pCalcWidth, pCalcHeight);
195 }
196 
RunScript(CXFA_Script * script,CXFA_Node * item)197 bool CXFA_FFNotify::RunScript(CXFA_Script* script, CXFA_Node* item) {
198   CXFA_FFDocView* pDocView = m_pDoc->GetDocView();
199   if (!pDocView)
200     return false;
201 
202   CXFA_EventParam EventParam;
203   EventParam.m_eType = XFA_EVENT_Unknown;
204 
205   XFA_EventError iRet;
206   bool bRet;
207   std::tie(iRet, bRet) = item->ExecuteBoolScript(pDocView, script, &EventParam);
208   return iRet == XFA_EventError::kSuccess && bRet;
209 }
210 
ExecEventByDeepFirst(CXFA_Node * pFormNode,XFA_EVENTTYPE eEventType,bool bIsFormReady,bool bRecursive)211 XFA_EventError CXFA_FFNotify::ExecEventByDeepFirst(CXFA_Node* pFormNode,
212                                                    XFA_EVENTTYPE eEventType,
213                                                    bool bIsFormReady,
214                                                    bool bRecursive) {
215   CXFA_FFDocView* pDocView = m_pDoc->GetDocView();
216   if (!pDocView)
217     return XFA_EventError::kNotExist;
218   return pDocView->ExecEventActivityByDeepFirst(pFormNode, eEventType,
219                                                 bIsFormReady, bRecursive);
220 }
221 
AddCalcValidate(CXFA_Node * pNode)222 void CXFA_FFNotify::AddCalcValidate(CXFA_Node* pNode) {
223   CXFA_FFDocView* pDocView = m_pDoc->GetDocView();
224   if (!pDocView)
225     return;
226 
227   pDocView->AddCalculateNode(pNode);
228   pDocView->AddValidateNode(pNode);
229 }
230 
GetAppProvider()231 IXFA_AppProvider* CXFA_FFNotify::GetAppProvider() {
232   return m_pDoc->GetApp()->GetAppProvider();
233 }
234 
GetWidgetHandler()235 CXFA_FFWidgetHandler* CXFA_FFNotify::GetWidgetHandler() {
236   CXFA_FFDocView* pDocView = m_pDoc->GetDocView();
237   return pDocView ? pDocView->GetWidgetHandler() : nullptr;
238 }
239 
OpenDropDownList(CXFA_Node * pNode)240 void CXFA_FFNotify::OpenDropDownList(CXFA_Node* pNode) {
241   auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(m_pDoc->GetXFADoc());
242   CXFA_LayoutItem* pLayoutItem = pDocLayout->GetLayoutItem(pNode);
243   if (!pLayoutItem)
244     return;
245 
246   CXFA_FFWidget* hWidget = XFA_GetWidgetFromLayoutItem(pLayoutItem);
247   if (!hWidget)
248     return;
249 
250   // SetFocusWidget() may destroy |hWidget| object by JS callback.
251   ObservedPtr<CXFA_FFWidget> pObservedWidget(hWidget);
252   CXFA_FFDoc* hDoc = GetHDOC();
253   hDoc->GetDocEnvironment()->SetFocusWidget(hDoc, hWidget);
254   if (!pObservedWidget)
255     return;
256 
257   if (hWidget->GetNode()->GetFFWidgetType() != XFA_FFWidgetType::kChoiceList)
258     return;
259 
260   if (!hWidget->IsLoaded())
261     return;
262 
263   CXFA_FFDropDown* pDropDown = ToDropDown(ToField(hWidget));
264   CXFA_FFComboBox* pComboBox = ToComboBox(pDropDown);
265   if (!pComboBox)
266     return;
267 
268   CXFA_FFDocView* pDocView = m_pDoc->GetDocView();
269   pDocView->LockUpdate();
270   pComboBox->OpenDropDownList();
271   pDocView->UnlockUpdate();
272   pDocView->UpdateDocView();
273 }
274 
ResetData(CXFA_Node * pNode)275 void CXFA_FFNotify::ResetData(CXFA_Node* pNode) {
276   CXFA_FFDocView* pDocView = m_pDoc->GetDocView();
277   if (!pDocView)
278     return;
279 
280   pDocView->ResetNode(pNode);
281 }
282 
GetLayoutStatus()283 int32_t CXFA_FFNotify::GetLayoutStatus() {
284   CXFA_FFDocView* pDocView = m_pDoc->GetDocView();
285   return pDocView ? pDocView->GetLayoutStatus() : 0;
286 }
287 
RunNodeInitialize(CXFA_Node * pNode)288 void CXFA_FFNotify::RunNodeInitialize(CXFA_Node* pNode) {
289   CXFA_FFDocView* pDocView = m_pDoc->GetDocView();
290   if (!pDocView)
291     return;
292 
293   pDocView->AddNewFormNode(pNode);
294 }
295 
RunSubformIndexChange(CXFA_Node * pSubformNode)296 void CXFA_FFNotify::RunSubformIndexChange(CXFA_Node* pSubformNode) {
297   CXFA_FFDocView* pDocView = m_pDoc->GetDocView();
298   if (!pDocView)
299     return;
300 
301   pDocView->AddIndexChangedSubform(pSubformNode);
302 }
303 
GetFocusWidgetNode()304 CXFA_Node* CXFA_FFNotify::GetFocusWidgetNode() {
305   CXFA_FFDocView* pDocView = m_pDoc->GetDocView();
306   return pDocView ? pDocView->GetFocusNode() : nullptr;
307 }
308 
SetFocusWidgetNode(CXFA_Node * pNode)309 void CXFA_FFNotify::SetFocusWidgetNode(CXFA_Node* pNode) {
310   CXFA_FFDocView* pDocView = m_pDoc->GetDocView();
311   if (!pDocView)
312     return;
313   pDocView->SetFocusNode(pNode);
314 }
315 
OnNodeReady(CXFA_Node * pNode)316 void CXFA_FFNotify::OnNodeReady(CXFA_Node* pNode) {
317   CXFA_FFDocView* pDocView = m_pDoc->GetDocView();
318   if (!pDocView)
319     return;
320 
321   if (pNode->HasCreatedUIWidget()) {
322     pNode->SetWidgetReady();
323     return;
324   }
325 
326   switch (pNode->GetElementType()) {
327     case XFA_Element::BindItems:
328       pDocView->AddBindItem(static_cast<CXFA_BindItems*>(pNode));
329       break;
330     case XFA_Element::Validate:
331       pNode->SetFlag(XFA_NodeFlag_NeedsInitApp);
332       break;
333     default:
334       break;
335   }
336 }
337 
OnValueChanging(CXFA_Node * pSender,XFA_Attribute eAttr)338 void CXFA_FFNotify::OnValueChanging(CXFA_Node* pSender, XFA_Attribute eAttr) {
339   if (eAttr != XFA_Attribute::Presence)
340     return;
341   if (pSender->GetPacketType() == XFA_PacketType::Datasets)
342     return;
343   if (!pSender->IsFormContainer())
344     return;
345 
346   CXFA_FFDocView* pDocView = m_pDoc->GetDocView();
347   if (!pDocView)
348     return;
349   if (pDocView->GetLayoutStatus() < XFA_DOCVIEW_LAYOUTSTATUS_End)
350     return;
351 
352   CXFA_FFWidget* pWidget = m_pDoc->GetDocView()->GetWidgetForNode(pSender);
353   for (; pWidget; pWidget = pWidget->GetNextFFWidget()) {
354     if (pWidget->IsLoaded())
355       pWidget->InvalidateRect();
356   }
357 }
358 
OnValueChanged(CXFA_Node * pSender,XFA_Attribute eAttr,CXFA_Node * pParentNode,CXFA_Node * pWidgetNode)359 void CXFA_FFNotify::OnValueChanged(CXFA_Node* pSender,
360                                    XFA_Attribute eAttr,
361                                    CXFA_Node* pParentNode,
362                                    CXFA_Node* pWidgetNode) {
363   CXFA_FFDocView* pDocView = m_pDoc->GetDocView();
364   if (!pDocView)
365     return;
366 
367   if (pSender->GetPacketType() != XFA_PacketType::Form) {
368     if (eAttr == XFA_Attribute::Value)
369       pDocView->AddCalculateNodeNotify(pSender);
370     return;
371   }
372 
373   XFA_Element eType = pParentNode->GetElementType();
374   bool bIsContainerNode = pParentNode->IsContainerNode();
375   bool bUpdateProperty = false;
376   pDocView->SetChangeMark();
377   switch (eType) {
378     case XFA_Element::Caption: {
379       CXFA_TextLayout* pCapOut = pWidgetNode->GetCaptionTextLayout();
380       if (!pCapOut)
381         return;
382 
383       pCapOut->Unload();
384       break;
385     }
386     case XFA_Element::Ui:
387     case XFA_Element::Para:
388       bUpdateProperty = true;
389       break;
390     default:
391       break;
392   }
393   if (bIsContainerNode && eAttr == XFA_Attribute::Access)
394     bUpdateProperty = true;
395 
396   if (eAttr == XFA_Attribute::Value) {
397     pDocView->AddCalculateNodeNotify(pSender);
398     if (eType == XFA_Element::Value || bIsContainerNode) {
399       if (bIsContainerNode) {
400         m_pDoc->GetDocView()->UpdateUIDisplay(pWidgetNode, nullptr);
401         pDocView->AddCalculateNode(pWidgetNode);
402         pDocView->AddValidateNode(pWidgetNode);
403       } else if (pWidgetNode->GetParent()->GetElementType() ==
404                  XFA_Element::ExclGroup) {
405         m_pDoc->GetDocView()->UpdateUIDisplay(pWidgetNode, nullptr);
406       }
407       return;
408     }
409   }
410 
411   CXFA_FFWidget* pWidget = m_pDoc->GetDocView()->GetWidgetForNode(pWidgetNode);
412   for (; pWidget; pWidget = pWidget->GetNextFFWidget()) {
413     if (!pWidget->IsLoaded())
414       continue;
415 
416     if (bUpdateProperty)
417       pWidget->UpdateWidgetProperty();
418     pWidget->PerformLayout();
419     pWidget->InvalidateRect();
420   }
421 }
422 
OnContainerChanged(CXFA_Node * pNode)423 void CXFA_FFNotify::OnContainerChanged(CXFA_Node* pNode) {
424   m_pDoc->GetXFADoc()->GetLayoutProcessor()->AddChangedContainer(pNode);
425 }
426 
OnChildAdded(CXFA_Node * pSender)427 void CXFA_FFNotify::OnChildAdded(CXFA_Node* pSender) {
428   if (!pSender->IsFormContainer())
429     return;
430 
431   CXFA_FFDocView* pDocView = m_pDoc->GetDocView();
432   if (!pDocView)
433     return;
434 
435   bool bLayoutReady =
436       !(pDocView->m_bInLayoutStatus) &&
437       (pDocView->GetLayoutStatus() == XFA_DOCVIEW_LAYOUTSTATUS_End);
438   if (bLayoutReady)
439     m_pDoc->GetDocEnvironment()->SetChangeMark(m_pDoc.Get());
440 }
441 
OnChildRemoved()442 void CXFA_FFNotify::OnChildRemoved() {
443   CXFA_FFDocView* pDocView = m_pDoc->GetDocView();
444   if (!pDocView)
445     return;
446 
447   bool bLayoutReady =
448       !(pDocView->m_bInLayoutStatus) &&
449       (pDocView->GetLayoutStatus() == XFA_DOCVIEW_LAYOUTSTATUS_End);
450   if (bLayoutReady)
451     m_pDoc->GetDocEnvironment()->SetChangeMark(m_pDoc.Get());
452 }
453 
OnLayoutItemAdded(CXFA_LayoutProcessor * pLayout,CXFA_LayoutItem * pSender,int32_t iPageIdx,uint32_t dwStatus)454 void CXFA_FFNotify::OnLayoutItemAdded(CXFA_LayoutProcessor* pLayout,
455                                       CXFA_LayoutItem* pSender,
456                                       int32_t iPageIdx,
457                                       uint32_t dwStatus) {
458   CXFA_FFDocView* pDocView = m_pDoc->GetDocView(pLayout);
459   if (!pDocView)
460     return;
461 
462   CXFA_FFWidget* pWidget = XFA_GetWidgetFromLayoutItem(pSender);
463   if (!pWidget)
464     return;
465 
466   CXFA_FFPageView* pNewPageView = pDocView->GetPageView(iPageIdx);
467   uint32_t dwFilter = XFA_WidgetStatus_Visible | XFA_WidgetStatus_Viewable |
468                       XFA_WidgetStatus_Printable;
469   pWidget->ModifyStatus(dwStatus, dwFilter);
470   CXFA_FFPageView* pPrePageView = pWidget->GetPageView();
471   if (pPrePageView != pNewPageView ||
472       (dwStatus & (XFA_WidgetStatus_Visible | XFA_WidgetStatus_Viewable)) ==
473           (XFA_WidgetStatus_Visible | XFA_WidgetStatus_Viewable)) {
474     pWidget->SetPageView(pNewPageView);
475     m_pDoc->GetDocEnvironment()->WidgetPostAdd(pWidget);
476   }
477   if (pDocView->GetLayoutStatus() != XFA_DOCVIEW_LAYOUTSTATUS_End ||
478       !(dwStatus & XFA_WidgetStatus_Visible)) {
479     return;
480   }
481   if (pWidget->IsLoaded()) {
482     if (pWidget->GetWidgetRect() != pWidget->RecacheWidgetRect())
483       pWidget->PerformLayout();
484   } else {
485     pWidget->LoadWidget();
486   }
487   pWidget->InvalidateRect();
488 }
489 
OnLayoutItemRemoving(CXFA_LayoutProcessor * pLayout,CXFA_LayoutItem * pSender)490 void CXFA_FFNotify::OnLayoutItemRemoving(CXFA_LayoutProcessor* pLayout,
491                                          CXFA_LayoutItem* pSender) {
492   CXFA_FFDocView* pDocView = m_pDoc->GetDocView(pLayout);
493   if (!pDocView)
494     return;
495 
496   CXFA_FFWidget* pWidget = XFA_GetWidgetFromLayoutItem(pSender);
497   if (!pWidget)
498     return;
499 
500   pDocView->DeleteLayoutItem(pWidget);
501   m_pDoc->GetDocEnvironment()->WidgetPreRemove(pWidget);
502   pWidget->InvalidateRect();
503 }
504