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.
17static const unsigned int kPerContextDataIndex = 3u;
18static unsigned int g_embedderDataSlot = 1u;
19static v8::Isolate* g_isolate = nullptr;
20static size_t g_isolate_ref_count = 0;
21static FXJS_ArrayBufferAllocator* g_arrayBufferAllocator = nullptr;
22static v8::Global<v8::ObjectTemplate>* g_DefaultGlobalObjectTemplate = nullptr;
23static wchar_t kPerObjectDataTag[] = L"CFXJS_PerObjectData";
24
25class CFXJS_PerObjectData {
26 public:
27  explicit CFXJS_PerObjectData(int nObjDefID)
28      : m_ObjDefID(nObjDefID), m_pPrivate(nullptr) {}
29
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
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
53class CFXJS_ObjDefinition {
54 public:
55  static int MaxID(v8::Isolate* pIsolate) {
56    return FXJS_PerIsolateData::Get(pIsolate)->m_ObjectDefnArray.size();
57  }
58
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
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
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
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
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
125static 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
146void* FXJS_ArrayBufferAllocator::Allocate(size_t length) {
147  return calloc(1, length);
148}
149
150void* FXJS_ArrayBufferAllocator::AllocateUninitialized(size_t length) {
151  return malloc(length);
152}
153
154void FXJS_ArrayBufferAllocator::Free(void* data, size_t length) {
155  free(data);
156}
157
158void 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
176V8TemplateMapTraits::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
183void 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
193void 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
203bool 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
217size_t FXJS_GlobalIsolateRefCount() {
218  return g_isolate_ref_count;
219}
220
221V8TemplateMap::V8TemplateMap(v8::Isolate* isolate) : m_map(isolate) {}
222
223V8TemplateMap::~V8TemplateMap() {}
224
225void V8TemplateMap::set(void* key, v8::Local<v8::Object> handle) {
226  ASSERT(!m_map.Contains(key));
227  m_map.Set(key, handle);
228}
229
230FXJS_PerIsolateData::~FXJS_PerIsolateData() {}
231
232// static
233void 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
239FXJS_PerIsolateData* FXJS_PerIsolateData::Get(v8::Isolate* pIsolate) {
240  return static_cast<FXJS_PerIsolateData*>(
241      pIsolate->GetData(g_embedderDataSlot));
242}
243
244FXJS_PerIsolateData::FXJS_PerIsolateData(v8::Isolate* pIsolate)
245    : m_pDynamicObjsMap(new V8TemplateMap(pIsolate)) {}
246
247CFXJS_Engine::CFXJS_Engine() : m_isolate(nullptr) {}
248
249CFXJS_Engine::CFXJS_Engine(v8::Isolate* pIsolate) : m_isolate(pIsolate) {}
250
251CFXJS_Engine::~CFXJS_Engine() {
252  m_V8PersistentContext.Reset();
253}
254
255// static
256CFXJS_Engine* CFXJS_Engine::CurrentEngineFromIsolate(v8::Isolate* pIsolate) {
257  return static_cast<CFXJS_Engine*>(
258      pIsolate->GetCurrentContext()->GetAlignedPointerFromEmbedderData(
259          kPerContextDataIndex));
260}
261
262// static
263int 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
269void CFXJS_Engine::FreeObjectPrivate(void* pPerObjectData) {
270  delete static_cast<CFXJS_PerObjectData*>(pPerObjectData);
271}
272
273// static
274void 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
281int 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
293void 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
310void 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
324void 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
338void 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
348void 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
362void 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
377void 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
425void 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
467int 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
493v8::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
529v8::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
539void 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
550void 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
558void* 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
572v8::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
585std::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
603void 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
614v8::Local<v8::Array> CFXJS_Engine::NewArray() {
615  return v8::Array::New(m_isolate);
616}
617
618unsigned 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
628v8::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
638unsigned CFXJS_Engine::GetArrayLength(v8::Local<v8::Array> pArray) {
639  if (pArray.IsEmpty())
640    return 0;
641  return pArray->Length();
642}
643
644v8::Local<v8::Context> CFXJS_Engine::NewLocalContext() {
645  return v8::Local<v8::Context>::New(m_isolate, m_V8PersistentContext);
646}
647
648v8::Local<v8::Context> CFXJS_Engine::GetPersistentContext() {
649  return m_V8PersistentContext.Get(m_isolate);
650}
651
652v8::Local<v8::Value> CFXJS_Engine::NewNumber(int number) {
653  return v8::Int32::New(m_isolate, number);
654}
655
656v8::Local<v8::Value> CFXJS_Engine::NewNumber(double number) {
657  return v8::Number::New(m_isolate, number);
658}
659
660v8::Local<v8::Value> CFXJS_Engine::NewNumber(float number) {
661  return v8::Number::New(m_isolate, (float)number);
662}
663
664v8::Local<v8::Value> CFXJS_Engine::NewBoolean(bool b) {
665  return v8::Boolean::New(m_isolate, b);
666}
667
668v8::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
675v8::Local<v8::Value> CFXJS_Engine::NewString(const CFX_WideStringC& str) {
676  return NewString(FX_UTF8Encode(str).AsStringC());
677}
678
679v8::Local<v8::Value> CFXJS_Engine::NewNull() {
680  return v8::Local<v8::Value>();
681}
682
683v8::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
689int 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
696bool 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
703double 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
710CFX_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
718v8::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
725v8::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
732void 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
737v8::Local<v8::Array> CFXJS_Engine::GetConstArray(const CFX_WideString& name) {
738  return v8::Local<v8::Array>::New(GetIsolate(), m_ConstArrays[name]);
739}
740