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