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