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 
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_PerObjectData {
32  public:
CFXJS_PerObjectData(int nObjDefID)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 
40 class CFXJS_ObjDefinition {
41  public:
MaxID(v8::Isolate * pIsolate)42   static int MaxID(v8::Isolate* pIsolate) {
43     return FXJS_PerIsolateData::Get(pIsolate)->m_ObjectDefnArray.size();
44   }
45 
ForID(v8::Isolate * pIsolate,int id)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 
CFXJS_ObjDefinition(v8::Isolate * isolate,const wchar_t * sObjName,FXJSOBJTYPE eObjType,FXJS_CONSTRUCTOR pConstructor,FXJS_DESTRUCTOR pDestructor)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 
AssignID()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 
GetInstanceTemplate()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 
GetSignature()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 
GetGlobalObjectTemplate(v8::Isolate * pIsolate)100 static 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 
Allocate(size_t length)116 void* FXJS_ArrayBufferAllocator::Allocate(size_t length) {
117   return calloc(1, length);
118 }
119 
AllocateUninitialized(size_t length)120 void* FXJS_ArrayBufferAllocator::AllocateUninitialized(size_t length) {
121   return malloc(length);
122 }
123 
Free(void * data,size_t length)124 void FXJS_ArrayBufferAllocator::Free(void* data, size_t length) {
125   free(data);
126 }
127 
FXJS_Initialize(unsigned int embedderDataSlot,v8::Isolate * pIsolate)128 void 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 
FXJS_Release()138 void 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 
FXJS_GetIsolate(v8::Isolate ** pResultIsolate)148 bool 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 
FXJS_GlobalIsolateRefCount()162 size_t FXJS_GlobalIsolateRefCount() {
163   return g_isolate_ref_count;
164 }
165 
166 // static
SetUp(v8::Isolate * pIsolate)167 void FXJS_PerIsolateData::SetUp(v8::Isolate* pIsolate) {
168   if (!pIsolate->GetData(g_embedderDataSlot))
169     pIsolate->SetData(g_embedderDataSlot, new FXJS_PerIsolateData());
170 }
171 
172 // static
Get(v8::Isolate * pIsolate)173 FXJS_PerIsolateData* FXJS_PerIsolateData::Get(v8::Isolate* pIsolate) {
174   return static_cast<FXJS_PerIsolateData*>(
175       pIsolate->GetData(g_embedderDataSlot));
176 }
177 
FXJS_DefineObj(v8::Isolate * pIsolate,const wchar_t * sObjName,FXJSOBJTYPE eObjType,FXJS_CONSTRUCTOR pConstructor,FXJS_DESTRUCTOR pDestructor)178 int 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 
FXJS_DefineObjMethod(v8::Isolate * pIsolate,int nObjDefnID,const wchar_t * sMethodName,v8::FunctionCallback pMethodCall)192 void 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 
FXJS_DefineObjProperty(v8::Isolate * pIsolate,int nObjDefnID,const wchar_t * sPropName,v8::AccessorGetterCallback pPropGet,v8::AccessorSetterCallback pPropPut)209 void 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 
FXJS_DefineObjAllProperties(v8::Isolate * pIsolate,int nObjDefnID,v8::NamedPropertyQueryCallback pPropQurey,v8::NamedPropertyGetterCallback pPropGet,v8::NamedPropertySetterCallback pPropPut,v8::NamedPropertyDeleterCallback pPropDel)225 void 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 
FXJS_DefineObjConst(v8::Isolate * pIsolate,int nObjDefnID,const wchar_t * sConstName,v8::Local<v8::Value> pDefault)239 void 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 
FXJS_DefineGlobalMethod(v8::Isolate * pIsolate,const wchar_t * sMethodName,v8::FunctionCallback pMethodCall)251 void 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 
FXJS_DefineGlobalConst(v8::Isolate * pIsolate,const wchar_t * sConstName,v8::Local<v8::Value> pDefault)263 void 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 
FXJS_InitializeRuntime(v8::Isolate * pIsolate,IJS_Runtime * pIRuntime,v8::Global<v8::Context> * pV8PersistentContext,std::vector<v8::Global<v8::Object> * > * pStaticObjects)275 void 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 
FXJS_ReleaseRuntime(v8::Isolate * pIsolate,v8::Global<v8::Context> * pV8PersistentContext,std::vector<v8::Global<v8::Object> * > * pStaticObjects)326 void 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 
FXJS_GetRuntimeFromIsolate(v8::Isolate * pIsolate)377 IJS_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
FXJS_SetRuntimeForV8Context(v8::Local<v8::Context> v8Context,IJS_Runtime * pIRuntime)384 void FXJS_SetRuntimeForV8Context(v8::Local<v8::Context> v8Context,
385                                  IJS_Runtime* pIRuntime) {
386   v8Context->SetAlignedPointerInEmbedderData(kPerContextDataIndex, pIRuntime);
387 }
388 #endif  // PDF_ENABLE_XFA
389 
FXJS_Execute(v8::Isolate * pIsolate,IJS_Context * pJSContext,const wchar_t * script,FXJSErr * pError)390 int 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 
FXJS_NewFxDynamicObj(v8::Isolate * pIsolate,IJS_Runtime * pIRuntime,int nObjDefnID)418 v8::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 
FXJS_GetThisObj(v8::Isolate * pIsolate)451 v8::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 
FXJS_GetObjDefnID(v8::Local<v8::Object> pObj)461 int 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 
FXJS_Error(v8::Isolate * pIsolate,const CFX_WideString & message)471 void 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 
FXJS_GetTypeof(v8::Local<v8::Value> pObj)481 const 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 
FXJS_SetPrivate(v8::Isolate * pIsolate,v8::Local<v8::Object> pObj,void * p)501 void 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 
FXJS_GetPrivate(v8::Isolate * pIsolate,v8::Local<v8::Object> pObj)513 void* 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 
FXJS_FreePrivate(void * pPerObjectData)534 void FXJS_FreePrivate(void* pPerObjectData) {
535   delete static_cast<CFXJS_PerObjectData*>(pPerObjectData);
536 }
537 
FXJS_FreePrivate(v8::Local<v8::Object> pObj)538 void 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 
FXJS_WSToJSString(v8::Isolate * pIsolate,const wchar_t * PropertyName,int Len)545 v8::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 
FXJS_GetObjectElement(v8::Isolate * pIsolate,v8::Local<v8::Object> pObj,const wchar_t * PropertyName)556 v8::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 
FXJS_GetObjectElementNames(v8::Isolate * pIsolate,v8::Local<v8::Object> pObj)568 v8::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 
FXJS_PutObjectString(v8::Isolate * pIsolate,v8::Local<v8::Object> pObj,const wchar_t * PropertyName,const wchar_t * sValue)578 void 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 
FXJS_PutObjectNumber(v8::Isolate * pIsolate,v8::Local<v8::Object> pObj,const wchar_t * PropertyName,int nValue)589 void 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 
FXJS_PutObjectNumber(v8::Isolate * pIsolate,v8::Local<v8::Object> pObj,const wchar_t * PropertyName,float fValue)600 void 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 
FXJS_PutObjectNumber(v8::Isolate * pIsolate,v8::Local<v8::Object> pObj,const wchar_t * PropertyName,double dValue)611 void 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 
FXJS_PutObjectBoolean(v8::Isolate * pIsolate,v8::Local<v8::Object> pObj,const wchar_t * PropertyName,bool bValue)622 void 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 
FXJS_PutObjectObject(v8::Isolate * pIsolate,v8::Local<v8::Object> pObj,const wchar_t * PropertyName,v8::Local<v8::Object> pPut)633 void 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 
FXJS_PutObjectNull(v8::Isolate * pIsolate,v8::Local<v8::Object> pObj,const wchar_t * PropertyName)643 void 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 
FXJS_NewArray(v8::Isolate * pIsolate)653 v8::Local<v8::Array> FXJS_NewArray(v8::Isolate* pIsolate) {
654   return v8::Array::New(pIsolate);
655 }
656 
FXJS_PutArrayElement(v8::Isolate * pIsolate,v8::Local<v8::Array> pArray,unsigned index,v8::Local<v8::Value> pValue)657 unsigned 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 
FXJS_GetArrayElement(v8::Isolate * pIsolate,v8::Local<v8::Array> pArray,unsigned index)668 v8::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 
FXJS_GetArrayLength(v8::Local<v8::Array> pArray)679 unsigned FXJS_GetArrayLength(v8::Local<v8::Array> pArray) {
680   if (pArray.IsEmpty())
681     return 0;
682   return pArray->Length();
683 }
684 
FXJS_NewNumber(v8::Isolate * pIsolate,int number)685 v8::Local<v8::Value> FXJS_NewNumber(v8::Isolate* pIsolate, int number) {
686   return v8::Int32::New(pIsolate, number);
687 }
688 
FXJS_NewNumber(v8::Isolate * pIsolate,double number)689 v8::Local<v8::Value> FXJS_NewNumber(v8::Isolate* pIsolate, double number) {
690   return v8::Number::New(pIsolate, number);
691 }
692 
FXJS_NewNumber(v8::Isolate * pIsolate,float number)693 v8::Local<v8::Value> FXJS_NewNumber(v8::Isolate* pIsolate, float number) {
694   return v8::Number::New(pIsolate, (float)number);
695 }
696 
FXJS_NewBoolean(v8::Isolate * pIsolate,bool b)697 v8::Local<v8::Value> FXJS_NewBoolean(v8::Isolate* pIsolate, bool b) {
698   return v8::Boolean::New(pIsolate, b);
699 }
700 
FXJS_NewObject(v8::Isolate * pIsolate,v8::Local<v8::Object> pObj)701 v8::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 
FXJS_NewObject2(v8::Isolate * pIsolate,v8::Local<v8::Array> pObj)708 v8::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 
FXJS_NewString(v8::Isolate * pIsolate,const wchar_t * string)715 v8::Local<v8::Value> FXJS_NewString(v8::Isolate* pIsolate,
716                                     const wchar_t* string) {
717   return FXJS_WSToJSString(pIsolate, string);
718 }
719 
FXJS_NewNull()720 v8::Local<v8::Value> FXJS_NewNull() {
721   return v8::Local<v8::Value>();
722 }
723 
FXJS_NewDate(v8::Isolate * pIsolate,double d)724 v8::Local<v8::Value> FXJS_NewDate(v8::Isolate* pIsolate, double d) {
725   return v8::Date::New(pIsolate->GetCurrentContext(), d).ToLocalChecked();
726 }
727 
FXJS_ToInt32(v8::Isolate * pIsolate,v8::Local<v8::Value> pValue)728 int 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 
FXJS_ToBoolean(v8::Isolate * pIsolate,v8::Local<v8::Value> pValue)735 bool 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 
FXJS_ToNumber(v8::Isolate * pIsolate,v8::Local<v8::Value> pValue)742 double 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 
FXJS_ToObject(v8::Isolate * pIsolate,v8::Local<v8::Value> pValue)749 v8::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 
FXJS_ToString(v8::Isolate * pIsolate,v8::Local<v8::Value> pValue)757 CFX_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 
FXJS_ToArray(v8::Isolate * pIsolate,v8::Local<v8::Value> pValue)766 v8::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 
FXJS_ValueCopy(v8::Local<v8::Value> & pTo,v8::Local<v8::Value> pFrom)774 void FXJS_ValueCopy(v8::Local<v8::Value>& pTo, v8::Local<v8::Value> pFrom) {
775   pTo = pFrom;
776 }
777 
778 
779