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