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