xref: /aosp_15_r20/external/pdfium/fxjs/xfa/cjx_layoutpseudomodel.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
1 // Copyright 2017 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 "fxjs/xfa/cjx_layoutpseudomodel.h"
8 
9 #include <set>
10 #include <utility>
11 
12 #include "core/fxcrt/fx_coordinates.h"
13 #include "fxjs/fxv8.h"
14 #include "fxjs/js_resources.h"
15 #include "fxjs/xfa/cfxjse_class.h"
16 #include "fxjs/xfa/cfxjse_engine.h"
17 #include "third_party/base/containers/contains.h"
18 #include "v8/include/cppgc/allocation.h"
19 #include "v8/include/v8-object.h"
20 #include "xfa/fxfa/cxfa_ffnotify.h"
21 #include "xfa/fxfa/layout/cxfa_contentlayoutitem.h"
22 #include "xfa/fxfa/layout/cxfa_layoutitem.h"
23 #include "xfa/fxfa/layout/cxfa_layoutprocessor.h"
24 #include "xfa/fxfa/layout/cxfa_traversestrategy_layoutitem.h"
25 #include "xfa/fxfa/layout/cxfa_viewlayoutitem.h"
26 #include "xfa/fxfa/parser/cscript_layoutpseudomodel.h"
27 #include "xfa/fxfa/parser/cxfa_arraynodelist.h"
28 #include "xfa/fxfa/parser/cxfa_document.h"
29 #include "xfa/fxfa/parser/cxfa_form.h"
30 #include "xfa/fxfa/parser/cxfa_measurement.h"
31 #include "xfa/fxfa/parser/cxfa_node.h"
32 #include "xfa/fxfa/parser/cxfa_nodeiteratortemplate.h"
33 
34 const CJX_MethodSpec CJX_LayoutPseudoModel::MethodSpecs[] = {
35     {"absPage", absPage_static},
36     {"absPageCount", absPageCount_static},
37     {"absPageCountInBatch", absPageCountInBatch_static},
38     {"absPageInBatch", absPageInBatch_static},
39     {"absPageSpan", absPageSpan_static},
40     {"h", h_static},
41     {"page", page_static},
42     {"pageContent", pageContent_static},
43     {"pageCount", pageCount_static},
44     {"pageSpan", pageSpan_static},
45     {"relayout", relayout_static},
46     {"relayoutPageArea", relayoutPageArea_static},
47     {"sheet", sheet_static},
48     {"sheetCount", sheetCount_static},
49     {"sheetCountInBatch", sheetCountInBatch_static},
50     {"sheetInBatch", sheetInBatch_static},
51     {"w", w_static},
52     {"x", x_static},
53     {"y", y_static}};
54 
CJX_LayoutPseudoModel(CScript_LayoutPseudoModel * model)55 CJX_LayoutPseudoModel::CJX_LayoutPseudoModel(CScript_LayoutPseudoModel* model)
56     : CJX_Object(model) {
57   DefineMethods(MethodSpecs);
58 }
59 
60 CJX_LayoutPseudoModel::~CJX_LayoutPseudoModel() = default;
61 
DynamicTypeIs(TypeTag eType) const62 bool CJX_LayoutPseudoModel::DynamicTypeIs(TypeTag eType) const {
63   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
64 }
65 
ready(v8::Isolate * pIsolate,v8::Local<v8::Value> * pValue,bool bSetting,XFA_Attribute eAttribute)66 void CJX_LayoutPseudoModel::ready(v8::Isolate* pIsolate,
67                                   v8::Local<v8::Value>* pValue,
68                                   bool bSetting,
69                                   XFA_Attribute eAttribute) {
70   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
71   if (!pNotify)
72     return;
73   if (bSetting) {
74     ThrowException(pIsolate,
75                    WideString::FromASCII("Unable to set ready value."));
76     return;
77   }
78 
79   CXFA_FFDocView::LayoutStatus iStatus = pNotify->GetLayoutStatus();
80   const bool bReady = iStatus != CXFA_FFDocView::LayoutStatus::kNone &&
81                       iStatus != CXFA_FFDocView::LayoutStatus::kStart;
82   *pValue = fxv8::NewBooleanHelper(pIsolate, bReady);
83 }
84 
DoHWXYInternal(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params,HWXY layoutModel)85 CJS_Result CJX_LayoutPseudoModel::DoHWXYInternal(
86     CFXJSE_Engine* runtime,
87     const std::vector<v8::Local<v8::Value>>& params,
88     HWXY layoutModel) {
89   if (params.empty() || params.size() > 3)
90     return CJS_Result::Failure(JSMessage::kParamError);
91 
92   CXFA_Node* pNode = ToNode(runtime->ToXFAObject(params[0]));
93   if (!pNode)
94     return CJS_Result::Success();
95 
96   WideString unit = WideString::FromASCII("pt");
97   if (params.size() >= 2) {
98     WideString tmp_unit = runtime->ToWideString(params[1]);
99     if (!tmp_unit.IsEmpty())
100       unit = std::move(tmp_unit);
101   }
102   int32_t iIndex = params.size() >= 3 ? runtime->ToInt32(params[2]) : 0;
103   auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(GetDocument());
104   CXFA_ContentLayoutItem* pLayoutItem =
105       ToContentLayoutItem(pDocLayout->GetLayoutItem(pNode));
106   if (!pLayoutItem)
107     return CJS_Result::Success();
108 
109   while (iIndex > 0 && pLayoutItem) {
110     pLayoutItem = pLayoutItem->GetNext();
111     --iIndex;
112   }
113 
114   if (!pLayoutItem)
115     return CJS_Result::Success(runtime->NewNumber(0.0));
116 
117   CXFA_Measurement measure;
118   CFX_RectF rtRect = pLayoutItem->GetRelativeRect();
119   switch (layoutModel) {
120     case HWXY::kH:
121       measure.Set(rtRect.height, XFA_Unit::Pt);
122       break;
123     case HWXY::kW:
124       measure.Set(rtRect.width, XFA_Unit::Pt);
125       break;
126     case HWXY::kX:
127       measure.Set(rtRect.left, XFA_Unit::Pt);
128       break;
129     case HWXY::kY:
130       measure.Set(rtRect.top, XFA_Unit::Pt);
131       break;
132   }
133 
134   XFA_Unit eUnit = CXFA_Measurement::GetUnitFromString(unit.AsStringView());
135   if (eUnit == XFA_Unit::Unknown)
136     return CJS_Result::Failure(JSMessage::kValueError);
137 
138   float fValue = measure.ToUnit(eUnit);
139   return CJS_Result::Success(
140       runtime->NewNumber(FXSYS_roundf(fValue * 1000) / 1000.0f));
141 }
142 
h(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)143 CJS_Result CJX_LayoutPseudoModel::h(
144     CFXJSE_Engine* runtime,
145     const std::vector<v8::Local<v8::Value>>& params) {
146   return DoHWXYInternal(runtime, params, HWXY::kH);
147 }
148 
w(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)149 CJS_Result CJX_LayoutPseudoModel::w(
150     CFXJSE_Engine* runtime,
151     const std::vector<v8::Local<v8::Value>>& params) {
152   return DoHWXYInternal(runtime, params, HWXY::kW);
153 }
154 
x(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)155 CJS_Result CJX_LayoutPseudoModel::x(
156     CFXJSE_Engine* runtime,
157     const std::vector<v8::Local<v8::Value>>& params) {
158   return DoHWXYInternal(runtime, params, HWXY::kX);
159 }
160 
y(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)161 CJS_Result CJX_LayoutPseudoModel::y(
162     CFXJSE_Engine* runtime,
163     const std::vector<v8::Local<v8::Value>>& params) {
164   return DoHWXYInternal(runtime, params, HWXY::kY);
165 }
166 
AllPageCount(CFXJSE_Engine * runtime)167 CJS_Result CJX_LayoutPseudoModel::AllPageCount(CFXJSE_Engine* runtime) {
168   auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(GetDocument());
169   return CJS_Result::Success(runtime->NewNumber(pDocLayout->CountPages()));
170 }
171 
NumberedPageCount(CFXJSE_Engine * runtime)172 CJS_Result CJX_LayoutPseudoModel::NumberedPageCount(CFXJSE_Engine* runtime) {
173   auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(GetDocument());
174   int32_t iPageCount = 0;
175   int32_t iPageNum = pDocLayout->CountPages();
176   for (int32_t i = 0; i < iPageNum; i++) {
177     CXFA_ViewLayoutItem* pLayoutPage = pDocLayout->GetPage(i);
178     if (!pLayoutPage)
179       continue;
180 
181     CXFA_Node* pMasterPage = pLayoutPage->GetMasterPage();
182     if (pMasterPage->JSObject()->GetInteger(XFA_Attribute::Numbered))
183       iPageCount++;
184   }
185   return CJS_Result::Success(runtime->NewNumber(iPageCount));
186 }
187 
pageCount(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)188 CJS_Result CJX_LayoutPseudoModel::pageCount(
189     CFXJSE_Engine* runtime,
190     const std::vector<v8::Local<v8::Value>>& params) {
191   return NumberedPageCount(runtime);
192 }
193 
pageSpan(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)194 CJS_Result CJX_LayoutPseudoModel::pageSpan(
195     CFXJSE_Engine* runtime,
196     const std::vector<v8::Local<v8::Value>>& params) {
197   if (params.size() != 1)
198     return CJS_Result::Failure(JSMessage::kParamError);
199 
200   CXFA_Node* pNode = ToNode(runtime->ToXFAObject(params[0]));
201   if (!pNode)
202     return CJS_Result::Success();
203 
204   auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(GetDocument());
205   CXFA_ContentLayoutItem* pLayoutItem =
206       ToContentLayoutItem(pDocLayout->GetLayoutItem(pNode));
207   if (!pLayoutItem)
208     return CJS_Result::Success(runtime->NewNumber(-1));
209 
210   int32_t iLast = pLayoutItem->GetLast()->GetPage()->GetPageIndex();
211   int32_t iFirst = pLayoutItem->GetFirst()->GetPage()->GetPageIndex();
212   int32_t iPageSpan = iLast - iFirst + 1;
213   return CJS_Result::Success(runtime->NewNumber(iPageSpan));
214 }
215 
page(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)216 CJS_Result CJX_LayoutPseudoModel::page(
217     CFXJSE_Engine* runtime,
218     const std::vector<v8::Local<v8::Value>>& params) {
219   return PageInternals(runtime, params, false);
220 }
221 
GetObjArray(CXFA_LayoutProcessor * pDocLayout,int32_t iPageNo,const WideString & wsType,bool bOnPageArea)222 std::vector<CXFA_Node*> CJX_LayoutPseudoModel::GetObjArray(
223     CXFA_LayoutProcessor* pDocLayout,
224     int32_t iPageNo,
225     const WideString& wsType,
226     bool bOnPageArea) {
227   CXFA_ViewLayoutItem* pLayoutPage = pDocLayout->GetPage(iPageNo);
228   if (!pLayoutPage)
229     return std::vector<CXFA_Node*>();
230 
231   std::vector<CXFA_Node*> retArray;
232   if (wsType.EqualsASCII("pageArea")) {
233     if (pLayoutPage->GetFormNode())
234       retArray.push_back(pLayoutPage->GetFormNode());
235     return retArray;
236   }
237   if (wsType.EqualsASCII("contentArea")) {
238     for (CXFA_LayoutItem* pItem = pLayoutPage->GetFirstChild(); pItem;
239          pItem = pItem->GetNextSibling()) {
240       if (pItem->GetFormNode()->GetElementType() == XFA_Element::ContentArea)
241         retArray.push_back(pItem->GetFormNode());
242     }
243     return retArray;
244   }
245   std::set<CXFA_Node*> formItems;
246   if (wsType.IsEmpty()) {
247     if (pLayoutPage->GetFormNode())
248       retArray.push_back(pLayoutPage->GetFormNode());
249 
250     for (CXFA_LayoutItem* pItem = pLayoutPage->GetFirstChild(); pItem;
251          pItem = pItem->GetNextSibling()) {
252       if (pItem->GetFormNode()->GetElementType() == XFA_Element::ContentArea) {
253         retArray.push_back(pItem->GetFormNode());
254         if (!bOnPageArea) {
255           CXFA_LayoutItemIterator iterator(pItem->GetFirstChild());
256           for (CXFA_LayoutItem* pChild = iterator.GetCurrent(); pChild;
257                pChild = iterator.MoveToNext()) {
258             CXFA_ContentLayoutItem* pItemChild = pChild->AsContentLayoutItem();
259             if (!pItemChild)
260               continue;
261 
262             XFA_Element eType = pItemChild->GetFormNode()->GetElementType();
263             if (eType != XFA_Element::Field && eType != XFA_Element::Draw &&
264                 eType != XFA_Element::Subform && eType != XFA_Element::Area) {
265               continue;
266             }
267             if (pdfium::Contains(formItems, pItemChild->GetFormNode()))
268               continue;
269 
270             formItems.insert(pItemChild->GetFormNode());
271             retArray.push_back(pItemChild->GetFormNode());
272           }
273         }
274       } else {
275         if (bOnPageArea) {
276           CXFA_LayoutItemIterator iterator(pItem);
277           for (CXFA_LayoutItem* pChild = iterator.GetCurrent(); pChild;
278                pChild = iterator.MoveToNext()) {
279             CXFA_ContentLayoutItem* pItemChild = pChild->AsContentLayoutItem();
280             if (!pItemChild)
281               continue;
282 
283             XFA_Element eType = pItemChild->GetFormNode()->GetElementType();
284             if (eType != XFA_Element::Field && eType != XFA_Element::Draw &&
285                 eType != XFA_Element::Subform && eType != XFA_Element::Area) {
286               continue;
287             }
288             if (pdfium::Contains(formItems, pItemChild->GetFormNode()))
289               continue;
290 
291             formItems.insert(pItemChild->GetFormNode());
292             retArray.push_back(pItemChild->GetFormNode());
293           }
294         }
295       }
296     }
297     return retArray;
298   }
299 
300   XFA_Element eType = XFA_Element::Unknown;
301   if (wsType.EqualsASCII("field"))
302     eType = XFA_Element::Field;
303   else if (wsType.EqualsASCII("draw"))
304     eType = XFA_Element::Draw;
305   else if (wsType.EqualsASCII("subform"))
306     eType = XFA_Element::Subform;
307   else if (wsType.EqualsASCII("area"))
308     eType = XFA_Element::Area;
309 
310   if (eType != XFA_Element::Unknown) {
311     for (CXFA_LayoutItem* pItem = pLayoutPage->GetFirstChild(); pItem;
312          pItem = pItem->GetNextSibling()) {
313       if (pItem->GetFormNode()->GetElementType() == XFA_Element::ContentArea) {
314         if (!bOnPageArea) {
315           CXFA_LayoutItemIterator iterator(pItem->GetFirstChild());
316           for (CXFA_LayoutItem* pChild = iterator.GetCurrent(); pChild;
317                pChild = iterator.MoveToNext()) {
318             CXFA_ContentLayoutItem* pItemChild = pChild->AsContentLayoutItem();
319             if (!pItemChild)
320               continue;
321             if (pItemChild->GetFormNode()->GetElementType() != eType)
322               continue;
323             if (pdfium::Contains(formItems, pItemChild->GetFormNode()))
324               continue;
325 
326             formItems.insert(pItemChild->GetFormNode());
327             retArray.push_back(pItemChild->GetFormNode());
328           }
329         }
330       } else {
331         if (bOnPageArea) {
332           CXFA_LayoutItemIterator iterator(pItem);
333           for (CXFA_LayoutItem* pChild = iterator.GetCurrent(); pChild;
334                pChild = iterator.MoveToNext()) {
335             CXFA_ContentLayoutItem* pItemChild = pChild->AsContentLayoutItem();
336             if (!pItemChild)
337               continue;
338             if (pItemChild->GetFormNode()->GetElementType() != eType)
339               continue;
340             if (pdfium::Contains(formItems, pItemChild->GetFormNode()))
341               continue;
342 
343             formItems.insert(pItemChild->GetFormNode());
344             retArray.push_back(pItemChild->GetFormNode());
345           }
346         }
347       }
348     }
349   }
350   return retArray;
351 }
352 
pageContent(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)353 CJS_Result CJX_LayoutPseudoModel::pageContent(
354     CFXJSE_Engine* runtime,
355     const std::vector<v8::Local<v8::Value>>& params) {
356   if (params.empty() || params.size() > 3)
357     return CJS_Result::Failure(JSMessage::kParamError);
358 
359   int32_t iIndex = 0;
360   if (params.size() >= 1)
361     iIndex = runtime->ToInt32(params[0]);
362 
363   WideString wsType;
364   if (params.size() >= 2)
365     wsType = runtime->ToWideString(params[1]);
366 
367   bool bOnPageArea = false;
368   if (params.size() >= 3)
369     bOnPageArea = runtime->ToBoolean(params[2]);
370 
371   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
372   if (!pNotify)
373     return CJS_Result::Success();
374 
375   CXFA_Document* pDoc = GetDocument();
376   auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(pDoc);
377   auto* pArrayNodeList = cppgc::MakeGarbageCollected<CXFA_ArrayNodeList>(
378       pDoc->GetHeap()->GetAllocationHandle(), pDoc);
379   pDoc->GetNodeOwner()->PersistList(pArrayNodeList);
380   pArrayNodeList->SetArrayNodeList(
381       GetObjArray(pDocLayout, iIndex, wsType, bOnPageArea));
382   return CJS_Result::Success(runtime->NewNormalXFAObject(pArrayNodeList));
383 }
384 
absPageCount(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)385 CJS_Result CJX_LayoutPseudoModel::absPageCount(
386     CFXJSE_Engine* runtime,
387     const std::vector<v8::Local<v8::Value>>& params) {
388   return AllPageCount(runtime);
389 }
390 
absPageCountInBatch(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)391 CJS_Result CJX_LayoutPseudoModel::absPageCountInBatch(
392     CFXJSE_Engine* runtime,
393     const std::vector<v8::Local<v8::Value>>& params) {
394   return CJS_Result::Success(runtime->NewNumber(0));
395 }
396 
sheetCountInBatch(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)397 CJS_Result CJX_LayoutPseudoModel::sheetCountInBatch(
398     CFXJSE_Engine* runtime,
399     const std::vector<v8::Local<v8::Value>>& params) {
400   return CJS_Result::Success(runtime->NewNumber(0));
401 }
402 
relayout(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)403 CJS_Result CJX_LayoutPseudoModel::relayout(
404     CFXJSE_Engine* runtime,
405     const std::vector<v8::Local<v8::Value>>& params) {
406   CXFA_Node* pRootNode = GetDocument()->GetRoot();
407   auto* pLayoutProcessor = GetDocument()->GetLayoutProcessor();
408   CXFA_Form* pFormRoot =
409       pRootNode->GetFirstChildByClass<CXFA_Form>(XFA_Element::Form);
410   if (pFormRoot) {
411     if (pFormRoot->GetFirstChild())
412       pLayoutProcessor->SetHasChangedContainer();
413   }
414   pLayoutProcessor->SetForceRelayout();
415   return CJS_Result::Success();
416 }
417 
absPageSpan(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)418 CJS_Result CJX_LayoutPseudoModel::absPageSpan(
419     CFXJSE_Engine* runtime,
420     const std::vector<v8::Local<v8::Value>>& params) {
421   return pageSpan(runtime, params);
422 }
423 
absPageInBatch(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)424 CJS_Result CJX_LayoutPseudoModel::absPageInBatch(
425     CFXJSE_Engine* runtime,
426     const std::vector<v8::Local<v8::Value>>& params) {
427   if (params.size() != 1)
428     return CJS_Result::Failure(JSMessage::kParamError);
429 
430   return CJS_Result::Success(runtime->NewNumber(0));
431 }
432 
sheetInBatch(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)433 CJS_Result CJX_LayoutPseudoModel::sheetInBatch(
434     CFXJSE_Engine* runtime,
435     const std::vector<v8::Local<v8::Value>>& params) {
436   if (params.size() != 1)
437     return CJS_Result::Failure(JSMessage::kParamError);
438 
439   return CJS_Result::Success(runtime->NewNumber(0));
440 }
441 
sheet(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)442 CJS_Result CJX_LayoutPseudoModel::sheet(
443     CFXJSE_Engine* runtime,
444     const std::vector<v8::Local<v8::Value>>& params) {
445   return PageInternals(runtime, params, true);
446 }
447 
relayoutPageArea(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)448 CJS_Result CJX_LayoutPseudoModel::relayoutPageArea(
449     CFXJSE_Engine* runtime,
450     const std::vector<v8::Local<v8::Value>>& params) {
451   return CJS_Result::Success();
452 }
453 
sheetCount(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)454 CJS_Result CJX_LayoutPseudoModel::sheetCount(
455     CFXJSE_Engine* runtime,
456     const std::vector<v8::Local<v8::Value>>& params) {
457   return AllPageCount(runtime);
458 }
459 
absPage(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params)460 CJS_Result CJX_LayoutPseudoModel::absPage(
461     CFXJSE_Engine* runtime,
462     const std::vector<v8::Local<v8::Value>>& params) {
463   return PageInternals(runtime, params, true);
464 }
465 
PageInternals(CFXJSE_Engine * runtime,const std::vector<v8::Local<v8::Value>> & params,bool bAbsPage)466 CJS_Result CJX_LayoutPseudoModel::PageInternals(
467     CFXJSE_Engine* runtime,
468     const std::vector<v8::Local<v8::Value>>& params,
469     bool bAbsPage) {
470   if (params.size() != 1)
471     return CJS_Result::Failure(JSMessage::kParamError);
472 
473   CXFA_Node* pNode = ToNode(runtime->ToXFAObject(params[0]));
474   if (!pNode)
475     return CJS_Result::Success(runtime->NewNumber(0));
476 
477   auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(GetDocument());
478   CXFA_ContentLayoutItem* pLayoutItem =
479       ToContentLayoutItem(pDocLayout->GetLayoutItem(pNode));
480   if (!pLayoutItem)
481     return CJS_Result::Success(runtime->NewNumber(-1));
482 
483   int32_t iPage = pLayoutItem->GetFirst()->GetPage()->GetPageIndex();
484   return CJS_Result::Success(runtime->NewNumber(bAbsPage ? iPage : iPage + 1));
485 }
486