1 // Copyright 2014 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/fxjs_v8.h"
8
9 #include <vector>
10
11 #include "core/fxcrt/fx_basic.h"
12
13 // Keep this consistent with the values defined in gin/public/context_holder.h
14 // (without actually requiring a dependency on gin itself for the standalone
15 // embedders of PDFIum). The value we want to use is:
16 // kPerContextDataStartIndex + kEmbedderPDFium, which is 3.
17 static const unsigned int kPerContextDataIndex = 3u;
18 static unsigned int g_embedderDataSlot = 1u;
19 static v8::Isolate* g_isolate = nullptr;
20 static size_t g_isolate_ref_count = 0;
21 static FXJS_ArrayBufferAllocator* g_arrayBufferAllocator = nullptr;
22 static v8::Global<v8::ObjectTemplate>* g_DefaultGlobalObjectTemplate = nullptr;
23 static wchar_t kPerObjectDataTag[] = L"CFXJS_PerObjectData";
24
25 class CFXJS_PerObjectData {
26 public:
CFXJS_PerObjectData(int nObjDefID)27 explicit CFXJS_PerObjectData(int nObjDefID)
28 : m_ObjDefID(nObjDefID), m_pPrivate(nullptr) {}
29
SetInObject(CFXJS_PerObjectData * pData,v8::Local<v8::Object> pObj)30 static void SetInObject(CFXJS_PerObjectData* pData,
31 v8::Local<v8::Object> pObj) {
32 if (pObj->InternalFieldCount() == 2) {
33 pObj->SetAlignedPointerInInternalField(0, pData);
34 pObj->SetAlignedPointerInInternalField(
35 1, static_cast<void*>(kPerObjectDataTag));
36 }
37 }
38
GetFromObject(v8::Local<v8::Object> pObj)39 static CFXJS_PerObjectData* GetFromObject(v8::Local<v8::Object> pObj) {
40 if (pObj.IsEmpty() || pObj->InternalFieldCount() != 2 ||
41 pObj->GetAlignedPointerFromInternalField(1) !=
42 static_cast<void*>(kPerObjectDataTag)) {
43 return nullptr;
44 }
45 return static_cast<CFXJS_PerObjectData*>(
46 pObj->GetAlignedPointerFromInternalField(0));
47 }
48
49 const int m_ObjDefID;
50 void* m_pPrivate;
51 };
52
53 class CFXJS_ObjDefinition {
54 public:
MaxID(v8::Isolate * pIsolate)55 static int MaxID(v8::Isolate* pIsolate) {
56 return FXJS_PerIsolateData::Get(pIsolate)->m_ObjectDefnArray.size();
57 }
58
ForID(v8::Isolate * pIsolate,int id)59 static CFXJS_ObjDefinition* ForID(v8::Isolate* pIsolate, int id) {
60 // Note: GetAt() halts if out-of-range even in release builds.
61 return FXJS_PerIsolateData::Get(pIsolate)->m_ObjectDefnArray[id].get();
62 }
63
CFXJS_ObjDefinition(v8::Isolate * isolate,const char * sObjName,FXJSOBJTYPE eObjType,CFXJS_Engine::Constructor pConstructor,CFXJS_Engine::Destructor pDestructor)64 CFXJS_ObjDefinition(v8::Isolate* isolate,
65 const char* sObjName,
66 FXJSOBJTYPE eObjType,
67 CFXJS_Engine::Constructor pConstructor,
68 CFXJS_Engine::Destructor pDestructor)
69 : m_ObjName(sObjName),
70 m_ObjType(eObjType),
71 m_pConstructor(pConstructor),
72 m_pDestructor(pDestructor),
73 m_pIsolate(isolate) {
74 v8::Isolate::Scope isolate_scope(isolate);
75 v8::HandleScope handle_scope(isolate);
76
77 v8::Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(isolate);
78 fun->InstanceTemplate()->SetInternalFieldCount(2);
79 fun->SetCallHandler([](const v8::FunctionCallbackInfo<v8::Value>& info) {
80 v8::Local<v8::Object> holder = info.Holder();
81 ASSERT(holder->InternalFieldCount() == 2);
82 holder->SetAlignedPointerInInternalField(0, nullptr);
83 holder->SetAlignedPointerInInternalField(1, nullptr);
84 });
85 if (eObjType == FXJSOBJTYPE_GLOBAL) {
86 fun->InstanceTemplate()->Set(
87 v8::Symbol::GetToStringTag(isolate),
88 v8::String::NewFromUtf8(isolate, "global", v8::NewStringType::kNormal)
89 .ToLocalChecked());
90 }
91 m_FunctionTemplate.Reset(isolate, fun);
92
93 v8::Local<v8::Signature> sig = v8::Signature::New(isolate, fun);
94 m_Signature.Reset(isolate, sig);
95 }
96
AssignID()97 int AssignID() {
98 FXJS_PerIsolateData* pData = FXJS_PerIsolateData::Get(m_pIsolate);
99 pData->m_ObjectDefnArray.emplace_back(this);
100 return pData->m_ObjectDefnArray.size() - 1;
101 }
102
GetInstanceTemplate()103 v8::Local<v8::ObjectTemplate> GetInstanceTemplate() {
104 v8::EscapableHandleScope scope(m_pIsolate);
105 v8::Local<v8::FunctionTemplate> function =
106 m_FunctionTemplate.Get(m_pIsolate);
107 return scope.Escape(function->InstanceTemplate());
108 }
109
GetSignature()110 v8::Local<v8::Signature> GetSignature() {
111 v8::EscapableHandleScope scope(m_pIsolate);
112 return scope.Escape(m_Signature.Get(m_pIsolate));
113 }
114
115 const char* const m_ObjName;
116 const FXJSOBJTYPE m_ObjType;
117 const CFXJS_Engine::Constructor m_pConstructor;
118 const CFXJS_Engine::Destructor m_pDestructor;
119
120 v8::Isolate* m_pIsolate;
121 v8::Global<v8::FunctionTemplate> m_FunctionTemplate;
122 v8::Global<v8::Signature> m_Signature;
123 };
124
GetGlobalObjectTemplate(v8::Isolate * pIsolate)125 static v8::Local<v8::ObjectTemplate> GetGlobalObjectTemplate(
126 v8::Isolate* pIsolate) {
127 int maxID = CFXJS_ObjDefinition::MaxID(pIsolate);
128 for (int i = 0; i < maxID; ++i) {
129 CFXJS_ObjDefinition* pObjDef = CFXJS_ObjDefinition::ForID(pIsolate, i);
130 if (pObjDef->m_ObjType == FXJSOBJTYPE_GLOBAL)
131 return pObjDef->GetInstanceTemplate();
132 }
133 if (!g_DefaultGlobalObjectTemplate) {
134 v8::Local<v8::ObjectTemplate> hGlobalTemplate =
135 v8::ObjectTemplate::New(pIsolate);
136 hGlobalTemplate->Set(
137 v8::Symbol::GetToStringTag(pIsolate),
138 v8::String::NewFromUtf8(pIsolate, "global", v8::NewStringType::kNormal)
139 .ToLocalChecked());
140 g_DefaultGlobalObjectTemplate =
141 new v8::Global<v8::ObjectTemplate>(pIsolate, hGlobalTemplate);
142 }
143 return g_DefaultGlobalObjectTemplate->Get(pIsolate);
144 }
145
Allocate(size_t length)146 void* FXJS_ArrayBufferAllocator::Allocate(size_t length) {
147 return calloc(1, length);
148 }
149
AllocateUninitialized(size_t length)150 void* FXJS_ArrayBufferAllocator::AllocateUninitialized(size_t length) {
151 return malloc(length);
152 }
153
Free(void * data,size_t length)154 void FXJS_ArrayBufferAllocator::Free(void* data, size_t length) {
155 free(data);
156 }
157
Dispose(v8::Isolate * isolate,v8::Global<v8::Object> value,void * key)158 void V8TemplateMapTraits::Dispose(v8::Isolate* isolate,
159 v8::Global<v8::Object> value,
160 void* key) {
161 v8::Local<v8::Object> obj = value.Get(isolate);
162 if (obj.IsEmpty())
163 return;
164 CFXJS_Engine* pEngine = CFXJS_Engine::CurrentEngineFromIsolate(isolate);
165 int id = pEngine->GetObjDefnID(obj);
166 if (id == -1)
167 return;
168 CFXJS_ObjDefinition* pObjDef = CFXJS_ObjDefinition::ForID(isolate, id);
169 if (!pObjDef)
170 return;
171 if (pObjDef->m_pDestructor)
172 pObjDef->m_pDestructor(pEngine, obj);
173 CFXJS_Engine::FreeObjectPrivate(obj);
174 }
175
MapFromWeakCallbackInfo(const v8::WeakCallbackInfo<WeakCallbackDataType> & data)176 V8TemplateMapTraits::MapType* V8TemplateMapTraits::MapFromWeakCallbackInfo(
177 const v8::WeakCallbackInfo<WeakCallbackDataType>& data) {
178 V8TemplateMap* pMap =
179 (FXJS_PerIsolateData::Get(data.GetIsolate()))->m_pDynamicObjsMap.get();
180 return pMap ? &pMap->m_map : nullptr;
181 }
182
FXJS_Initialize(unsigned int embedderDataSlot,v8::Isolate * pIsolate)183 void FXJS_Initialize(unsigned int embedderDataSlot, v8::Isolate* pIsolate) {
184 if (g_isolate) {
185 ASSERT(g_embedderDataSlot == embedderDataSlot);
186 ASSERT(g_isolate == pIsolate);
187 return;
188 }
189 g_embedderDataSlot = embedderDataSlot;
190 g_isolate = pIsolate;
191 }
192
FXJS_Release()193 void FXJS_Release() {
194 ASSERT(!g_isolate || g_isolate_ref_count == 0);
195 delete g_DefaultGlobalObjectTemplate;
196 g_DefaultGlobalObjectTemplate = nullptr;
197 g_isolate = nullptr;
198
199 delete g_arrayBufferAllocator;
200 g_arrayBufferAllocator = nullptr;
201 }
202
FXJS_GetIsolate(v8::Isolate ** pResultIsolate)203 bool FXJS_GetIsolate(v8::Isolate** pResultIsolate) {
204 if (g_isolate) {
205 *pResultIsolate = g_isolate;
206 return false;
207 }
208 // Provide backwards compatibility when no external isolate.
209 if (!g_arrayBufferAllocator)
210 g_arrayBufferAllocator = new FXJS_ArrayBufferAllocator();
211 v8::Isolate::CreateParams params;
212 params.array_buffer_allocator = g_arrayBufferAllocator;
213 *pResultIsolate = v8::Isolate::New(params);
214 return true;
215 }
216
FXJS_GlobalIsolateRefCount()217 size_t FXJS_GlobalIsolateRefCount() {
218 return g_isolate_ref_count;
219 }
220
V8TemplateMap(v8::Isolate * isolate)221 V8TemplateMap::V8TemplateMap(v8::Isolate* isolate) : m_map(isolate) {}
222
~V8TemplateMap()223 V8TemplateMap::~V8TemplateMap() {}
224
set(void * key,v8::Local<v8::Object> handle)225 void V8TemplateMap::set(void* key, v8::Local<v8::Object> handle) {
226 ASSERT(!m_map.Contains(key));
227 m_map.Set(key, handle);
228 }
229
~FXJS_PerIsolateData()230 FXJS_PerIsolateData::~FXJS_PerIsolateData() {}
231
232 // static
SetUp(v8::Isolate * pIsolate)233 void FXJS_PerIsolateData::SetUp(v8::Isolate* pIsolate) {
234 if (!pIsolate->GetData(g_embedderDataSlot))
235 pIsolate->SetData(g_embedderDataSlot, new FXJS_PerIsolateData(pIsolate));
236 }
237
238 // static
Get(v8::Isolate * pIsolate)239 FXJS_PerIsolateData* FXJS_PerIsolateData::Get(v8::Isolate* pIsolate) {
240 return static_cast<FXJS_PerIsolateData*>(
241 pIsolate->GetData(g_embedderDataSlot));
242 }
243
FXJS_PerIsolateData(v8::Isolate * pIsolate)244 FXJS_PerIsolateData::FXJS_PerIsolateData(v8::Isolate* pIsolate)
245 : m_pDynamicObjsMap(new V8TemplateMap(pIsolate)) {}
246
CFXJS_Engine()247 CFXJS_Engine::CFXJS_Engine() : m_isolate(nullptr) {}
248
CFXJS_Engine(v8::Isolate * pIsolate)249 CFXJS_Engine::CFXJS_Engine(v8::Isolate* pIsolate) : m_isolate(pIsolate) {}
250
~CFXJS_Engine()251 CFXJS_Engine::~CFXJS_Engine() {
252 m_V8PersistentContext.Reset();
253 }
254
255 // static
CurrentEngineFromIsolate(v8::Isolate * pIsolate)256 CFXJS_Engine* CFXJS_Engine::CurrentEngineFromIsolate(v8::Isolate* pIsolate) {
257 return static_cast<CFXJS_Engine*>(
258 pIsolate->GetCurrentContext()->GetAlignedPointerFromEmbedderData(
259 kPerContextDataIndex));
260 }
261
262 // static
GetObjDefnID(v8::Local<v8::Object> pObj)263 int CFXJS_Engine::GetObjDefnID(v8::Local<v8::Object> pObj) {
264 CFXJS_PerObjectData* pData = CFXJS_PerObjectData::GetFromObject(pObj);
265 return pData ? pData->m_ObjDefID : -1;
266 }
267
268 // static
FreeObjectPrivate(void * pPerObjectData)269 void CFXJS_Engine::FreeObjectPrivate(void* pPerObjectData) {
270 delete static_cast<CFXJS_PerObjectData*>(pPerObjectData);
271 }
272
273 // static
FreeObjectPrivate(v8::Local<v8::Object> pObj)274 void CFXJS_Engine::FreeObjectPrivate(v8::Local<v8::Object> pObj) {
275 CFXJS_PerObjectData* pData = CFXJS_PerObjectData::GetFromObject(pObj);
276 pObj->SetAlignedPointerInInternalField(0, nullptr);
277 pObj->SetAlignedPointerInInternalField(1, nullptr);
278 delete pData;
279 }
280
DefineObj(const char * sObjName,FXJSOBJTYPE eObjType,CFXJS_Engine::Constructor pConstructor,CFXJS_Engine::Destructor pDestructor)281 int CFXJS_Engine::DefineObj(const char* sObjName,
282 FXJSOBJTYPE eObjType,
283 CFXJS_Engine::Constructor pConstructor,
284 CFXJS_Engine::Destructor pDestructor) {
285 v8::Isolate::Scope isolate_scope(m_isolate);
286 v8::HandleScope handle_scope(m_isolate);
287 FXJS_PerIsolateData::SetUp(m_isolate);
288 CFXJS_ObjDefinition* pObjDef = new CFXJS_ObjDefinition(
289 m_isolate, sObjName, eObjType, pConstructor, pDestructor);
290 return pObjDef->AssignID();
291 }
292
DefineObjMethod(int nObjDefnID,const char * sMethodName,v8::FunctionCallback pMethodCall)293 void CFXJS_Engine::DefineObjMethod(int nObjDefnID,
294 const char* sMethodName,
295 v8::FunctionCallback pMethodCall) {
296 v8::Isolate::Scope isolate_scope(m_isolate);
297 v8::HandleScope handle_scope(m_isolate);
298 CFXJS_ObjDefinition* pObjDef =
299 CFXJS_ObjDefinition::ForID(m_isolate, nObjDefnID);
300 v8::Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(
301 m_isolate, pMethodCall, v8::Local<v8::Value>(), pObjDef->GetSignature());
302 fun->RemovePrototype();
303 pObjDef->GetInstanceTemplate()->Set(
304 v8::String::NewFromUtf8(m_isolate, sMethodName,
305 v8::NewStringType::kNormal)
306 .ToLocalChecked(),
307 fun, v8::ReadOnly);
308 }
309
DefineObjProperty(int nObjDefnID,const char * sPropName,v8::AccessorGetterCallback pPropGet,v8::AccessorSetterCallback pPropPut)310 void CFXJS_Engine::DefineObjProperty(int nObjDefnID,
311 const char* sPropName,
312 v8::AccessorGetterCallback pPropGet,
313 v8::AccessorSetterCallback pPropPut) {
314 v8::Isolate::Scope isolate_scope(m_isolate);
315 v8::HandleScope handle_scope(m_isolate);
316 CFXJS_ObjDefinition* pObjDef =
317 CFXJS_ObjDefinition::ForID(m_isolate, nObjDefnID);
318 pObjDef->GetInstanceTemplate()->SetAccessor(
319 v8::String::NewFromUtf8(m_isolate, sPropName, v8::NewStringType::kNormal)
320 .ToLocalChecked(),
321 pPropGet, pPropPut);
322 }
323
DefineObjAllProperties(int nObjDefnID,v8::NamedPropertyQueryCallback pPropQurey,v8::NamedPropertyGetterCallback pPropGet,v8::NamedPropertySetterCallback pPropPut,v8::NamedPropertyDeleterCallback pPropDel)324 void CFXJS_Engine::DefineObjAllProperties(
325 int nObjDefnID,
326 v8::NamedPropertyQueryCallback pPropQurey,
327 v8::NamedPropertyGetterCallback pPropGet,
328 v8::NamedPropertySetterCallback pPropPut,
329 v8::NamedPropertyDeleterCallback pPropDel) {
330 v8::Isolate::Scope isolate_scope(m_isolate);
331 v8::HandleScope handle_scope(m_isolate);
332 CFXJS_ObjDefinition* pObjDef =
333 CFXJS_ObjDefinition::ForID(m_isolate, nObjDefnID);
334 pObjDef->GetInstanceTemplate()->SetNamedPropertyHandler(pPropGet, pPropPut,
335 pPropQurey, pPropDel);
336 }
337
DefineObjConst(int nObjDefnID,const char * sConstName,v8::Local<v8::Value> pDefault)338 void CFXJS_Engine::DefineObjConst(int nObjDefnID,
339 const char* sConstName,
340 v8::Local<v8::Value> pDefault) {
341 v8::Isolate::Scope isolate_scope(m_isolate);
342 v8::HandleScope handle_scope(m_isolate);
343 CFXJS_ObjDefinition* pObjDef =
344 CFXJS_ObjDefinition::ForID(m_isolate, nObjDefnID);
345 pObjDef->GetInstanceTemplate()->Set(m_isolate, sConstName, pDefault);
346 }
347
DefineGlobalMethod(const char * sMethodName,v8::FunctionCallback pMethodCall)348 void CFXJS_Engine::DefineGlobalMethod(const char* sMethodName,
349 v8::FunctionCallback pMethodCall) {
350 v8::Isolate::Scope isolate_scope(m_isolate);
351 v8::HandleScope handle_scope(m_isolate);
352 v8::Local<v8::FunctionTemplate> fun =
353 v8::FunctionTemplate::New(m_isolate, pMethodCall);
354 fun->RemovePrototype();
355 GetGlobalObjectTemplate(m_isolate)->Set(
356 v8::String::NewFromUtf8(m_isolate, sMethodName,
357 v8::NewStringType::kNormal)
358 .ToLocalChecked(),
359 fun, v8::ReadOnly);
360 }
361
DefineGlobalConst(const wchar_t * sConstName,v8::FunctionCallback pConstGetter)362 void CFXJS_Engine::DefineGlobalConst(const wchar_t* sConstName,
363 v8::FunctionCallback pConstGetter) {
364 v8::Isolate::Scope isolate_scope(m_isolate);
365 v8::HandleScope handle_scope(m_isolate);
366 CFX_ByteString bsConst = FX_UTF8Encode(CFX_WideStringC(sConstName));
367 v8::Local<v8::FunctionTemplate> fun =
368 v8::FunctionTemplate::New(m_isolate, pConstGetter);
369 fun->RemovePrototype();
370 GetGlobalObjectTemplate(m_isolate)->SetAccessorProperty(
371 v8::String::NewFromUtf8(m_isolate, bsConst.c_str(),
372 v8::NewStringType::kNormal)
373 .ToLocalChecked(),
374 fun);
375 }
376
InitializeEngine()377 void CFXJS_Engine::InitializeEngine() {
378 if (m_isolate == g_isolate)
379 ++g_isolate_ref_count;
380
381 v8::Isolate::Scope isolate_scope(m_isolate);
382 v8::HandleScope handle_scope(m_isolate);
383
384 // This has to happen before we call GetGlobalObjectTemplate because that
385 // method gets the PerIsolateData from m_isolate.
386 FXJS_PerIsolateData::SetUp(m_isolate);
387
388 v8::Local<v8::Context> v8Context =
389 v8::Context::New(m_isolate, nullptr, GetGlobalObjectTemplate(m_isolate));
390 v8::Context::Scope context_scope(v8Context);
391
392 v8Context->SetAlignedPointerInEmbedderData(kPerContextDataIndex, this);
393
394 int maxID = CFXJS_ObjDefinition::MaxID(m_isolate);
395 m_StaticObjects.resize(maxID + 1);
396 for (int i = 0; i < maxID; ++i) {
397 CFXJS_ObjDefinition* pObjDef = CFXJS_ObjDefinition::ForID(m_isolate, i);
398 if (pObjDef->m_ObjType == FXJSOBJTYPE_GLOBAL) {
399 CFXJS_PerObjectData::SetInObject(new CFXJS_PerObjectData(i),
400 v8Context->Global()
401 ->GetPrototype()
402 ->ToObject(v8Context)
403 .ToLocalChecked());
404 if (pObjDef->m_pConstructor) {
405 pObjDef->m_pConstructor(this, v8Context->Global()
406 ->GetPrototype()
407 ->ToObject(v8Context)
408 .ToLocalChecked());
409 }
410 } else if (pObjDef->m_ObjType == FXJSOBJTYPE_STATIC) {
411 v8::Local<v8::String> pObjName =
412 v8::String::NewFromUtf8(m_isolate, pObjDef->m_ObjName,
413 v8::NewStringType::kNormal,
414 strlen(pObjDef->m_ObjName))
415 .ToLocalChecked();
416
417 v8::Local<v8::Object> obj = NewFxDynamicObj(i, true);
418 v8Context->Global()->Set(v8Context, pObjName, obj).FromJust();
419 m_StaticObjects[i] = new v8::Global<v8::Object>(m_isolate, obj);
420 }
421 }
422 m_V8PersistentContext.Reset(m_isolate, v8Context);
423 }
424
ReleaseEngine()425 void CFXJS_Engine::ReleaseEngine() {
426 v8::Isolate::Scope isolate_scope(m_isolate);
427 v8::HandleScope handle_scope(m_isolate);
428 v8::Local<v8::Context> context =
429 v8::Local<v8::Context>::New(m_isolate, m_V8PersistentContext);
430 v8::Context::Scope context_scope(context);
431
432 FXJS_PerIsolateData* pData = FXJS_PerIsolateData::Get(m_isolate);
433 if (!pData)
434 return;
435
436 m_ConstArrays.clear();
437
438 int maxID = CFXJS_ObjDefinition::MaxID(m_isolate);
439 for (int i = 0; i < maxID; ++i) {
440 CFXJS_ObjDefinition* pObjDef = CFXJS_ObjDefinition::ForID(m_isolate, i);
441 v8::Local<v8::Object> pObj;
442 if (pObjDef->m_ObjType == FXJSOBJTYPE_GLOBAL) {
443 pObj =
444 context->Global()->GetPrototype()->ToObject(context).ToLocalChecked();
445 } else if (m_StaticObjects[i] && !m_StaticObjects[i]->IsEmpty()) {
446 pObj = v8::Local<v8::Object>::New(m_isolate, *m_StaticObjects[i]);
447 delete m_StaticObjects[i];
448 m_StaticObjects[i] = nullptr;
449 }
450
451 if (!pObj.IsEmpty()) {
452 if (pObjDef->m_pDestructor)
453 pObjDef->m_pDestructor(this, pObj);
454 FreeObjectPrivate(pObj);
455 }
456 }
457
458 m_V8PersistentContext.Reset();
459
460 if (m_isolate == g_isolate && --g_isolate_ref_count > 0)
461 return;
462
463 delete pData;
464 m_isolate->SetData(g_embedderDataSlot, nullptr);
465 }
466
Execute(const CFX_WideString & script,FXJSErr * pError)467 int CFXJS_Engine::Execute(const CFX_WideString& script, FXJSErr* pError) {
468 v8::Isolate::Scope isolate_scope(m_isolate);
469 v8::TryCatch try_catch(m_isolate);
470 CFX_ByteString bsScript = script.UTF8Encode();
471 v8::Local<v8::Context> context = m_isolate->GetCurrentContext();
472 v8::Local<v8::Script> compiled_script;
473 if (!v8::Script::Compile(context,
474 v8::String::NewFromUtf8(m_isolate, bsScript.c_str(),
475 v8::NewStringType::kNormal,
476 bsScript.GetLength())
477 .ToLocalChecked())
478 .ToLocal(&compiled_script)) {
479 v8::String::Utf8Value error(try_catch.Exception());
480 // TODO(tsepez): return error via pError->message.
481 return -1;
482 }
483
484 v8::Local<v8::Value> result;
485 if (!compiled_script->Run(context).ToLocal(&result)) {
486 v8::String::Utf8Value error(try_catch.Exception());
487 // TODO(tsepez): return error via pError->message.
488 return -1;
489 }
490 return 0;
491 }
492
NewFxDynamicObj(int nObjDefnID,bool bStatic)493 v8::Local<v8::Object> CFXJS_Engine::NewFxDynamicObj(int nObjDefnID,
494 bool bStatic) {
495 v8::Isolate::Scope isolate_scope(m_isolate);
496 v8::Local<v8::Context> context = m_isolate->GetCurrentContext();
497 if (nObjDefnID == -1) {
498 v8::Local<v8::ObjectTemplate> objTempl = v8::ObjectTemplate::New(m_isolate);
499 v8::Local<v8::Object> obj;
500 if (!objTempl->NewInstance(context).ToLocal(&obj))
501 return v8::Local<v8::Object>();
502 return obj;
503 }
504
505 FXJS_PerIsolateData* pData = FXJS_PerIsolateData::Get(m_isolate);
506 if (!pData)
507 return v8::Local<v8::Object>();
508
509 if (nObjDefnID < 0 || nObjDefnID >= CFXJS_ObjDefinition::MaxID(m_isolate))
510 return v8::Local<v8::Object>();
511
512 CFXJS_ObjDefinition* pObjDef =
513 CFXJS_ObjDefinition::ForID(m_isolate, nObjDefnID);
514 v8::Local<v8::Object> obj;
515 if (!pObjDef->GetInstanceTemplate()->NewInstance(context).ToLocal(&obj))
516 return v8::Local<v8::Object>();
517
518 CFXJS_PerObjectData* pObjData = new CFXJS_PerObjectData(nObjDefnID);
519 CFXJS_PerObjectData::SetInObject(pObjData, obj);
520 if (pObjDef->m_pConstructor)
521 pObjDef->m_pConstructor(this, obj);
522
523 if (!bStatic && FXJS_PerIsolateData::Get(m_isolate)->m_pDynamicObjsMap)
524 FXJS_PerIsolateData::Get(m_isolate)->m_pDynamicObjsMap->set(pObjData, obj);
525
526 return obj;
527 }
528
GetThisObj()529 v8::Local<v8::Object> CFXJS_Engine::GetThisObj() {
530 v8::Isolate::Scope isolate_scope(m_isolate);
531 if (!FXJS_PerIsolateData::Get(m_isolate))
532 return v8::Local<v8::Object>();
533
534 // Return the global object.
535 v8::Local<v8::Context> context = m_isolate->GetCurrentContext();
536 return context->Global()->GetPrototype()->ToObject(context).ToLocalChecked();
537 }
538
Error(const CFX_WideString & message)539 void CFXJS_Engine::Error(const CFX_WideString& message) {
540 // Conversion from pdfium's wchar_t wide-strings to v8's uint16_t
541 // wide-strings isn't handled by v8, so use UTF8 as a common
542 // intermediate format.
543 CFX_ByteString utf8_message = message.UTF8Encode();
544 m_isolate->ThrowException(v8::String::NewFromUtf8(m_isolate,
545 utf8_message.c_str(),
546 v8::NewStringType::kNormal)
547 .ToLocalChecked());
548 }
549
SetObjectPrivate(v8::Local<v8::Object> pObj,void * p)550 void CFXJS_Engine::SetObjectPrivate(v8::Local<v8::Object> pObj, void* p) {
551 CFXJS_PerObjectData* pPerObjectData =
552 CFXJS_PerObjectData::GetFromObject(pObj);
553 if (!pPerObjectData)
554 return;
555 pPerObjectData->m_pPrivate = p;
556 }
557
GetObjectPrivate(v8::Local<v8::Object> pObj)558 void* CFXJS_Engine::GetObjectPrivate(v8::Local<v8::Object> pObj) {
559 CFXJS_PerObjectData* pData = CFXJS_PerObjectData::GetFromObject(pObj);
560 if (!pData && !pObj.IsEmpty()) {
561 // It could be a global proxy object.
562 v8::Local<v8::Value> v = pObj->GetPrototype();
563 v8::Local<v8::Context> context = m_isolate->GetCurrentContext();
564 if (v->IsObject()) {
565 pData = CFXJS_PerObjectData::GetFromObject(
566 v->ToObject(context).ToLocalChecked());
567 }
568 }
569 return pData ? pData->m_pPrivate : nullptr;
570 }
571
GetObjectProperty(v8::Local<v8::Object> pObj,const CFX_WideString & wsPropertyName)572 v8::Local<v8::Value> CFXJS_Engine::GetObjectProperty(
573 v8::Local<v8::Object> pObj,
574 const CFX_WideString& wsPropertyName) {
575 if (pObj.IsEmpty())
576 return v8::Local<v8::Value>();
577 v8::Local<v8::Value> val;
578 if (!pObj->Get(m_isolate->GetCurrentContext(),
579 NewString(wsPropertyName.AsStringC()))
580 .ToLocal(&val))
581 return v8::Local<v8::Value>();
582 return val;
583 }
584
GetObjectPropertyNames(v8::Local<v8::Object> pObj)585 std::vector<CFX_WideString> CFXJS_Engine::GetObjectPropertyNames(
586 v8::Local<v8::Object> pObj) {
587 if (pObj.IsEmpty())
588 return std::vector<CFX_WideString>();
589
590 v8::Local<v8::Array> val;
591 v8::Local<v8::Context> context = m_isolate->GetCurrentContext();
592 if (!pObj->GetPropertyNames(context).ToLocal(&val))
593 return std::vector<CFX_WideString>();
594
595 std::vector<CFX_WideString> result;
596 for (uint32_t i = 0; i < val->Length(); ++i) {
597 result.push_back(ToWideString(val->Get(context, i).ToLocalChecked()));
598 }
599
600 return result;
601 }
602
PutObjectProperty(v8::Local<v8::Object> pObj,const CFX_WideString & wsPropertyName,v8::Local<v8::Value> pPut)603 void CFXJS_Engine::PutObjectProperty(v8::Local<v8::Object> pObj,
604 const CFX_WideString& wsPropertyName,
605 v8::Local<v8::Value> pPut) {
606 if (pObj.IsEmpty())
607 return;
608 pObj->Set(m_isolate->GetCurrentContext(),
609 NewString(wsPropertyName.AsStringC()), pPut)
610 .FromJust();
611 }
612
613
NewArray()614 v8::Local<v8::Array> CFXJS_Engine::NewArray() {
615 return v8::Array::New(m_isolate);
616 }
617
PutArrayElement(v8::Local<v8::Array> pArray,unsigned index,v8::Local<v8::Value> pValue)618 unsigned CFXJS_Engine::PutArrayElement(v8::Local<v8::Array> pArray,
619 unsigned index,
620 v8::Local<v8::Value> pValue) {
621 if (pArray.IsEmpty())
622 return 0;
623 if (pArray->Set(m_isolate->GetCurrentContext(), index, pValue).IsNothing())
624 return 0;
625 return 1;
626 }
627
GetArrayElement(v8::Local<v8::Array> pArray,unsigned index)628 v8::Local<v8::Value> CFXJS_Engine::GetArrayElement(v8::Local<v8::Array> pArray,
629 unsigned index) {
630 if (pArray.IsEmpty())
631 return v8::Local<v8::Value>();
632 v8::Local<v8::Value> val;
633 if (!pArray->Get(m_isolate->GetCurrentContext(), index).ToLocal(&val))
634 return v8::Local<v8::Value>();
635 return val;
636 }
637
GetArrayLength(v8::Local<v8::Array> pArray)638 unsigned CFXJS_Engine::GetArrayLength(v8::Local<v8::Array> pArray) {
639 if (pArray.IsEmpty())
640 return 0;
641 return pArray->Length();
642 }
643
NewLocalContext()644 v8::Local<v8::Context> CFXJS_Engine::NewLocalContext() {
645 return v8::Local<v8::Context>::New(m_isolate, m_V8PersistentContext);
646 }
647
GetPersistentContext()648 v8::Local<v8::Context> CFXJS_Engine::GetPersistentContext() {
649 return m_V8PersistentContext.Get(m_isolate);
650 }
651
NewNumber(int number)652 v8::Local<v8::Value> CFXJS_Engine::NewNumber(int number) {
653 return v8::Int32::New(m_isolate, number);
654 }
655
NewNumber(double number)656 v8::Local<v8::Value> CFXJS_Engine::NewNumber(double number) {
657 return v8::Number::New(m_isolate, number);
658 }
659
NewNumber(float number)660 v8::Local<v8::Value> CFXJS_Engine::NewNumber(float number) {
661 return v8::Number::New(m_isolate, (float)number);
662 }
663
NewBoolean(bool b)664 v8::Local<v8::Value> CFXJS_Engine::NewBoolean(bool b) {
665 return v8::Boolean::New(m_isolate, b);
666 }
667
NewString(const CFX_ByteStringC & str)668 v8::Local<v8::Value> CFXJS_Engine::NewString(const CFX_ByteStringC& str) {
669 v8::Isolate* pIsolate = m_isolate ? m_isolate : v8::Isolate::GetCurrent();
670 return v8::String::NewFromUtf8(pIsolate, str.c_str(),
671 v8::NewStringType::kNormal, str.GetLength())
672 .ToLocalChecked();
673 }
674
NewString(const CFX_WideStringC & str)675 v8::Local<v8::Value> CFXJS_Engine::NewString(const CFX_WideStringC& str) {
676 return NewString(FX_UTF8Encode(str).AsStringC());
677 }
678
NewNull()679 v8::Local<v8::Value> CFXJS_Engine::NewNull() {
680 return v8::Local<v8::Value>();
681 }
682
NewDate(double d)683 v8::Local<v8::Date> CFXJS_Engine::NewDate(double d) {
684 return v8::Date::New(m_isolate->GetCurrentContext(), d)
685 .ToLocalChecked()
686 .As<v8::Date>();
687 }
688
ToInt32(v8::Local<v8::Value> pValue)689 int CFXJS_Engine::ToInt32(v8::Local<v8::Value> pValue) {
690 if (pValue.IsEmpty())
691 return 0;
692 v8::Local<v8::Context> context = m_isolate->GetCurrentContext();
693 return pValue->ToInt32(context).ToLocalChecked()->Value();
694 }
695
ToBoolean(v8::Local<v8::Value> pValue)696 bool CFXJS_Engine::ToBoolean(v8::Local<v8::Value> pValue) {
697 if (pValue.IsEmpty())
698 return false;
699 v8::Local<v8::Context> context = m_isolate->GetCurrentContext();
700 return pValue->ToBoolean(context).ToLocalChecked()->Value();
701 }
702
ToDouble(v8::Local<v8::Value> pValue)703 double CFXJS_Engine::ToDouble(v8::Local<v8::Value> pValue) {
704 if (pValue.IsEmpty())
705 return 0.0;
706 v8::Local<v8::Context> context = m_isolate->GetCurrentContext();
707 return pValue->ToNumber(context).ToLocalChecked()->Value();
708 }
709
ToWideString(v8::Local<v8::Value> pValue)710 CFX_WideString CFXJS_Engine::ToWideString(v8::Local<v8::Value> pValue) {
711 if (pValue.IsEmpty())
712 return CFX_WideString();
713 v8::Local<v8::Context> context = m_isolate->GetCurrentContext();
714 v8::String::Utf8Value s(pValue->ToString(context).ToLocalChecked());
715 return CFX_WideString::FromUTF8(CFX_ByteStringC(*s, s.length()));
716 }
717
ToObject(v8::Local<v8::Value> pValue)718 v8::Local<v8::Object> CFXJS_Engine::ToObject(v8::Local<v8::Value> pValue) {
719 if (pValue.IsEmpty() || !pValue->IsObject())
720 return v8::Local<v8::Object>();
721 v8::Local<v8::Context> context = m_isolate->GetCurrentContext();
722 return pValue->ToObject(context).ToLocalChecked();
723 }
724
ToArray(v8::Local<v8::Value> pValue)725 v8::Local<v8::Array> CFXJS_Engine::ToArray(v8::Local<v8::Value> pValue) {
726 if (pValue.IsEmpty() || !pValue->IsArray())
727 return v8::Local<v8::Array>();
728 v8::Local<v8::Context> context = m_isolate->GetCurrentContext();
729 return v8::Local<v8::Array>::Cast(pValue->ToObject(context).ToLocalChecked());
730 }
731
SetConstArray(const CFX_WideString & name,v8::Local<v8::Array> array)732 void CFXJS_Engine::SetConstArray(const CFX_WideString& name,
733 v8::Local<v8::Array> array) {
734 m_ConstArrays[name] = v8::Global<v8::Array>(GetIsolate(), array);
735 }
736
GetConstArray(const CFX_WideString & name)737 v8::Local<v8::Array> CFXJS_Engine::GetConstArray(const CFX_WideString& name) {
738 return v8::Local<v8::Array>::New(GetIsolate(), m_ConstArrays[name]);
739 }
740