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