• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 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/cjs_global.h"
8 
9 #include <memory>
10 #include <utility>
11 #include <vector>
12 
13 #include "core/fxcrt/check.h"
14 #include "core/fxcrt/containers/contains.h"
15 #include "core/fxcrt/fx_extension.h"
16 #include "core/fxcrt/span.h"
17 #include "fxjs/cfx_globaldata.h"
18 #include "fxjs/cfx_keyvalue.h"
19 #include "fxjs/cjs_event_context.h"
20 #include "fxjs/cjs_object.h"
21 #include "fxjs/fxv8.h"
22 #include "fxjs/js_define.h"
23 #include "fxjs/js_resources.h"
24 #include "v8/include/v8-isolate.h"
25 
26 namespace {
27 
ByteStringFromV8Name(v8::Isolate * pIsolate,v8::Local<v8::Name> name)28 ByteString ByteStringFromV8Name(v8::Isolate* pIsolate,
29                                 v8::Local<v8::Name> name) {
30   CHECK(name->IsString());
31   return fxv8::ToByteString(pIsolate, name.As<v8::String>());
32 }
33 
34 }  // namespace
35 
36 CJS_Global::JSGlobalData::JSGlobalData() = default;
37 
38 CJS_Global::JSGlobalData::~JSGlobalData() = default;
39 
40 const JSMethodSpec CJS_Global::MethodSpecs[] = {
41     {"setPersistent", setPersistent_static}};
42 
43 uint32_t CJS_Global::ObjDefnID = 0;
44 
45 // static
setPersistent_static(const v8::FunctionCallbackInfo<v8::Value> & info)46 void CJS_Global::setPersistent_static(
47     const v8::FunctionCallbackInfo<v8::Value>& info) {
48   JSMethod<CJS_Global, &CJS_Global::setPersistent>("setPersistent", "global",
49                                                    info);
50 }
51 
52 // static
queryprop_static(v8::Local<v8::Name> property,const v8::PropertyCallbackInfo<v8::Integer> & info)53 v8::Intercepted CJS_Global::queryprop_static(
54     v8::Local<v8::Name> property,
55     const v8::PropertyCallbackInfo<v8::Integer>& info) {
56   auto pObj = JSGetObject<CJS_Global>(info.GetIsolate(), info.Holder());
57   if (!pObj) {
58     return v8::Intercepted::kNo;
59   }
60 
61   ByteString bsProp = ByteStringFromV8Name(info.GetIsolate(), property);
62   if (!pObj->HasProperty(bsProp)) {
63     return v8::Intercepted::kNo;
64   }
65 
66   info.GetReturnValue().Set(static_cast<int>(v8::PropertyAttribute::None));
67   return v8::Intercepted::kYes;
68 }
69 
70 // static
getprop_static(v8::Local<v8::Name> property,const v8::PropertyCallbackInfo<v8::Value> & info)71 v8::Intercepted CJS_Global::getprop_static(
72     v8::Local<v8::Name> property,
73     const v8::PropertyCallbackInfo<v8::Value>& info) {
74   auto pObj = JSGetObject<CJS_Global>(info.GetIsolate(), info.Holder());
75   if (!pObj) {
76     return v8::Intercepted::kNo;
77   }
78 
79   CJS_Runtime* pRuntime = pObj->GetRuntime();
80   if (!pRuntime) {
81     return v8::Intercepted::kNo;
82   }
83 
84   ByteString bsProp = ByteStringFromV8Name(info.GetIsolate(), property);
85   CJS_Result result = pObj->GetProperty(pRuntime, bsProp);
86   if (result.HasError()) {
87     pRuntime->Error(
88         JSFormatErrorString("global", "GetProperty", result.Error()));
89     return v8::Intercepted::kYes;
90   }
91   if (!result.HasReturn()) {
92     return v8::Intercepted::kNo;
93   }
94 
95   info.GetReturnValue().Set(result.Return());
96   return v8::Intercepted::kYes;
97 }
98 
99 // static
putprop_static(v8::Local<v8::Name> property,v8::Local<v8::Value> value,const v8::PropertyCallbackInfo<void> & info)100 v8::Intercepted CJS_Global::putprop_static(
101     v8::Local<v8::Name> property,
102     v8::Local<v8::Value> value,
103     const v8::PropertyCallbackInfo<void>& info) {
104   auto pObj = JSGetObject<CJS_Global>(info.GetIsolate(), info.Holder());
105   if (!pObj) {
106     return v8::Intercepted::kNo;
107   }
108 
109   CJS_Runtime* pRuntime = pObj->GetRuntime();
110   if (!pRuntime) {
111     return v8::Intercepted::kNo;
112   }
113 
114   ByteString bsProp = ByteStringFromV8Name(info.GetIsolate(), property);
115   CJS_Result result = pObj->SetProperty(pRuntime, bsProp, value);
116   if (result.HasError()) {
117     pRuntime->Error(
118         JSFormatErrorString("global", "PutProperty", result.Error()));
119     return v8::Intercepted::kYes;
120   }
121   return v8::Intercepted::kYes;
122 }
123 
124 // static
delprop_static(v8::Local<v8::Name> property,const v8::PropertyCallbackInfo<v8::Boolean> & info)125 v8::Intercepted CJS_Global::delprop_static(
126     v8::Local<v8::Name> property,
127     const v8::PropertyCallbackInfo<v8::Boolean>& info) {
128   auto pObj = JSGetObject<CJS_Global>(info.GetIsolate(), info.Holder());
129   if (!pObj) {
130     return v8::Intercepted::kNo;
131   }
132 
133   ByteString bsProp = ByteStringFromV8Name(info.GetIsolate(), property);
134   if (!pObj->DelProperty(bsProp)) {
135     return v8::Intercepted::kNo;
136   }
137 
138   info.GetReturnValue().Set(true);
139   return v8::Intercepted::kYes;
140 }
141 
enumprop_static(const v8::PropertyCallbackInfo<v8::Array> & info)142 void CJS_Global::enumprop_static(
143     const v8::PropertyCallbackInfo<v8::Array>& info) {
144   auto pObj = JSGetObject<CJS_Global>(info.GetIsolate(), info.Holder());
145   if (!pObj)
146     return;
147 
148   CJS_Runtime* pRuntime = pObj->GetRuntime();
149   if (!pRuntime)
150     return;
151 
152   pObj->EnumProperties(pRuntime, info);
153 }
154 
155 // static
DefineAllProperties(CFXJS_Engine * pEngine)156 void CJS_Global::DefineAllProperties(CFXJS_Engine* pEngine) {
157   pEngine->DefineObjAllProperties(
158       ObjDefnID, CJS_Global::queryprop_static, CJS_Global::getprop_static,
159       CJS_Global::putprop_static, CJS_Global::delprop_static,
160       CJS_Global::enumprop_static);
161 }
162 
163 // static
GetObjDefnID()164 uint32_t CJS_Global::GetObjDefnID() {
165   return ObjDefnID;
166 }
167 
168 // static
DefineJSObjects(CFXJS_Engine * pEngine)169 void CJS_Global::DefineJSObjects(CFXJS_Engine* pEngine) {
170   ObjDefnID = pEngine->DefineObj("global", FXJSOBJTYPE_STATIC,
171                                  JSConstructor<CJS_Global>, JSDestructor);
172   DefineMethods(pEngine, ObjDefnID, MethodSpecs);
173   DefineAllProperties(pEngine);
174 }
175 
CJS_Global(v8::Local<v8::Object> pObject,CJS_Runtime * pRuntime)176 CJS_Global::CJS_Global(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime)
177     : CJS_Object(pObject, pRuntime),
178       m_pGlobalData(CFX_GlobalData::GetRetainedInstance(nullptr)) {
179   UpdateGlobalPersistentVariables();
180 }
181 
~CJS_Global()182 CJS_Global::~CJS_Global() {
183   DestroyGlobalPersisitentVariables();
184   m_pGlobalData.ExtractAsDangling()->Release();
185 }
186 
HasProperty(const ByteString & propname)187 bool CJS_Global::HasProperty(const ByteString& propname) {
188   return pdfium::Contains(m_MapGlobal, propname);
189 }
190 
DelProperty(const ByteString & propname)191 bool CJS_Global::DelProperty(const ByteString& propname) {
192   auto it = m_MapGlobal.find(propname);
193   if (it == m_MapGlobal.end())
194     return false;
195 
196   it->second->bDeleted = true;
197   return true;
198 }
199 
GetProperty(CJS_Runtime * pRuntime,const ByteString & propname)200 CJS_Result CJS_Global::GetProperty(CJS_Runtime* pRuntime,
201                                    const ByteString& propname) {
202   auto it = m_MapGlobal.find(propname);
203   if (it == m_MapGlobal.end())
204     return CJS_Result::Success();
205 
206   JSGlobalData* pData = it->second.get();
207   if (pData->bDeleted)
208     return CJS_Result::Success();
209 
210   switch (pData->nType) {
211     case CFX_Value::DataType::kNumber:
212       return CJS_Result::Success(pRuntime->NewNumber(pData->dData));
213     case CFX_Value::DataType::kBoolean:
214       return CJS_Result::Success(pRuntime->NewBoolean(pData->bData));
215     case CFX_Value::DataType::kString:
216       return CJS_Result::Success(
217           pRuntime->NewString(pData->sData.AsStringView()));
218     case CFX_Value::DataType::kObject:
219       return CJS_Result::Success(
220           v8::Local<v8::Object>::New(pRuntime->GetIsolate(), pData->pData));
221     case CFX_Value::DataType::kNull:
222       return CJS_Result::Success(pRuntime->NewNull());
223   }
224 }
225 
SetProperty(CJS_Runtime * pRuntime,const ByteString & propname,v8::Local<v8::Value> vp)226 CJS_Result CJS_Global::SetProperty(CJS_Runtime* pRuntime,
227                                    const ByteString& propname,
228                                    v8::Local<v8::Value> vp) {
229   if (vp->IsNumber()) {
230     return SetGlobalVariables(propname, CFX_Value::DataType::kNumber,
231                               pRuntime->ToDouble(vp), false, ByteString(),
232                               v8::Local<v8::Object>(), false);
233   }
234   if (vp->IsBoolean()) {
235     return SetGlobalVariables(propname, CFX_Value::DataType::kBoolean, 0,
236                               pRuntime->ToBoolean(vp), ByteString(),
237                               v8::Local<v8::Object>(), false);
238   }
239   if (vp->IsString()) {
240     return SetGlobalVariables(propname, CFX_Value::DataType::kString, 0, false,
241                               pRuntime->ToByteString(vp),
242                               v8::Local<v8::Object>(), false);
243   }
244   if (vp->IsObject()) {
245     return SetGlobalVariables(propname, CFX_Value::DataType::kObject, 0, false,
246                               ByteString(), pRuntime->ToObject(vp), false);
247   }
248   if (vp->IsNull()) {
249     return SetGlobalVariables(propname, CFX_Value::DataType::kNull, 0, false,
250                               ByteString(), v8::Local<v8::Object>(), false);
251   }
252   if (vp->IsUndefined()) {
253     DelProperty(propname);
254     return CJS_Result::Success();
255   }
256   return CJS_Result::Failure(JSMessage::kObjectTypeError);
257 }
258 
EnumProperties(CJS_Runtime * pRuntime,const v8::PropertyCallbackInfo<v8::Array> & info)259 void CJS_Global::EnumProperties(
260     CJS_Runtime* pRuntime,
261     const v8::PropertyCallbackInfo<v8::Array>& info) {
262   v8::Local<v8::Array> result = pRuntime->NewArray();
263   int idx = 0;
264   for (const auto& it : m_MapGlobal) {
265     if (it.second->bDeleted)
266       continue;
267     v8::Local<v8::Name> name = pRuntime->NewString(it.first.AsStringView());
268     pRuntime->PutArrayElement(result, idx, name);
269     ++idx;
270   }
271   info.GetReturnValue().Set(result);
272 }
273 
setPersistent(CJS_Runtime * pRuntime,pdfium::span<v8::Local<v8::Value>> params)274 CJS_Result CJS_Global::setPersistent(
275     CJS_Runtime* pRuntime,
276     pdfium::span<v8::Local<v8::Value>> params) {
277   if (params.size() != 2)
278     return CJS_Result::Failure(JSMessage::kParamError);
279 
280   auto it = m_MapGlobal.find(pRuntime->ToByteString(params[0]));
281   if (it == m_MapGlobal.end() || it->second->bDeleted)
282     return CJS_Result::Failure(JSMessage::kGlobalNotFoundError);
283 
284   it->second->bPersistent = pRuntime->ToBoolean(params[1]);
285   return CJS_Result::Success();
286 }
287 
UpdateGlobalPersistentVariables()288 void CJS_Global::UpdateGlobalPersistentVariables() {
289   CJS_Runtime* pRuntime = GetRuntime();
290   if (!pRuntime)
291     return;
292 
293   for (int i = 0, sz = m_pGlobalData->GetSize(); i < sz; i++) {
294     CFX_GlobalData::Element* pData = m_pGlobalData->GetAt(i);
295     switch (pData->data.nType) {
296       case CFX_Value::DataType::kNumber:
297         SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::kNumber,
298                            pData->data.dData, false, ByteString(),
299                            v8::Local<v8::Object>(), pData->bPersistent);
300         pRuntime->PutObjectProperty(ToV8Object(),
301                                     pData->data.sKey.AsStringView(),
302                                     pRuntime->NewNumber(pData->data.dData));
303         break;
304       case CFX_Value::DataType::kBoolean:
305         SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::kBoolean, 0,
306                            pData->data.bData == 1, ByteString(),
307                            v8::Local<v8::Object>(), pData->bPersistent);
308         pRuntime->PutObjectProperty(
309             ToV8Object(), pData->data.sKey.AsStringView(),
310             pRuntime->NewBoolean(pData->data.bData == 1));
311         break;
312       case CFX_Value::DataType::kString:
313         SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::kString, 0,
314                            false, pData->data.sData, v8::Local<v8::Object>(),
315                            pData->bPersistent);
316         pRuntime->PutObjectProperty(
317             ToV8Object(), pData->data.sKey.AsStringView(),
318             pRuntime->NewString(pData->data.sData.AsStringView()));
319         break;
320       case CFX_Value::DataType::kObject: {
321         v8::Local<v8::Object> pObj = pRuntime->NewObject();
322         if (!pObj.IsEmpty()) {
323           PutObjectProperty(pObj, &pData->data);
324           SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::kObject, 0,
325                              false, ByteString(), pObj, pData->bPersistent);
326           pRuntime->PutObjectProperty(ToV8Object(),
327                                       pData->data.sKey.AsStringView(), pObj);
328         }
329       } break;
330       case CFX_Value::DataType::kNull:
331         SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::kNull, 0,
332                            false, ByteString(), v8::Local<v8::Object>(),
333                            pData->bPersistent);
334         pRuntime->PutObjectProperty(
335             ToV8Object(), pData->data.sKey.AsStringView(), pRuntime->NewNull());
336         break;
337     }
338   }
339 }
340 
CommitGlobalPersisitentVariables()341 void CJS_Global::CommitGlobalPersisitentVariables() {
342   CJS_Runtime* pRuntime = GetRuntime();
343   if (!pRuntime)
344     return;
345 
346   for (const auto& iter : m_MapGlobal) {
347     ByteString name = iter.first;
348     JSGlobalData* pData = iter.second.get();
349     if (pData->bDeleted) {
350       m_pGlobalData->DeleteGlobalVariable(name);
351       continue;
352     }
353     switch (pData->nType) {
354       case CFX_Value::DataType::kNumber:
355         m_pGlobalData->SetGlobalVariableNumber(name, pData->dData);
356         m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
357         break;
358       case CFX_Value::DataType::kBoolean:
359         m_pGlobalData->SetGlobalVariableBoolean(name, pData->bData);
360         m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
361         break;
362       case CFX_Value::DataType::kString:
363         m_pGlobalData->SetGlobalVariableString(name, pData->sData);
364         m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
365         break;
366       case CFX_Value::DataType::kObject: {
367         v8::Local<v8::Object> obj =
368             v8::Local<v8::Object>::New(pRuntime->GetIsolate(), pData->pData);
369         m_pGlobalData->SetGlobalVariableObject(name,
370                                                ObjectToArray(pRuntime, obj));
371         m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
372       } break;
373       case CFX_Value::DataType::kNull:
374         m_pGlobalData->SetGlobalVariableNull(name);
375         m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
376         break;
377     }
378   }
379 }
380 
ObjectToArray(CJS_Runtime * pRuntime,v8::Local<v8::Object> pObj)381 std::vector<std::unique_ptr<CFX_KeyValue>> CJS_Global::ObjectToArray(
382     CJS_Runtime* pRuntime,
383     v8::Local<v8::Object> pObj) {
384   std::vector<std::unique_ptr<CFX_KeyValue>> array;
385   std::vector<WideString> pKeyList = pRuntime->GetObjectPropertyNames(pObj);
386   for (const auto& ws : pKeyList) {
387     ByteString sKey = ws.ToUTF8();
388     v8::Local<v8::Value> v =
389         pRuntime->GetObjectProperty(pObj, sKey.AsStringView());
390     if (v->IsNumber()) {
391       auto pObjElement = std::make_unique<CFX_KeyValue>();
392       pObjElement->nType = CFX_Value::DataType::kNumber;
393       pObjElement->sKey = sKey;
394       pObjElement->dData = pRuntime->ToDouble(v);
395       array.push_back(std::move(pObjElement));
396       continue;
397     }
398     if (v->IsBoolean()) {
399       auto pObjElement = std::make_unique<CFX_KeyValue>();
400       pObjElement->nType = CFX_Value::DataType::kBoolean;
401       pObjElement->sKey = sKey;
402       pObjElement->dData = pRuntime->ToBoolean(v);
403       array.push_back(std::move(pObjElement));
404       continue;
405     }
406     if (v->IsString()) {
407       ByteString sValue = pRuntime->ToByteString(v);
408       auto pObjElement = std::make_unique<CFX_KeyValue>();
409       pObjElement->nType = CFX_Value::DataType::kString;
410       pObjElement->sKey = sKey;
411       pObjElement->sData = sValue;
412       array.push_back(std::move(pObjElement));
413       continue;
414     }
415     if (v->IsObject()) {
416       auto pObjElement = std::make_unique<CFX_KeyValue>();
417       pObjElement->nType = CFX_Value::DataType::kObject;
418       pObjElement->sKey = sKey;
419       pObjElement->objData = ObjectToArray(pRuntime, pRuntime->ToObject(v));
420       array.push_back(std::move(pObjElement));
421       continue;
422     }
423     if (v->IsNull()) {
424       auto pObjElement = std::make_unique<CFX_KeyValue>();
425       pObjElement->nType = CFX_Value::DataType::kNull;
426       pObjElement->sKey = sKey;
427       array.push_back(std::move(pObjElement));
428     }
429   }
430   return array;
431 }
432 
PutObjectProperty(v8::Local<v8::Object> pObj,CFX_KeyValue * pData)433 void CJS_Global::PutObjectProperty(v8::Local<v8::Object> pObj,
434                                    CFX_KeyValue* pData) {
435   CJS_Runtime* pRuntime = GetRuntime();
436   if (pRuntime)
437     return;
438 
439   for (size_t i = 0; i < pData->objData.size(); ++i) {
440     CFX_KeyValue* pObjData = pData->objData.at(i).get();
441     switch (pObjData->nType) {
442       case CFX_Value::DataType::kNumber:
443         pRuntime->PutObjectProperty(pObj, pObjData->sKey.AsStringView(),
444                                     pRuntime->NewNumber(pObjData->dData));
445         break;
446       case CFX_Value::DataType::kBoolean:
447         pRuntime->PutObjectProperty(pObj, pObjData->sKey.AsStringView(),
448                                     pRuntime->NewBoolean(pObjData->bData == 1));
449         break;
450       case CFX_Value::DataType::kString:
451         pRuntime->PutObjectProperty(
452             pObj, pObjData->sKey.AsStringView(),
453             pRuntime->NewString(pObjData->sData.AsStringView()));
454         break;
455       case CFX_Value::DataType::kObject: {
456         v8::Local<v8::Object> pNewObj = pRuntime->NewObject();
457         if (!pNewObj.IsEmpty()) {
458           PutObjectProperty(pNewObj, pObjData);
459           pRuntime->PutObjectProperty(pObj, pObjData->sKey.AsStringView(),
460                                       pNewObj);
461         }
462       } break;
463       case CFX_Value::DataType::kNull:
464         pRuntime->PutObjectProperty(pObj, pObjData->sKey.AsStringView(),
465                                     pRuntime->NewNull());
466         break;
467     }
468   }
469 }
470 
DestroyGlobalPersisitentVariables()471 void CJS_Global::DestroyGlobalPersisitentVariables() {
472   m_MapGlobal.clear();
473 }
474 
SetGlobalVariables(const ByteString & propname,CFX_Value::DataType nType,double dData,bool bData,const ByteString & sData,v8::Local<v8::Object> pData,bool bDefaultPersistent)475 CJS_Result CJS_Global::SetGlobalVariables(const ByteString& propname,
476                                           CFX_Value::DataType nType,
477                                           double dData,
478                                           bool bData,
479                                           const ByteString& sData,
480                                           v8::Local<v8::Object> pData,
481                                           bool bDefaultPersistent) {
482   if (propname.IsEmpty())
483     return CJS_Result::Failure(JSMessage::kUnknownProperty);
484 
485   auto it = m_MapGlobal.find(propname);
486   if (it != m_MapGlobal.end()) {
487     JSGlobalData* pTemp = it->second.get();
488     if (pTemp->bDeleted || pTemp->nType != nType) {
489       pTemp->dData = 0;
490       pTemp->bData = false;
491       pTemp->sData.clear();
492       pTemp->nType = nType;
493     }
494     pTemp->bDeleted = false;
495     switch (nType) {
496       case CFX_Value::DataType::kNumber:
497         pTemp->dData = dData;
498         break;
499       case CFX_Value::DataType::kBoolean:
500         pTemp->bData = bData;
501         break;
502       case CFX_Value::DataType::kString:
503         pTemp->sData = sData;
504         break;
505       case CFX_Value::DataType::kObject:
506         pTemp->pData.Reset(pData->GetIsolate(), pData);
507         break;
508       case CFX_Value::DataType::kNull:
509         break;
510     }
511     return CJS_Result::Success();
512   }
513 
514   auto pNewData = std::make_unique<JSGlobalData>();
515   switch (nType) {
516     case CFX_Value::DataType::kNumber:
517       pNewData->nType = CFX_Value::DataType::kNumber;
518       pNewData->dData = dData;
519       pNewData->bPersistent = bDefaultPersistent;
520       break;
521     case CFX_Value::DataType::kBoolean:
522       pNewData->nType = CFX_Value::DataType::kBoolean;
523       pNewData->bData = bData;
524       pNewData->bPersistent = bDefaultPersistent;
525       break;
526     case CFX_Value::DataType::kString:
527       pNewData->nType = CFX_Value::DataType::kString;
528       pNewData->sData = sData;
529       pNewData->bPersistent = bDefaultPersistent;
530       break;
531     case CFX_Value::DataType::kObject:
532       pNewData->nType = CFX_Value::DataType::kObject;
533       pNewData->pData.Reset(pData->GetIsolate(), pData);
534       pNewData->bPersistent = bDefaultPersistent;
535       break;
536     case CFX_Value::DataType::kNull:
537       pNewData->nType = CFX_Value::DataType::kNull;
538       pNewData->bPersistent = bDefaultPersistent;
539       break;
540   }
541   m_MapGlobal[propname] = std::move(pNewData);
542   return CJS_Result::Success();
543 }
544