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