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(¶m);
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