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