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