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