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