• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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