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