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