Home | History | Annotate | Download | only in fxjs
      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:
     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 
     53 class 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 
    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 
    146 void* FXJS_ArrayBufferAllocator::Allocate(size_t length) {
    147   return calloc(1, length);
    148 }
    149 
    150 void* FXJS_ArrayBufferAllocator::AllocateUninitialized(size_t length) {
    151   return malloc(length);
    152 }
    153 
    154 void FXJS_ArrayBufferAllocator::Free(void* data, size_t length) {
    155   free(data);
    156 }
    157 
    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 
    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 
    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 
    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 
    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 
    217 size_t FXJS_GlobalIsolateRefCount() {
    218   return g_isolate_ref_count;
    219 }
    220 
    221 V8TemplateMap::V8TemplateMap(v8::Isolate* isolate) : m_map(isolate) {}
    222 
    223 V8TemplateMap::~V8TemplateMap() {}
    224 
    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 
    230 FXJS_PerIsolateData::~FXJS_PerIsolateData() {}
    231 
    232 // static
    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
    239 FXJS_PerIsolateData* FXJS_PerIsolateData::Get(v8::Isolate* pIsolate) {
    240   return static_cast<FXJS_PerIsolateData*>(
    241       pIsolate->GetData(g_embedderDataSlot));
    242 }
    243 
    244 FXJS_PerIsolateData::FXJS_PerIsolateData(v8::Isolate* pIsolate)
    245     : m_pDynamicObjsMap(new V8TemplateMap(pIsolate)) {}
    246 
    247 CFXJS_Engine::CFXJS_Engine() : m_isolate(nullptr) {}
    248 
    249 CFXJS_Engine::CFXJS_Engine(v8::Isolate* pIsolate) : m_isolate(pIsolate) {}
    250 
    251 CFXJS_Engine::~CFXJS_Engine() {
    252   m_V8PersistentContext.Reset();
    253 }
    254 
    255 // static
    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
    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
    269 void CFXJS_Engine::FreeObjectPrivate(void* pPerObjectData) {
    270   delete static_cast<CFXJS_PerObjectData*>(pPerObjectData);
    271 }
    272 
    273 // static
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    614 v8::Local<v8::Array> CFXJS_Engine::NewArray() {
    615   return v8::Array::New(m_isolate);
    616 }
    617 
    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 
    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 
    638 unsigned CFXJS_Engine::GetArrayLength(v8::Local<v8::Array> pArray) {
    639   if (pArray.IsEmpty())
    640     return 0;
    641   return pArray->Length();
    642 }
    643 
    644 v8::Local<v8::Context> CFXJS_Engine::NewLocalContext() {
    645   return v8::Local<v8::Context>::New(m_isolate, m_V8PersistentContext);
    646 }
    647 
    648 v8::Local<v8::Context> CFXJS_Engine::GetPersistentContext() {
    649   return m_V8PersistentContext.Get(m_isolate);
    650 }
    651 
    652 v8::Local<v8::Value> CFXJS_Engine::NewNumber(int number) {
    653   return v8::Int32::New(m_isolate, number);
    654 }
    655 
    656 v8::Local<v8::Value> CFXJS_Engine::NewNumber(double number) {
    657   return v8::Number::New(m_isolate, number);
    658 }
    659 
    660 v8::Local<v8::Value> CFXJS_Engine::NewNumber(float number) {
    661   return v8::Number::New(m_isolate, (float)number);
    662 }
    663 
    664 v8::Local<v8::Value> CFXJS_Engine::NewBoolean(bool b) {
    665   return v8::Boolean::New(m_isolate, b);
    666 }
    667 
    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 
    675 v8::Local<v8::Value> CFXJS_Engine::NewString(const CFX_WideStringC& str) {
    676   return NewString(FX_UTF8Encode(str).AsStringC());
    677 }
    678 
    679 v8::Local<v8::Value> CFXJS_Engine::NewNull() {
    680   return v8::Local<v8::Value>();
    681 }
    682 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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