Store object definition ID in each js_class.
[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 "../../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_PrivateData {
32  public:
33   CFXJS_PrivateData(int nObjDefID) : ObjDefID(nObjDefID), pPrivate(NULL) {}
34
35   int ObjDefID;
36   void* pPrivate;
37 };
38
39 class CFXJS_ObjDefinition {
40  public:
41   static int MaxID(v8::Isolate* pIsolate) {
42     return static_cast<int>(
43         FXJS_PerIsolateData::Get(pIsolate)->m_ObjectDefnArray.GetSize());
44   }
45   static CFXJS_ObjDefinition* ForID(v8::Isolate* pIsolate, int id) {
46     // Note: GetAt() halts if out-of-range even in release builds.
47     return static_cast<CFXJS_ObjDefinition*>(
48         FXJS_PerIsolateData::Get(pIsolate)->m_ObjectDefnArray.GetAt(id));
49   }
50   CFXJS_ObjDefinition(v8::Isolate* isolate,
51                       const wchar_t* sObjName,
52                       FXJSOBJTYPE eObjType,
53                       FXJS_CONSTRUCTOR pConstructor,
54                       FXJS_DESTRUCTOR pDestructor)
55       : m_ObjName(sObjName),
56         m_ObjType(eObjType),
57         m_pConstructor(pConstructor),
58         m_pDestructor(pDestructor),
59         m_pIsolate(isolate) {
60     v8::Isolate::Scope isolate_scope(isolate);
61     v8::HandleScope handle_scope(isolate);
62
63     v8::Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(isolate);
64     fun->InstanceTemplate()->SetInternalFieldCount(2);
65     m_FunctionTemplate.Reset(isolate, fun);
66
67     v8::Local<v8::Signature> sig = v8::Signature::New(isolate, fun);
68     m_Signature.Reset(isolate, sig);
69   }
70
71   int AssignID() {
72     FXJS_PerIsolateData* pData = FXJS_PerIsolateData::Get(m_pIsolate);
73     pData->m_ObjectDefnArray.Add(this);
74     return pData->m_ObjectDefnArray.GetSize() - 1;
75   }
76
77   v8::Local<v8::ObjectTemplate> GetInstanceTemplate() {
78     v8::EscapableHandleScope scope(m_pIsolate);
79     v8::Local<v8::FunctionTemplate> function =
80         m_FunctionTemplate.Get(m_pIsolate);
81     return scope.Escape(function->InstanceTemplate());
82   }
83
84   v8::Local<v8::Signature> GetSignature() {
85     v8::EscapableHandleScope scope(m_pIsolate);
86     return scope.Escape(m_Signature.Get(m_pIsolate));
87   }
88
89   const wchar_t* const m_ObjName;
90   const FXJSOBJTYPE m_ObjType;
91   const FXJS_CONSTRUCTOR m_pConstructor;
92   const FXJS_DESTRUCTOR m_pDestructor;
93
94   v8::Isolate* m_pIsolate;
95   v8::Global<v8::FunctionTemplate> m_FunctionTemplate;
96   v8::Global<v8::Signature> m_Signature;
97   v8::Global<v8::Object> m_StaticObj;
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(v8::Isolate* pIsolate,
276                             IFXJS_Runtime* pFXRuntime,
277                             IFXJS_Context* context,
278                             v8::Global<v8::Context>& v8PersistentContext) {
279   if (pIsolate == g_isolate)
280     ++g_isolate_ref_count;
281
282   v8::Isolate::Scope isolate_scope(pIsolate);
283   v8::HandleScope handle_scope(pIsolate);
284   v8::Local<v8::Context> v8Context =
285       v8::Context::New(pIsolate, NULL, GetGlobalObjectTemplate(pIsolate));
286   v8::Context::Scope context_scope(v8Context);
287
288   FXJS_PerIsolateData::SetUp(pIsolate);
289   v8Context->SetAlignedPointerInEmbedderData(kPerContextDataIndex, pFXRuntime);
290
291   int maxID = CFXJS_ObjDefinition::MaxID(pIsolate);
292   for (int i = 0; i < maxID; ++i) {
293     CFXJS_ObjDefinition* pObjDef = CFXJS_ObjDefinition::ForID(pIsolate, i);
294     CFX_ByteString bs = CFX_WideString(pObjDef->m_ObjName).UTF8Encode();
295     v8::Local<v8::String> m_ObjName =
296         v8::String::NewFromUtf8(pIsolate, bs.c_str(),
297                                 v8::NewStringType::kNormal,
298                                 bs.GetLength()).ToLocalChecked();
299
300     if (pObjDef->m_ObjType == FXJSOBJTYPE_GLOBAL) {
301       v8Context->Global()
302           ->GetPrototype()
303           ->ToObject(v8Context)
304           .ToLocalChecked()
305           ->SetAlignedPointerInInternalField(0, new CFXJS_PrivateData(i));
306
307       if (pObjDef->m_pConstructor)
308         pObjDef->m_pConstructor(context, v8Context->Global()
309                                              ->GetPrototype()
310                                              ->ToObject(v8Context)
311                                              .ToLocalChecked(),
312                                 v8Context->Global()
313                                     ->GetPrototype()
314                                     ->ToObject(v8Context)
315                                     .ToLocalChecked());
316     } else if (pObjDef->m_ObjType == FXJSOBJTYPE_STATIC) {
317       v8::Local<v8::Object> obj = FXJS_NewFxDynamicObj(pIsolate, context, i);
318       v8Context->Global()->Set(v8Context, m_ObjName, obj).FromJust();
319       pObjDef->m_StaticObj.Reset(pIsolate, obj);
320     }
321   }
322   v8PersistentContext.Reset(pIsolate, v8Context);
323 }
324
325 void FXJS_ReleaseRuntime(v8::Isolate* pIsolate,
326                          v8::Global<v8::Context>& v8PersistentContext) {
327   if (pIsolate == g_isolate && --g_isolate_ref_count > 0)
328     return;
329
330   v8::Isolate::Scope isolate_scope(pIsolate);
331   v8::HandleScope handle_scope(pIsolate);
332   v8::Local<v8::Context> context =
333       v8::Local<v8::Context>::New(pIsolate, v8PersistentContext);
334   v8::Context::Scope context_scope(context);
335
336   FXJS_PerIsolateData* pData = FXJS_PerIsolateData::Get(pIsolate);
337   if (!pData)
338     return;
339
340   int maxID = CFXJS_ObjDefinition::MaxID(pIsolate);
341   for (int i = 0; i < maxID; ++i) {
342     CFXJS_ObjDefinition* pObjDef = CFXJS_ObjDefinition::ForID(pIsolate, i);
343     if (!pObjDef->m_StaticObj.IsEmpty()) {
344       v8::Local<v8::Object> pObj =
345           v8::Local<v8::Object>::New(pIsolate, pObjDef->m_StaticObj);
346       if (pObjDef->m_pDestructor)
347         pObjDef->m_pDestructor(pObj);
348       FXJS_FreePrivate(pObj);
349     }
350     delete pObjDef;
351   }
352
353   pIsolate->SetData(g_embedderDataSlot, nullptr);
354   delete pData;
355 }
356
357 IFXJS_Runtime* FXJS_GetRuntimeFromIsolate(v8::Isolate* pIsolate) {
358   v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
359   return static_cast<IFXJS_Runtime*>(
360       context->GetAlignedPointerFromEmbedderData(kPerContextDataIndex));
361 }
362
363 int FXJS_Execute(v8::Isolate* pIsolate,
364                  IFXJS_Context* pJSContext,
365                  const wchar_t* script,
366                  long length,
367                  FXJSErr* pError) {
368   v8::Isolate::Scope isolate_scope(pIsolate);
369   v8::TryCatch try_catch(pIsolate);
370   CFX_ByteString bsScript = CFX_WideString(script).UTF8Encode();
371   v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
372   v8::Local<v8::Script> compiled_script;
373   if (!v8::Script::Compile(
374            context, v8::String::NewFromUtf8(
375                         pIsolate, bsScript.c_str(), v8::NewStringType::kNormal,
376                         bsScript.GetLength()).ToLocalChecked())
377            .ToLocal(&compiled_script)) {
378     v8::String::Utf8Value error(try_catch.Exception());
379     // TODO(tsepez): return error via pError->message.
380     return -1;
381   }
382
383   v8::Local<v8::Value> result;
384   if (!compiled_script->Run(context).ToLocal(&result)) {
385     v8::String::Utf8Value error(try_catch.Exception());
386     // TODO(tsepez): return error via pError->message.
387     return -1;
388   }
389   return 0;
390 }
391
392 v8::Local<v8::Object> FXJS_NewFxDynamicObj(v8::Isolate* pIsolate,
393                                            IFXJS_Context* pJSContext,
394                                            int nObjDefnID) {
395   v8::Isolate::Scope isolate_scope(pIsolate);
396   v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
397   if (nObjDefnID == -1) {
398     v8::Local<v8::ObjectTemplate> objTempl = v8::ObjectTemplate::New(pIsolate);
399     v8::Local<v8::Object> obj;
400     if (!objTempl->NewInstance(context).ToLocal(&obj))
401       return v8::Local<v8::Object>();
402     return obj;
403   }
404
405   FXJS_PerIsolateData* pData = FXJS_PerIsolateData::Get(pIsolate);
406   if (!pData)
407     return v8::Local<v8::Object>();
408
409   if (nObjDefnID < 0 || nObjDefnID >= CFXJS_ObjDefinition::MaxID(pIsolate))
410     return v8::Local<v8::Object>();
411
412   CFXJS_ObjDefinition* pObjDef =
413       CFXJS_ObjDefinition::ForID(pIsolate, nObjDefnID);
414   v8::Local<v8::Object> obj;
415   if (!pObjDef->GetInstanceTemplate()->NewInstance(context).ToLocal(&obj))
416     return v8::Local<v8::Object>();
417
418   obj->SetAlignedPointerInInternalField(0, new CFXJS_PrivateData(nObjDefnID));
419   if (pObjDef->m_pConstructor) {
420     pObjDef->m_pConstructor(
421         pJSContext, obj,
422         context->Global()->GetPrototype()->ToObject(context).ToLocalChecked());
423   }
424
425   return obj;
426 }
427
428 v8::Local<v8::Object> FXJS_GetThisObj(v8::Isolate* pIsolate) {
429   v8::Isolate::Scope isolate_scope(pIsolate);
430   if (!FXJS_PerIsolateData::Get(pIsolate))
431     return v8::Local<v8::Object>();
432
433   // Return the global object.
434   v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
435   return context->Global()->GetPrototype()->ToObject(context).ToLocalChecked();
436 }
437
438 int FXJS_GetObjDefnID(v8::Local<v8::Object> pObj) {
439   if (pObj.IsEmpty() || !pObj->InternalFieldCount())
440     return -1;
441   CFXJS_PrivateData* pPrivateData =
442       (CFXJS_PrivateData*)pObj->GetAlignedPointerFromInternalField(0);
443   if (pPrivateData)
444     return pPrivateData->ObjDefID;
445   return -1;
446 }
447
448 v8::Isolate* FXJS_GetRuntime(v8::Local<v8::Object> pObj) {
449   if (pObj.IsEmpty())
450     return NULL;
451   v8::Local<v8::Context> context = pObj->CreationContext();
452   if (context.IsEmpty())
453     return NULL;
454   return context->GetIsolate();
455 }
456
457 void FXJS_Error(v8::Isolate* pIsolate, const CFX_WideString& message) {
458   // Conversion from pdfium's wchar_t wide-strings to v8's uint16_t
459   // wide-strings isn't handled by v8, so use UTF8 as a common
460   // intermediate format.
461   CFX_ByteString utf8_message = message.UTF8Encode();
462   pIsolate->ThrowException(
463       v8::String::NewFromUtf8(pIsolate, utf8_message.c_str(),
464                               v8::NewStringType::kNormal).ToLocalChecked());
465 }
466
467 const wchar_t* FXJS_GetTypeof(v8::Local<v8::Value> pObj) {
468   if (pObj.IsEmpty())
469     return NULL;
470   if (pObj->IsString())
471     return kFXJSValueNameString;
472   if (pObj->IsNumber())
473     return kFXJSValueNameNumber;
474   if (pObj->IsBoolean())
475     return kFXJSValueNameBoolean;
476   if (pObj->IsDate())
477     return kFXJSValueNameDate;
478   if (pObj->IsObject())
479     return kFXJSValueNameObject;
480   if (pObj->IsNull())
481     return kFXJSValueNameNull;
482   if (pObj->IsUndefined())
483     return kFXJSValueNameUndefined;
484   return NULL;
485 }
486
487 void FXJS_SetPrivate(v8::Isolate* pIsolate,
488                      v8::Local<v8::Object> pObj,
489                      void* p) {
490   if (pObj.IsEmpty() || !pObj->InternalFieldCount())
491     return;
492   CFXJS_PrivateData* pPrivateData =
493       (CFXJS_PrivateData*)pObj->GetAlignedPointerFromInternalField(0);
494   if (!pPrivateData)
495     return;
496   pPrivateData->pPrivate = p;
497 }
498
499 void* FXJS_GetPrivate(v8::Isolate* pIsolate, v8::Local<v8::Object> pObj) {
500   if (pObj.IsEmpty())
501     return nullptr;
502   CFXJS_PrivateData* pPrivateData = nullptr;
503   if (pObj->InternalFieldCount()) {
504     pPrivateData =
505         (CFXJS_PrivateData*)pObj->GetAlignedPointerFromInternalField(0);
506   } else {
507     // It could be a global proxy object.
508     v8::Local<v8::Value> v = pObj->GetPrototype();
509     v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
510     if (v->IsObject()) {
511       pPrivateData = (CFXJS_PrivateData*)v->ToObject(context)
512                          .ToLocalChecked()
513                          ->GetAlignedPointerFromInternalField(0);
514     }
515   }
516   return pPrivateData ? pPrivateData->pPrivate : nullptr;
517 }
518
519 void FXJS_FreePrivate(void* pPrivateData) {
520   delete (CFXJS_PrivateData*)pPrivateData;
521 }
522
523 void FXJS_FreePrivate(v8::Local<v8::Object> pObj) {
524   if (pObj.IsEmpty() || !pObj->InternalFieldCount())
525     return;
526   FXJS_FreePrivate(pObj->GetAlignedPointerFromInternalField(0));
527   pObj->SetAlignedPointerInInternalField(0, NULL);
528 }
529
530 v8::Local<v8::String> FXJS_WSToJSString(v8::Isolate* pIsolate,
531                                         const wchar_t* PropertyName,
532                                         int Len) {
533   CFX_WideString ws = CFX_WideString(PropertyName, Len);
534   CFX_ByteString bs = ws.UTF8Encode();
535   if (!pIsolate)
536     pIsolate = v8::Isolate::GetCurrent();
537   return v8::String::NewFromUtf8(pIsolate, bs.c_str(),
538                                  v8::NewStringType::kNormal).ToLocalChecked();
539 }
540
541 v8::Local<v8::Value> FXJS_GetObjectElement(v8::Isolate* pIsolate,
542                                            v8::Local<v8::Object> pObj,
543                                            const wchar_t* PropertyName) {
544   if (pObj.IsEmpty())
545     return v8::Local<v8::Value>();
546   v8::Local<v8::Value> val;
547   if (!pObj->Get(pIsolate->GetCurrentContext(),
548                  FXJS_WSToJSString(pIsolate, PropertyName)).ToLocal(&val))
549     return v8::Local<v8::Value>();
550   return val;
551 }
552
553 v8::Local<v8::Array> FXJS_GetObjectElementNames(v8::Isolate* pIsolate,
554                                                 v8::Local<v8::Object> pObj) {
555   if (pObj.IsEmpty())
556     return v8::Local<v8::Array>();
557   v8::Local<v8::Array> val;
558   if (!pObj->GetPropertyNames(pIsolate->GetCurrentContext()).ToLocal(&val))
559     return v8::Local<v8::Array>();
560   return val;
561 }
562
563 void FXJS_PutObjectString(v8::Isolate* pIsolate,
564                           v8::Local<v8::Object> pObj,
565                           const wchar_t* PropertyName,
566                           const wchar_t* sValue)  // VT_string
567 {
568   if (pObj.IsEmpty())
569     return;
570   pObj->Set(pIsolate->GetCurrentContext(),
571             FXJS_WSToJSString(pIsolate, PropertyName),
572             FXJS_WSToJSString(pIsolate, sValue)).FromJust();
573 }
574
575 void FXJS_PutObjectNumber(v8::Isolate* pIsolate,
576                           v8::Local<v8::Object> pObj,
577                           const wchar_t* PropertyName,
578                           int nValue) {
579   if (pObj.IsEmpty())
580     return;
581   pObj->Set(pIsolate->GetCurrentContext(),
582             FXJS_WSToJSString(pIsolate, PropertyName),
583             v8::Int32::New(pIsolate, nValue)).FromJust();
584 }
585
586 void FXJS_PutObjectNumber(v8::Isolate* pIsolate,
587                           v8::Local<v8::Object> pObj,
588                           const wchar_t* PropertyName,
589                           float fValue) {
590   if (pObj.IsEmpty())
591     return;
592   pObj->Set(pIsolate->GetCurrentContext(),
593             FXJS_WSToJSString(pIsolate, PropertyName),
594             v8::Number::New(pIsolate, (double)fValue)).FromJust();
595 }
596
597 void FXJS_PutObjectNumber(v8::Isolate* pIsolate,
598                           v8::Local<v8::Object> pObj,
599                           const wchar_t* PropertyName,
600                           double dValue) {
601   if (pObj.IsEmpty())
602     return;
603   pObj->Set(pIsolate->GetCurrentContext(),
604             FXJS_WSToJSString(pIsolate, PropertyName),
605             v8::Number::New(pIsolate, (double)dValue)).FromJust();
606 }
607
608 void FXJS_PutObjectBoolean(v8::Isolate* pIsolate,
609                            v8::Local<v8::Object> pObj,
610                            const wchar_t* PropertyName,
611                            bool bValue) {
612   if (pObj.IsEmpty())
613     return;
614   pObj->Set(pIsolate->GetCurrentContext(),
615             FXJS_WSToJSString(pIsolate, PropertyName),
616             v8::Boolean::New(pIsolate, bValue)).FromJust();
617 }
618
619 void FXJS_PutObjectObject(v8::Isolate* pIsolate,
620                           v8::Local<v8::Object> pObj,
621                           const wchar_t* PropertyName,
622                           v8::Local<v8::Object> pPut) {
623   if (pObj.IsEmpty())
624     return;
625   pObj->Set(pIsolate->GetCurrentContext(),
626             FXJS_WSToJSString(pIsolate, PropertyName), pPut).FromJust();
627 }
628
629 void FXJS_PutObjectNull(v8::Isolate* pIsolate,
630                         v8::Local<v8::Object> pObj,
631                         const wchar_t* PropertyName) {
632   if (pObj.IsEmpty())
633     return;
634   pObj->Set(pIsolate->GetCurrentContext(),
635             FXJS_WSToJSString(pIsolate, PropertyName),
636             v8::Local<v8::Object>()).FromJust();
637 }
638
639 v8::Local<v8::Array> FXJS_NewArray(v8::Isolate* pIsolate) {
640   return v8::Array::New(pIsolate);
641 }
642
643 unsigned FXJS_PutArrayElement(v8::Isolate* pIsolate,
644                               v8::Local<v8::Array> pArray,
645                               unsigned index,
646                               v8::Local<v8::Value> pValue) {
647   if (pArray.IsEmpty())
648     return 0;
649   if (pArray->Set(pIsolate->GetCurrentContext(), index, pValue).IsNothing())
650     return 0;
651   return 1;
652 }
653
654 v8::Local<v8::Value> FXJS_GetArrayElement(v8::Isolate* pIsolate,
655                                           v8::Local<v8::Array> pArray,
656                                           unsigned index) {
657   if (pArray.IsEmpty())
658     return v8::Local<v8::Value>();
659   v8::Local<v8::Value> val;
660   if (!pArray->Get(pIsolate->GetCurrentContext(), index).ToLocal(&val))
661     return v8::Local<v8::Value>();
662   return val;
663 }
664
665 unsigned FXJS_GetArrayLength(v8::Local<v8::Array> pArray) {
666   if (pArray.IsEmpty())
667     return 0;
668   return pArray->Length();
669 }
670
671 v8::Local<v8::Value> FXJS_NewNumber(v8::Isolate* pIsolate, int number) {
672   return v8::Int32::New(pIsolate, number);
673 }
674
675 v8::Local<v8::Value> FXJS_NewNumber(v8::Isolate* pIsolate, double number) {
676   return v8::Number::New(pIsolate, number);
677 }
678
679 v8::Local<v8::Value> FXJS_NewNumber(v8::Isolate* pIsolate, float number) {
680   return v8::Number::New(pIsolate, (float)number);
681 }
682
683 v8::Local<v8::Value> FXJS_NewBoolean(v8::Isolate* pIsolate, bool b) {
684   return v8::Boolean::New(pIsolate, b);
685 }
686
687 v8::Local<v8::Value> FXJS_NewObject(v8::Isolate* pIsolate,
688                                     v8::Local<v8::Object> pObj) {
689   if (pObj.IsEmpty())
690     return v8::Local<v8::Value>();
691   return pObj->Clone();
692 }
693
694 v8::Local<v8::Value> FXJS_NewObject2(v8::Isolate* pIsolate,
695                                      v8::Local<v8::Array> pObj) {
696   if (pObj.IsEmpty())
697     return v8::Local<v8::Value>();
698   return pObj->Clone();
699 }
700
701 v8::Local<v8::Value> FXJS_NewString(v8::Isolate* pIsolate,
702                                     const wchar_t* string) {
703   return FXJS_WSToJSString(pIsolate, string);
704 }
705
706 v8::Local<v8::Value> FXJS_NewNull() {
707   return v8::Local<v8::Value>();
708 }
709
710 v8::Local<v8::Value> FXJS_NewDate(v8::Isolate* pIsolate, double d) {
711   return v8::Date::New(pIsolate->GetCurrentContext(), d).ToLocalChecked();
712 }
713
714 int FXJS_ToInt32(v8::Isolate* pIsolate, v8::Local<v8::Value> pValue) {
715   if (pValue.IsEmpty())
716     return 0;
717   v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
718   return pValue->ToInt32(context).ToLocalChecked()->Value();
719 }
720
721 bool FXJS_ToBoolean(v8::Isolate* pIsolate, v8::Local<v8::Value> pValue) {
722   if (pValue.IsEmpty())
723     return false;
724   v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
725   return pValue->ToBoolean(context).ToLocalChecked()->Value();
726 }
727
728 double FXJS_ToNumber(v8::Isolate* pIsolate, v8::Local<v8::Value> pValue) {
729   if (pValue.IsEmpty())
730     return 0.0;
731   v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
732   return pValue->ToNumber(context).ToLocalChecked()->Value();
733 }
734
735 v8::Local<v8::Object> FXJS_ToObject(v8::Isolate* pIsolate,
736                                     v8::Local<v8::Value> pValue) {
737   if (pValue.IsEmpty())
738     return v8::Local<v8::Object>();
739   v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
740   return pValue->ToObject(context).ToLocalChecked();
741 }
742
743 CFX_WideString FXJS_ToString(v8::Isolate* pIsolate,
744                              v8::Local<v8::Value> pValue) {
745   if (pValue.IsEmpty())
746     return L"";
747   v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
748   v8::String::Utf8Value s(pValue->ToString(context).ToLocalChecked());
749   return CFX_WideString::FromUTF8(*s, s.length());
750 }
751
752 v8::Local<v8::Array> FXJS_ToArray(v8::Isolate* pIsolate,
753                                   v8::Local<v8::Value> pValue) {
754   if (pValue.IsEmpty())
755     return v8::Local<v8::Array>();
756   v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
757   return v8::Local<v8::Array>::Cast(pValue->ToObject(context).ToLocalChecked());
758 }
759
760 void FXJS_ValueCopy(v8::Local<v8::Value>& pTo, v8::Local<v8::Value> pFrom) {
761   pTo = pFrom;
762 }
763
764