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_ffpageview.h"
8
9#include <algorithm>
10#include <memory>
11#include <vector>
12
13#include "fxjs/xfa/cjx_object.h"
14#include "third_party/base/ptr_util.h"
15#include "third_party/base/stl_util.h"
16#include "xfa/fxfa/cxfa_ffcheckbutton.h"
17#include "xfa/fxfa/cxfa_ffdoc.h"
18#include "xfa/fxfa/cxfa_ffdocview.h"
19#include "xfa/fxfa/cxfa_fffield.h"
20#include "xfa/fxfa/cxfa_ffimageedit.h"
21#include "xfa/fxfa/cxfa_ffpushbutton.h"
22#include "xfa/fxfa/cxfa_ffwidget.h"
23#include "xfa/fxfa/cxfa_fwladapterwidgetmgr.h"
24#include "xfa/fxfa/parser/cxfa_node.h"
25#include "xfa/fxfa/parser/cxfa_traversal.h"
26#include "xfa/fxfa/parser/cxfa_traverse.h"
27
28namespace {
29
30CFX_Matrix GetPageMatrix(const CFX_RectF& docPageRect,
31                         const CFX_Rect& devicePageRect,
32                         int32_t iRotate,
33                         uint32_t dwCoordinatesType) {
34  ASSERT(iRotate >= 0 && iRotate <= 3);
35
36  bool bFlipX = (dwCoordinatesType & 0x01) != 0;
37  bool bFlipY = (dwCoordinatesType & 0x02) != 0;
38  CFX_Matrix m((bFlipX ? -1.0f : 1.0f), 0, 0, (bFlipY ? -1.0f : 1.0f), 0, 0);
39  if (iRotate == 0 || iRotate == 2) {
40    m.a *= (float)devicePageRect.width / docPageRect.width;
41    m.d *= (float)devicePageRect.height / docPageRect.height;
42  } else {
43    m.a *= (float)devicePageRect.height / docPageRect.width;
44    m.d *= (float)devicePageRect.width / docPageRect.height;
45  }
46  m.Rotate(iRotate * 1.57079632675f);
47  switch (iRotate) {
48    case 0:
49      m.e = bFlipX ? (float)devicePageRect.right() : (float)devicePageRect.left;
50      m.f = bFlipY ? (float)devicePageRect.bottom() : (float)devicePageRect.top;
51      break;
52    case 1:
53      m.e = bFlipY ? (float)devicePageRect.left : (float)devicePageRect.right();
54      m.f = bFlipX ? (float)devicePageRect.bottom() : (float)devicePageRect.top;
55      break;
56    case 2:
57      m.e = bFlipX ? (float)devicePageRect.left : (float)devicePageRect.right();
58      m.f = bFlipY ? (float)devicePageRect.top : (float)devicePageRect.bottom();
59      break;
60    case 3:
61      m.e = bFlipY ? (float)devicePageRect.right() : (float)devicePageRect.left;
62      m.f = bFlipX ? (float)devicePageRect.top : (float)devicePageRect.bottom();
63      break;
64    default:
65      break;
66  }
67  return m;
68}
69
70bool PageWidgetFilter(CXFA_FFWidget* pWidget,
71                      uint32_t dwFilter,
72                      bool bTraversal,
73                      bool bIgnorerelevant) {
74  CXFA_Node* pNode = pWidget->GetNode();
75
76  if (!!(dwFilter & XFA_WidgetStatus_Focused) &&
77      (!pNode || pNode->GetElementType() != XFA_Element::Field)) {
78    return false;
79  }
80
81  uint32_t dwStatus = pWidget->GetStatus();
82  if (bTraversal && (dwStatus & XFA_WidgetStatus_Disabled))
83    return false;
84  if (bIgnorerelevant)
85    return !!(dwStatus & XFA_WidgetStatus_Visible);
86
87  dwFilter &= (XFA_WidgetStatus_Visible | XFA_WidgetStatus_Viewable |
88               XFA_WidgetStatus_Printable);
89  return (dwFilter & dwStatus) == dwFilter;
90}
91
92bool IsLayoutElement(XFA_Element eElement, bool bLayoutContainer) {
93  switch (eElement) {
94    case XFA_Element::Draw:
95    case XFA_Element::Field:
96    case XFA_Element::InstanceManager:
97      return !bLayoutContainer;
98    case XFA_Element::Area:
99    case XFA_Element::Subform:
100    case XFA_Element::ExclGroup:
101    case XFA_Element::SubformSet:
102    case XFA_Element::PageArea:
103    case XFA_Element::Form:
104      return true;
105    default:
106      return false;
107  }
108}
109
110}  // namespace
111
112CXFA_FFPageView::CXFA_FFPageView(CXFA_FFDocView* pDocView, CXFA_Node* pPageArea)
113    : CXFA_ContainerLayoutItem(pPageArea), m_pDocView(pDocView) {}
114
115CXFA_FFPageView::~CXFA_FFPageView() {}
116
117CXFA_FFDocView* CXFA_FFPageView::GetDocView() const {
118  return m_pDocView.Get();
119}
120
121CFX_RectF CXFA_FFPageView::GetPageViewRect() const {
122  return CFX_RectF(0, 0, GetPageSize());
123}
124
125CFX_Matrix CXFA_FFPageView::GetDisplayMatrix(const CFX_Rect& rtDisp,
126                                             int32_t iRotate) const {
127  return GetPageMatrix(CFX_RectF(0, 0, GetPageSize()), rtDisp, iRotate, 0);
128}
129
130std::unique_ptr<IXFA_WidgetIterator> CXFA_FFPageView::CreateWidgetIterator(
131    uint32_t dwTraverseWay,
132    uint32_t dwWidgetFilter) {
133  switch (dwTraverseWay) {
134    case XFA_TRAVERSEWAY_Tranvalse:
135      return pdfium::MakeUnique<CXFA_FFTabOrderPageWidgetIterator>(
136          this, dwWidgetFilter);
137    case XFA_TRAVERSEWAY_Form:
138      return pdfium::MakeUnique<CXFA_FFPageWidgetIterator>(this,
139                                                           dwWidgetFilter);
140  }
141  return nullptr;
142}
143
144CXFA_FFPageWidgetIterator::CXFA_FFPageWidgetIterator(CXFA_FFPageView* pPageView,
145                                                     uint32_t dwFilter)
146    : m_pPageView(pPageView), m_dwFilter(dwFilter), m_sIterator(pPageView) {
147  m_bIgnorerelevant =
148      m_pPageView->GetDocView()->GetDoc()->GetXFADoc()->GetCurVersionMode() <
149      XFA_VERSION_205;
150}
151
152CXFA_FFPageWidgetIterator::~CXFA_FFPageWidgetIterator() {}
153
154void CXFA_FFPageWidgetIterator::Reset() {
155  m_sIterator.Reset();
156}
157
158CXFA_FFWidget* CXFA_FFPageWidgetIterator::MoveToFirst() {
159  m_sIterator.Reset();
160  for (CXFA_LayoutItem* pLayoutItem = m_sIterator.GetCurrent(); pLayoutItem;
161       pLayoutItem = m_sIterator.MoveToNext()) {
162    if (CXFA_FFWidget* hWidget = GetWidget(pLayoutItem)) {
163      return hWidget;
164    }
165  }
166  return nullptr;
167}
168
169CXFA_FFWidget* CXFA_FFPageWidgetIterator::MoveToLast() {
170  m_sIterator.SetCurrent(nullptr);
171  return MoveToPrevious();
172}
173
174CXFA_FFWidget* CXFA_FFPageWidgetIterator::MoveToNext() {
175  for (CXFA_LayoutItem* pLayoutItem = m_sIterator.MoveToNext(); pLayoutItem;
176       pLayoutItem = m_sIterator.MoveToNext()) {
177    if (CXFA_FFWidget* hWidget = GetWidget(pLayoutItem)) {
178      return hWidget;
179    }
180  }
181  return nullptr;
182}
183
184CXFA_FFWidget* CXFA_FFPageWidgetIterator::MoveToPrevious() {
185  for (CXFA_LayoutItem* pLayoutItem = m_sIterator.MoveToPrev(); pLayoutItem;
186       pLayoutItem = m_sIterator.MoveToPrev()) {
187    if (CXFA_FFWidget* hWidget = GetWidget(pLayoutItem)) {
188      return hWidget;
189    }
190  }
191  return nullptr;
192}
193
194CXFA_FFWidget* CXFA_FFPageWidgetIterator::GetCurrentWidget() {
195  CXFA_LayoutItem* pLayoutItem = m_sIterator.GetCurrent();
196  return pLayoutItem ? XFA_GetWidgetFromLayoutItem(pLayoutItem) : nullptr;
197}
198
199bool CXFA_FFPageWidgetIterator::SetCurrentWidget(CXFA_FFWidget* hWidget) {
200  return hWidget && m_sIterator.SetCurrent(hWidget);
201}
202
203CXFA_FFWidget* CXFA_FFPageWidgetIterator::GetWidget(
204    CXFA_LayoutItem* pLayoutItem) {
205  CXFA_FFWidget* pWidget = XFA_GetWidgetFromLayoutItem(pLayoutItem);
206  if (!pWidget)
207    return nullptr;
208
209  if (!PageWidgetFilter(pWidget, m_dwFilter, false, m_bIgnorerelevant))
210    return nullptr;
211
212  if (!pWidget->IsLoaded() &&
213      !!(pWidget->GetStatus() & XFA_WidgetStatus_Visible)) {
214    if (!pWidget->LoadWidget())
215      return nullptr;
216  }
217  return pWidget;
218}
219
220void CXFA_TabParam::AppendTabParam(CXFA_TabParam* pParam) {
221  m_Children.push_back(pParam->GetWidget());
222  m_Children.insert(m_Children.end(), pParam->GetChildren().begin(),
223                    pParam->GetChildren().end());
224}
225
226void CXFA_TabParam::ClearChildren() {
227  m_Children.clear();
228}
229
230CXFA_FFTabOrderPageWidgetIterator::CXFA_FFTabOrderPageWidgetIterator(
231    CXFA_FFPageView* pPageView,
232    uint32_t dwFilter)
233    : m_pPageView(pPageView), m_dwFilter(dwFilter), m_iCurWidget(-1) {
234  m_bIgnorerelevant =
235      m_pPageView->GetDocView()->GetDoc()->GetXFADoc()->GetCurVersionMode() <
236      XFA_VERSION_205;
237  Reset();
238}
239
240CXFA_FFTabOrderPageWidgetIterator::~CXFA_FFTabOrderPageWidgetIterator() {}
241
242void CXFA_FFTabOrderPageWidgetIterator::Reset() {
243  CreateTabOrderWidgetArray();
244  m_iCurWidget = -1;
245}
246
247CXFA_FFWidget* CXFA_FFTabOrderPageWidgetIterator::MoveToFirst() {
248  for (int32_t i = 0;
249       i < pdfium::CollectionSize<int32_t>(m_TabOrderWidgetArray); i++) {
250    if (PageWidgetFilter(m_TabOrderWidgetArray[i], m_dwFilter, true,
251                         m_bIgnorerelevant)) {
252      m_iCurWidget = i;
253      return m_TabOrderWidgetArray[m_iCurWidget];
254    }
255  }
256  return nullptr;
257}
258
259CXFA_FFWidget* CXFA_FFTabOrderPageWidgetIterator::MoveToLast() {
260  for (int32_t i = pdfium::CollectionSize<int32_t>(m_TabOrderWidgetArray) - 1;
261       i >= 0; i--) {
262    if (PageWidgetFilter(m_TabOrderWidgetArray[i], m_dwFilter, true,
263                         m_bIgnorerelevant)) {
264      m_iCurWidget = i;
265      return m_TabOrderWidgetArray[m_iCurWidget];
266    }
267  }
268  return nullptr;
269}
270
271CXFA_FFWidget* CXFA_FFTabOrderPageWidgetIterator::MoveToNext() {
272  for (int32_t i = m_iCurWidget + 1;
273       i < pdfium::CollectionSize<int32_t>(m_TabOrderWidgetArray); i++) {
274    if (PageWidgetFilter(m_TabOrderWidgetArray[i], m_dwFilter, true,
275                         m_bIgnorerelevant)) {
276      m_iCurWidget = i;
277      return m_TabOrderWidgetArray[m_iCurWidget];
278    }
279  }
280  m_iCurWidget = -1;
281  return nullptr;
282}
283
284CXFA_FFWidget* CXFA_FFTabOrderPageWidgetIterator::MoveToPrevious() {
285  for (int32_t i = m_iCurWidget - 1; i >= 0; i--) {
286    if (PageWidgetFilter(m_TabOrderWidgetArray[i], m_dwFilter, true,
287                         m_bIgnorerelevant)) {
288      m_iCurWidget = i;
289      return m_TabOrderWidgetArray[m_iCurWidget];
290    }
291  }
292  m_iCurWidget = -1;
293  return nullptr;
294}
295
296CXFA_FFWidget* CXFA_FFTabOrderPageWidgetIterator::GetCurrentWidget() {
297  return m_iCurWidget >= 0 ? m_TabOrderWidgetArray[m_iCurWidget] : nullptr;
298}
299
300bool CXFA_FFTabOrderPageWidgetIterator::SetCurrentWidget(
301    CXFA_FFWidget* hWidget) {
302  auto it = std::find(m_TabOrderWidgetArray.begin(),
303                      m_TabOrderWidgetArray.end(), hWidget);
304  if (it == m_TabOrderWidgetArray.end())
305    return false;
306
307  m_iCurWidget = it - m_TabOrderWidgetArray.begin();
308  return true;
309}
310
311CXFA_FFWidget* CXFA_FFTabOrderPageWidgetIterator::GetTraverseWidget(
312    CXFA_FFWidget* pWidget) {
313  CXFA_Traversal* pTraversal = pWidget->GetNode()->GetChild<CXFA_Traversal>(
314      0, XFA_Element::Traversal, false);
315  if (pTraversal) {
316    CXFA_Traverse* pTraverse =
317        pTraversal->GetChild<CXFA_Traverse>(0, XFA_Element::Traverse, false);
318    if (pTraverse) {
319      Optional<WideString> traverseWidgetName =
320          pTraverse->JSObject()->TryAttribute(XFA_Attribute::Ref, true);
321      if (traverseWidgetName)
322        return FindWidgetByName(*traverseWidgetName, pWidget);
323    }
324  }
325  return nullptr;
326}
327CXFA_FFWidget* CXFA_FFTabOrderPageWidgetIterator::FindWidgetByName(
328    const WideString& wsWidgetName,
329    CXFA_FFWidget* pRefWidget) {
330  return pRefWidget->GetDocView()->GetWidgetByName(wsWidgetName, pRefWidget);
331}
332
333void CXFA_FFTabOrderPageWidgetIterator::CreateTabOrderWidgetArray() {
334  m_TabOrderWidgetArray.clear();
335
336  std::vector<CXFA_FFWidget*> SpaceOrderWidgetArray;
337  CreateSpaceOrderWidgetArray(&SpaceOrderWidgetArray);
338  if (SpaceOrderWidgetArray.empty())
339    return;
340
341  int32_t nWidgetCount = pdfium::CollectionSize<int32_t>(SpaceOrderWidgetArray);
342  CXFA_FFWidget* hWidget = SpaceOrderWidgetArray[0];
343  while (pdfium::CollectionSize<int32_t>(m_TabOrderWidgetArray) <
344         nWidgetCount) {
345    if (!pdfium::ContainsValue(m_TabOrderWidgetArray, hWidget)) {
346      m_TabOrderWidgetArray.push_back(hWidget);
347      CXFA_WidgetAcc* pWidgetAcc = hWidget->GetNode()->GetWidgetAcc();
348      if (pWidgetAcc->GetUIType() == XFA_Element::ExclGroup) {
349        auto it = std::find(SpaceOrderWidgetArray.begin(),
350                            SpaceOrderWidgetArray.end(), hWidget);
351        int32_t iWidgetIndex = it != SpaceOrderWidgetArray.end()
352                                   ? it - SpaceOrderWidgetArray.begin() + 1
353                                   : 0;
354        while (true) {
355          CXFA_FFWidget* radio =
356              SpaceOrderWidgetArray[iWidgetIndex % nWidgetCount];
357          if (radio->GetNode()->GetExclGroupIfExists() != pWidgetAcc->GetNode())
358            break;
359          if (!pdfium::ContainsValue(m_TabOrderWidgetArray, hWidget))
360            m_TabOrderWidgetArray.push_back(radio);
361
362          iWidgetIndex++;
363        }
364      }
365      if (CXFA_FFWidget* hTraverseWidget = GetTraverseWidget(hWidget)) {
366        hWidget = hTraverseWidget;
367        continue;
368      }
369    }
370    auto it = std::find(SpaceOrderWidgetArray.begin(),
371                        SpaceOrderWidgetArray.end(), hWidget);
372    int32_t iWidgetIndex = it != SpaceOrderWidgetArray.end()
373                               ? it - SpaceOrderWidgetArray.begin() + 1
374                               : 0;
375    hWidget = SpaceOrderWidgetArray[iWidgetIndex % nWidgetCount];
376  }
377}
378
379void CXFA_FFTabOrderPageWidgetIterator::OrderContainer(
380    CXFA_LayoutItemIterator* sIterator,
381    CXFA_LayoutItem* pContainerItem,
382    CXFA_TabParam* pContainer,
383    bool& bCurrentItem,
384    bool& bContentArea,
385    bool bMarsterPage) {
386  std::vector<std::unique_ptr<CXFA_TabParam>> tabParams;
387  CXFA_LayoutItem* pSearchItem = sIterator->MoveToNext();
388  while (pSearchItem) {
389    if (!pSearchItem->IsContentLayoutItem()) {
390      bContentArea = true;
391      pSearchItem = sIterator->MoveToNext();
392      continue;
393    }
394    if (bMarsterPage && bContentArea) {
395      break;
396    }
397    if (bMarsterPage || bContentArea) {
398      CXFA_FFWidget* hWidget = GetWidget(pSearchItem);
399      if (!hWidget) {
400        pSearchItem = sIterator->MoveToNext();
401        continue;
402      }
403      if (pContainerItem && (pSearchItem->GetParent() != pContainerItem)) {
404        bCurrentItem = true;
405        break;
406      }
407      tabParams.push_back(pdfium::MakeUnique<CXFA_TabParam>(hWidget));
408      if (IsLayoutElement(pSearchItem->GetFormNode()->GetElementType(), true)) {
409        OrderContainer(sIterator, pSearchItem, tabParams.back().get(),
410                       bCurrentItem, bContentArea, bMarsterPage);
411      }
412    }
413    if (bCurrentItem) {
414      pSearchItem = sIterator->GetCurrent();
415      bCurrentItem = false;
416    } else {
417      pSearchItem = sIterator->MoveToNext();
418    }
419  }
420  std::sort(tabParams.begin(), tabParams.end(),
421            [](const std::unique_ptr<CXFA_TabParam>& arg1,
422               const std::unique_ptr<CXFA_TabParam>& arg2) {
423              const CFX_RectF& rt1 = arg1->GetWidget()->GetWidgetRect();
424              const CFX_RectF& rt2 = arg2->GetWidget()->GetWidgetRect();
425              if (rt1.top - rt2.top >= XFA_FLOAT_PERCISION)
426                return rt1.top < rt2.top;
427              return rt1.left < rt2.left;
428            });
429  for (const auto& pParam : tabParams)
430    pContainer->AppendTabParam(pParam.get());
431}
432
433void CXFA_FFTabOrderPageWidgetIterator::CreateSpaceOrderWidgetArray(
434    std::vector<CXFA_FFWidget*>* WidgetArray) {
435  CXFA_LayoutItemIterator sIterator(m_pPageView);
436  auto pParam = pdfium::MakeUnique<CXFA_TabParam>(nullptr);
437  bool bCurrentItem = false;
438  bool bContentArea = false;
439  OrderContainer(&sIterator, nullptr, pParam.get(), bCurrentItem, bContentArea);
440  WidgetArray->insert(WidgetArray->end(), pParam->GetChildren().begin(),
441                      pParam->GetChildren().end());
442
443  sIterator.Reset();
444  bCurrentItem = false;
445  bContentArea = false;
446  pParam->ClearChildren();
447  OrderContainer(&sIterator, nullptr, pParam.get(), bCurrentItem, bContentArea,
448                 true);
449  WidgetArray->insert(WidgetArray->end(), pParam->GetChildren().begin(),
450                      pParam->GetChildren().end());
451}
452
453CXFA_FFWidget* CXFA_FFTabOrderPageWidgetIterator::GetWidget(
454    CXFA_LayoutItem* pLayoutItem) {
455  if (CXFA_FFWidget* pWidget = XFA_GetWidgetFromLayoutItem(pLayoutItem)) {
456    if (!pWidget->IsLoaded() &&
457        (pWidget->GetStatus() & XFA_WidgetStatus_Visible)) {
458      pWidget->LoadWidget();
459    }
460    return pWidget;
461  }
462  return nullptr;
463}
464
465CXFA_TabParam::CXFA_TabParam(CXFA_FFWidget* pWidget) : m_pWidget(pWidget) {}
466
467CXFA_TabParam::~CXFA_TabParam() {}
468