• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 The PDFium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6 
7 #include "xfa/fxfa/cxfa_ffpageview.h"
8 
9 #include <algorithm>
10 #include <vector>
11 
12 #include "core/fxcrt/stl_util.h"
13 #include "fxjs/gc/container_trace.h"
14 #include "fxjs/xfa/cjx_object.h"
15 #include "third_party/base/check.h"
16 #include "third_party/base/containers/contains.h"
17 #include "xfa/fxfa/cxfa_ffcheckbutton.h"
18 #include "xfa/fxfa/cxfa_ffdoc.h"
19 #include "xfa/fxfa/cxfa_ffdocview.h"
20 #include "xfa/fxfa/cxfa_fffield.h"
21 #include "xfa/fxfa/cxfa_ffimageedit.h"
22 #include "xfa/fxfa/cxfa_ffpushbutton.h"
23 #include "xfa/fxfa/cxfa_ffwidget.h"
24 #include "xfa/fxfa/cxfa_fwladapterwidgetmgr.h"
25 #include "xfa/fxfa/parser/cxfa_node.h"
26 #include "xfa/fxfa/parser/cxfa_traversal.h"
27 #include "xfa/fxfa/parser/cxfa_traverse.h"
28 
29 namespace {
30 
GetPageMatrix(const CFX_RectF & docPageRect,const FX_RECT & devicePageRect,int32_t iRotate)31 CFX_Matrix GetPageMatrix(const CFX_RectF& docPageRect,
32                          const FX_RECT& devicePageRect,
33                          int32_t iRotate) {
34   DCHECK(iRotate >= 0);
35   DCHECK(iRotate <= 3);
36 
37   CFX_Matrix m;
38   if (iRotate == 0 || iRotate == 2) {
39     m.a *= (float)devicePageRect.Width() / docPageRect.width;
40     m.d *= (float)devicePageRect.Height() / docPageRect.height;
41   } else {
42     m.a *= (float)devicePageRect.Height() / docPageRect.width;
43     m.d *= (float)devicePageRect.Width() / docPageRect.height;
44   }
45   m.Rotate(iRotate * 1.57079632675f);
46   switch (iRotate) {
47     case 0:
48       m.e = devicePageRect.left;
49       m.f = devicePageRect.top;
50       break;
51     case 1:
52       m.e = devicePageRect.right;
53       m.f = devicePageRect.top;
54       break;
55     case 2:
56       m.e = devicePageRect.right;
57       m.f = devicePageRect.bottom;
58       break;
59     case 3:
60       m.e = devicePageRect.left;
61       m.f = devicePageRect.bottom;
62       break;
63     default:
64       break;
65   }
66   return m;
67 }
68 
PageWidgetFilter(CXFA_FFWidget * pWidget,Mask<XFA_WidgetStatus> dwFilter,bool bTraversal,bool bIgnoreRelevant)69 bool PageWidgetFilter(CXFA_FFWidget* pWidget,
70                       Mask<XFA_WidgetStatus> dwFilter,
71                       bool bTraversal,
72                       bool bIgnoreRelevant) {
73   CXFA_Node* pNode = pWidget->GetNode();
74 
75   if ((dwFilter & XFA_WidgetStatus::kFocused) &&
76       (!pNode || pNode->GetElementType() != XFA_Element::Field)) {
77     return false;
78   }
79 
80   CXFA_ContentLayoutItem* pItem = pWidget->GetLayoutItem();
81   if (bTraversal && pItem->TestStatusBits(XFA_WidgetStatus::kDisabled))
82     return false;
83   if (bIgnoreRelevant)
84     return pItem->TestStatusBits(XFA_WidgetStatus::kVisible);
85 
86   dwFilter &= Mask<XFA_WidgetStatus>{XFA_WidgetStatus::kVisible,
87                                      XFA_WidgetStatus::kViewable,
88                                      XFA_WidgetStatus::kPrintable};
89   return pItem->TestStatusBits(dwFilter);
90 }
91 
IsLayoutElement(XFA_Element eElement)92 bool IsLayoutElement(XFA_Element eElement) {
93   switch (eElement) {
94     case XFA_Element::Area:
95     case XFA_Element::Subform:
96     case XFA_Element::ExclGroup:
97     case XFA_Element::SubformSet:
98     case XFA_Element::PageArea:
99     case XFA_Element::Form:
100       return true;
101     default:
102       return false;
103   }
104 }
105 
GetDocForPageView(const CXFA_FFPageView * view)106 CXFA_Document* GetDocForPageView(const CXFA_FFPageView* view) {
107   return view->GetDocView()->GetDoc()->GetXFADoc();
108 }
109 
IsDocVersionBelow205(const CXFA_Document * doc)110 bool IsDocVersionBelow205(const CXFA_Document* doc) {
111   return doc->GetCurVersionMode() < XFA_VERSION_205;
112 }
113 
EnsureWidgetLoadedIfVisible(CXFA_FFWidget * pWidget)114 bool EnsureWidgetLoadedIfVisible(CXFA_FFWidget* pWidget) {
115   if (!pWidget->IsLoaded() &&
116       pWidget->GetLayoutItem()->TestStatusBits(XFA_WidgetStatus::kVisible)) {
117     if (!pWidget->LoadWidget())
118       return false;
119   }
120   return true;
121 }
122 
LoadedWidgetFromLayoutItem(CXFA_LayoutItem * pLayoutItem)123 CXFA_FFWidget* LoadedWidgetFromLayoutItem(CXFA_LayoutItem* pLayoutItem) {
124   CXFA_FFWidget* pWidget = CXFA_FFWidget::FromLayoutItem(pLayoutItem);
125   if (!pWidget)
126     return nullptr;
127 
128   EnsureWidgetLoadedIfVisible(pWidget);
129   return pWidget;
130 }
131 
FilteredLoadedWidgetFromLayoutItem(CXFA_LayoutItem * pLayoutItem,Mask<XFA_WidgetStatus> dwFilter,bool bIgnoreRelevant)132 CXFA_FFWidget* FilteredLoadedWidgetFromLayoutItem(
133     CXFA_LayoutItem* pLayoutItem,
134     Mask<XFA_WidgetStatus> dwFilter,
135     bool bIgnoreRelevant) {
136   CXFA_FFWidget* pWidget = CXFA_FFWidget::FromLayoutItem(pLayoutItem);
137   if (!pWidget)
138     return nullptr;
139 
140   if (!PageWidgetFilter(pWidget, dwFilter, false, bIgnoreRelevant))
141     return nullptr;
142 
143   if (!EnsureWidgetLoadedIfVisible(pWidget))
144     return nullptr;
145 
146   return pWidget;
147 }
148 
149 class CXFA_TabParam {
150  public:
151   CXFA_TabParam() = default;
CXFA_TabParam(CXFA_FFWidget * pWidget)152   explicit CXFA_TabParam(CXFA_FFWidget* pWidget)
153       : m_pItem(pWidget->GetLayoutItem()) {}
154   CXFA_TabParam(const CXFA_TabParam&) = delete;
155   CXFA_TabParam(CXFA_TabParam&&) noexcept = default;
156   ~CXFA_TabParam() = default;
157 
158   CXFA_TabParam& operator=(const CXFA_TabParam&) = delete;
159   CXFA_TabParam& operator=(CXFA_TabParam&&) noexcept = default;
160 
GetWidget() const161   CXFA_FFWidget* GetWidget() const { return m_pItem->GetFFWidget(); }
GetChildren() const162   const std::vector<cppgc::Persistent<CXFA_ContentLayoutItem>>& GetChildren()
163       const {
164     return m_Children;
165   }
ClearChildren()166   void ClearChildren() { m_Children.clear(); }
AppendTabParam(const CXFA_TabParam * pParam)167   void AppendTabParam(const CXFA_TabParam* pParam) {
168     m_Children.push_back(pParam->m_pItem);
169     m_Children.insert(m_Children.end(), pParam->m_Children.begin(),
170                       pParam->m_Children.end());
171   }
172 
173  private:
174   cppgc::Persistent<CXFA_ContentLayoutItem> m_pItem;
175   std::vector<cppgc::Persistent<CXFA_ContentLayoutItem>> m_Children;
176 };
177 
OrderContainer(CXFA_LayoutItemIterator * sIterator,CXFA_LayoutItem * pViewItem,CXFA_TabParam * pContainer,bool * bCurrentItem,bool * bContentArea,bool bMasterPage)178 void OrderContainer(CXFA_LayoutItemIterator* sIterator,
179                     CXFA_LayoutItem* pViewItem,
180                     CXFA_TabParam* pContainer,
181                     bool* bCurrentItem,
182                     bool* bContentArea,
183                     bool bMasterPage) {
184   std::vector<CXFA_TabParam> tabParams;
185   CXFA_LayoutItem* pSearchItem = sIterator->MoveToNext();
186   while (pSearchItem) {
187     if (!pSearchItem->IsContentLayoutItem()) {
188       *bContentArea = true;
189       pSearchItem = sIterator->MoveToNext();
190       continue;
191     }
192     if (bMasterPage && *bContentArea) {
193       break;
194     }
195     if (bMasterPage || *bContentArea) {
196       CXFA_FFWidget* hWidget = LoadedWidgetFromLayoutItem(pSearchItem);
197       if (!hWidget) {
198         pSearchItem = sIterator->MoveToNext();
199         continue;
200       }
201       if (pViewItem && (pSearchItem->GetParent() != pViewItem)) {
202         *bCurrentItem = true;
203         break;
204       }
205       tabParams.emplace_back(hWidget);
206       if (IsLayoutElement(pSearchItem->GetFormNode()->GetElementType())) {
207         OrderContainer(sIterator, pSearchItem, &tabParams.back(), bCurrentItem,
208                        bContentArea, bMasterPage);
209       }
210     }
211     if (*bCurrentItem) {
212       pSearchItem = sIterator->GetCurrent();
213       *bCurrentItem = false;
214     } else {
215       pSearchItem = sIterator->MoveToNext();
216     }
217   }
218   std::sort(tabParams.begin(), tabParams.end(),
219             [](const CXFA_TabParam& arg1, const CXFA_TabParam& arg2) {
220               const CFX_RectF& rt1 = arg1.GetWidget()->GetWidgetRect();
221               const CFX_RectF& rt2 = arg2.GetWidget()->GetWidgetRect();
222               if (rt1.top - rt2.top >= kXFAWidgetPrecision)
223                 return rt1.top < rt2.top;
224               return rt1.left < rt2.left;
225             });
226   for (const auto& param : tabParams)
227     pContainer->AppendTabParam(&param);
228 }
229 
230 }  // namespace
231 
CXFA_FFPageView(CXFA_FFDocView * pDocView,CXFA_Node * pPageArea)232 CXFA_FFPageView::CXFA_FFPageView(CXFA_FFDocView* pDocView, CXFA_Node* pPageArea)
233     : m_pPageArea(pPageArea), m_pDocView(pDocView) {}
234 
235 CXFA_FFPageView::~CXFA_FFPageView() = default;
236 
Trace(cppgc::Visitor * visitor) const237 void CXFA_FFPageView::Trace(cppgc::Visitor* visitor) const {
238   visitor->Trace(m_pPageArea);
239   visitor->Trace(m_pDocView);
240   visitor->Trace(m_pLayoutItem);
241 }
242 
GetDocView() const243 CXFA_FFDocView* CXFA_FFPageView::GetDocView() const {
244   return m_pDocView;
245 }
246 
GetPageViewRect() const247 CFX_RectF CXFA_FFPageView::GetPageViewRect() const {
248   auto* pItem = GetLayoutItem();
249   if (!pItem)
250     return CFX_RectF();
251 
252   return CFX_RectF(0, 0, pItem->GetPageSize());
253 }
254 
GetDisplayMatrix(const FX_RECT & rtDisp,int32_t iRotate) const255 CFX_Matrix CXFA_FFPageView::GetDisplayMatrix(const FX_RECT& rtDisp,
256                                              int32_t iRotate) const {
257   auto* pItem = GetLayoutItem();
258   if (!pItem)
259     return CFX_Matrix();
260 
261   return GetPageMatrix(CFX_RectF(0, 0, pItem->GetPageSize()), rtDisp, iRotate);
262 }
263 
CreateGCedTraverseWidgetIterator(Mask<XFA_WidgetStatus> dwWidgetFilter)264 CXFA_FFWidget::IteratorIface* CXFA_FFPageView::CreateGCedTraverseWidgetIterator(
265     Mask<XFA_WidgetStatus> dwWidgetFilter) {
266   return cppgc::MakeGarbageCollected<CXFA_FFTabOrderPageWidgetIterator>(
267       GetDocView()->GetDoc()->GetHeap()->GetAllocationHandle(), this,
268       dwWidgetFilter);
269 }
270 
CXFA_FFPageWidgetIterator(CXFA_FFPageView * pPageView,Mask<XFA_WidgetStatus> dwFilter)271 CXFA_FFPageWidgetIterator::CXFA_FFPageWidgetIterator(
272     CXFA_FFPageView* pPageView,
273     Mask<XFA_WidgetStatus> dwFilter)
274     : m_sIterator(pPageView->GetLayoutItem()),
275       m_dwFilter(dwFilter),
276       m_bIgnoreRelevant(IsDocVersionBelow205(GetDocForPageView(pPageView))) {}
277 
278 CXFA_FFPageWidgetIterator::~CXFA_FFPageWidgetIterator() = default;
279 
MoveToFirst()280 CXFA_FFWidget* CXFA_FFPageWidgetIterator::MoveToFirst() {
281   m_sIterator.Reset();
282   for (CXFA_LayoutItem* pLayoutItem = m_sIterator.GetCurrent(); pLayoutItem;
283        pLayoutItem = m_sIterator.MoveToNext()) {
284     CXFA_FFWidget* hWidget = FilteredLoadedWidgetFromLayoutItem(
285         pLayoutItem, m_dwFilter, m_bIgnoreRelevant);
286     if (hWidget)
287       return hWidget;
288   }
289   return nullptr;
290 }
291 
MoveToLast()292 CXFA_FFWidget* CXFA_FFPageWidgetIterator::MoveToLast() {
293   m_sIterator.SetCurrent(nullptr);
294   return MoveToPrevious();
295 }
296 
MoveToNext()297 CXFA_FFWidget* CXFA_FFPageWidgetIterator::MoveToNext() {
298   for (CXFA_LayoutItem* pLayoutItem = m_sIterator.MoveToNext(); pLayoutItem;
299        pLayoutItem = m_sIterator.MoveToNext()) {
300     CXFA_FFWidget* hWidget = FilteredLoadedWidgetFromLayoutItem(
301         pLayoutItem, m_dwFilter, m_bIgnoreRelevant);
302     if (hWidget)
303       return hWidget;
304   }
305   return nullptr;
306 }
307 
MoveToPrevious()308 CXFA_FFWidget* CXFA_FFPageWidgetIterator::MoveToPrevious() {
309   for (CXFA_LayoutItem* pLayoutItem = m_sIterator.MoveToPrev(); pLayoutItem;
310        pLayoutItem = m_sIterator.MoveToPrev()) {
311     CXFA_FFWidget* hWidget = FilteredLoadedWidgetFromLayoutItem(
312         pLayoutItem, m_dwFilter, m_bIgnoreRelevant);
313     if (hWidget)
314       return hWidget;
315   }
316   return nullptr;
317 }
318 
GetCurrentWidget()319 CXFA_FFWidget* CXFA_FFPageWidgetIterator::GetCurrentWidget() {
320   CXFA_LayoutItem* pLayoutItem = m_sIterator.GetCurrent();
321   return pLayoutItem ? CXFA_FFWidget::FromLayoutItem(pLayoutItem) : nullptr;
322 }
323 
SetCurrentWidget(CXFA_FFWidget * pWidget)324 bool CXFA_FFPageWidgetIterator::SetCurrentWidget(CXFA_FFWidget* pWidget) {
325   return pWidget && m_sIterator.SetCurrent(pWidget->GetLayoutItem());
326 }
327 
CXFA_FFTabOrderPageWidgetIterator(CXFA_FFPageView * pPageView,Mask<XFA_WidgetStatus> dwFilter)328 CXFA_FFTabOrderPageWidgetIterator::CXFA_FFTabOrderPageWidgetIterator(
329     CXFA_FFPageView* pPageView,
330     Mask<XFA_WidgetStatus> dwFilter)
331     : m_pPageViewLayout(pPageView->GetLayoutItem()),
332       m_dwFilter(dwFilter),
333       m_bIgnoreRelevant(IsDocVersionBelow205(GetDocForPageView(pPageView))) {
334   CreateTabOrderWidgetArray();
335 }
336 
337 CXFA_FFTabOrderPageWidgetIterator::~CXFA_FFTabOrderPageWidgetIterator() =
338     default;
339 
Trace(cppgc::Visitor * visitor) const340 void CXFA_FFTabOrderPageWidgetIterator::Trace(cppgc::Visitor* visitor) const {
341   visitor->Trace(m_pPageViewLayout);
342   ContainerTrace(visitor, m_TabOrderWidgetArray);
343 }
344 
MoveToFirst()345 CXFA_FFWidget* CXFA_FFTabOrderPageWidgetIterator::MoveToFirst() {
346   for (int32_t i = 0; i < fxcrt::CollectionSize<int32_t>(m_TabOrderWidgetArray);
347        i++) {
348     if (PageWidgetFilter(m_TabOrderWidgetArray[i]->GetFFWidget(), m_dwFilter,
349                          true, m_bIgnoreRelevant)) {
350       m_iCurWidget = i;
351       return m_TabOrderWidgetArray[m_iCurWidget]->GetFFWidget();
352     }
353   }
354   return nullptr;
355 }
356 
MoveToLast()357 CXFA_FFWidget* CXFA_FFTabOrderPageWidgetIterator::MoveToLast() {
358   for (int32_t i = fxcrt::CollectionSize<int32_t>(m_TabOrderWidgetArray) - 1;
359        i >= 0; i--) {
360     if (PageWidgetFilter(m_TabOrderWidgetArray[i]->GetFFWidget(), m_dwFilter,
361                          true, m_bIgnoreRelevant)) {
362       m_iCurWidget = i;
363       return m_TabOrderWidgetArray[m_iCurWidget]->GetFFWidget();
364     }
365   }
366   return nullptr;
367 }
368 
MoveToNext()369 CXFA_FFWidget* CXFA_FFTabOrderPageWidgetIterator::MoveToNext() {
370   for (int32_t i = m_iCurWidget + 1;
371        i < fxcrt::CollectionSize<int32_t>(m_TabOrderWidgetArray); i++) {
372     if (PageWidgetFilter(m_TabOrderWidgetArray[i]->GetFFWidget(), m_dwFilter,
373                          true, m_bIgnoreRelevant)) {
374       m_iCurWidget = i;
375       return m_TabOrderWidgetArray[m_iCurWidget]->GetFFWidget();
376     }
377   }
378   m_iCurWidget = -1;
379   return nullptr;
380 }
381 
MoveToPrevious()382 CXFA_FFWidget* CXFA_FFTabOrderPageWidgetIterator::MoveToPrevious() {
383   for (int32_t i = m_iCurWidget - 1; i >= 0; i--) {
384     if (PageWidgetFilter(m_TabOrderWidgetArray[i]->GetFFWidget(), m_dwFilter,
385                          true, m_bIgnoreRelevant)) {
386       m_iCurWidget = i;
387       return m_TabOrderWidgetArray[m_iCurWidget]->GetFFWidget();
388     }
389   }
390   m_iCurWidget = -1;
391   return nullptr;
392 }
393 
GetCurrentWidget()394 CXFA_FFWidget* CXFA_FFTabOrderPageWidgetIterator::GetCurrentWidget() {
395   return m_iCurWidget >= 0 ? m_TabOrderWidgetArray[m_iCurWidget]->GetFFWidget()
396                            : nullptr;
397 }
398 
SetCurrentWidget(CXFA_FFWidget * hWidget)399 bool CXFA_FFTabOrderPageWidgetIterator::SetCurrentWidget(
400     CXFA_FFWidget* hWidget) {
401   auto it = std::find(m_TabOrderWidgetArray.begin(),
402                       m_TabOrderWidgetArray.end(), hWidget->GetLayoutItem());
403   if (it == m_TabOrderWidgetArray.end())
404     return false;
405 
406   m_iCurWidget =
407       pdfium::base::checked_cast<int32_t>(it - m_TabOrderWidgetArray.begin());
408   return true;
409 }
410 
GetTraverseWidget(CXFA_FFWidget * pWidget)411 CXFA_FFWidget* CXFA_FFTabOrderPageWidgetIterator::GetTraverseWidget(
412     CXFA_FFWidget* pWidget) {
413   CXFA_Traversal* pTraversal = pWidget->GetNode()->GetChild<CXFA_Traversal>(
414       0, XFA_Element::Traversal, false);
415   if (pTraversal) {
416     CXFA_Traverse* pTraverse =
417         pTraversal->GetChild<CXFA_Traverse>(0, XFA_Element::Traverse, false);
418     if (pTraverse) {
419       absl::optional<WideString> traverseWidgetName =
420           pTraverse->JSObject()->TryAttribute(XFA_Attribute::Ref, true);
421       if (traverseWidgetName.has_value())
422         return FindWidgetByName(traverseWidgetName.value(), pWidget);
423     }
424   }
425   return nullptr;
426 }
FindWidgetByName(const WideString & wsWidgetName,CXFA_FFWidget * pRefWidget)427 CXFA_FFWidget* CXFA_FFTabOrderPageWidgetIterator::FindWidgetByName(
428     const WideString& wsWidgetName,
429     CXFA_FFWidget* pRefWidget) {
430   return pRefWidget->GetDocView()->GetWidgetByName(wsWidgetName, pRefWidget);
431 }
432 
CreateTabOrderWidgetArray()433 void CXFA_FFTabOrderPageWidgetIterator::CreateTabOrderWidgetArray() {
434   m_TabOrderWidgetArray.clear();
435 
436   const std::vector<CXFA_ContentLayoutItem*> items =
437       CreateSpaceOrderLayoutItems();
438   if (items.empty())
439     return;
440 
441   CXFA_ContentLayoutItem* item = items[0];
442   while (m_TabOrderWidgetArray.size() < items.size()) {
443     if (!pdfium::Contains(m_TabOrderWidgetArray, item)) {
444       m_TabOrderWidgetArray.emplace_back(item);
445       CXFA_Node* node = item->GetFFWidget()->GetNode();
446       if (node->GetFFWidgetType() == XFA_FFWidgetType::kExclGroup) {
447         auto it = std::find(items.begin(), items.end(), item);
448         size_t index = it != items.end() ? it - items.begin() + 1 : 0;
449         while (true) {
450           CXFA_FFWidget* radio = items[index % items.size()]->GetFFWidget();
451           if (radio->GetNode()->GetExclGroupIfExists() != node)
452             break;
453           if (!pdfium::Contains(m_TabOrderWidgetArray, item))
454             m_TabOrderWidgetArray.emplace_back(radio->GetLayoutItem());
455           ++index;
456         }
457       }
458       CXFA_FFWidget* next_widget = GetTraverseWidget(item->GetFFWidget());
459       if (next_widget) {
460         item = next_widget->GetLayoutItem();
461         continue;
462       }
463     }
464     auto it = std::find(items.begin(), items.end(), item);
465     size_t index = it != items.end() ? it - items.begin() + 1 : 0;
466     item = items[index % items.size()];
467   }
468 }
469 
470 std::vector<CXFA_ContentLayoutItem*>
CreateSpaceOrderLayoutItems()471 CXFA_FFTabOrderPageWidgetIterator::CreateSpaceOrderLayoutItems() {
472   std::vector<CXFA_ContentLayoutItem*> items;
473   CXFA_LayoutItemIterator sIterator(m_pPageViewLayout.Get());
474   CXFA_TabParam tabparam;
475   bool bCurrentItem = false;
476   bool bContentArea = false;
477   OrderContainer(&sIterator, nullptr, &tabparam, &bCurrentItem, &bContentArea,
478                  false);
479   items.reserve(tabparam.GetChildren().size());
480   for (const auto& layout_item : tabparam.GetChildren())
481     items.push_back(layout_item);
482 
483   sIterator.Reset();
484   bCurrentItem = false;
485   bContentArea = false;
486   tabparam.ClearChildren();
487   OrderContainer(&sIterator, nullptr, &tabparam, &bCurrentItem, &bContentArea,
488                  true);
489   for (const auto& layout_item : tabparam.GetChildren())
490     items.push_back(layout_item);
491 
492   return items;
493 }
494