Fix strings, remove stringify macros, void return types for Consts.h.
[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 "../../../core/include/fxcrt/fx_ext.h"
9 #include "../../include/jsapi/fxjs_v8.h"
10 #include "../../include/fsdk_define.h"
11 #include "time.h"
12 #include <cmath>
13 #include <limits>
14
15 const wchar_t kFXJSValueNameString[] = L"string";
16 const wchar_t kFXJSValueNameNumber[] = L"number";
17 const wchar_t kFXJSValueNameBoolean[] = L"boolean";
18 const wchar_t kFXJSValueNameDate[] = L"date";
19 const wchar_t kFXJSValueNameObject[] = L"object";
20 const wchar_t kFXJSValueNameFxobj[] = L"fxobj";
21 const wchar_t kFXJSValueNameNull[] = L"null";
22 const wchar_t kFXJSValueNameUndefined[] = L"undefined";
23
24 const static FX_DWORD g_nan[2] = {0, 0x7FF80000};
25 static double GetNan() {
26   return *(double*)g_nan;
27 }
28 static unsigned int g_embedderDataSlot = 0u;
29
30 class CJS_PrivateData {
31  public:
32   CJS_PrivateData() : ObjDefID(-1), pPrivate(NULL) {}
33   int ObjDefID;
34   void* pPrivate;
35 };
36
37 class CJS_ObjDefintion {
38  public:
39   CJS_ObjDefintion(v8::Isolate* isolate,
40                    const wchar_t* sObjName,
41                    FXJSOBJTYPE eObjType,
42                    LP_CONSTRUCTOR pConstructor,
43                    LP_DESTRUCTOR pDestructor)
44       : objName(sObjName),
45         objType(eObjType),
46         m_pConstructor(pConstructor),
47         m_pDestructor(pDestructor),
48         m_bSetAsGlobalObject(FALSE) {
49     v8::Isolate::Scope isolate_scope(isolate);
50     v8::HandleScope handle_scope(isolate);
51
52     v8::Local<v8::ObjectTemplate> objTemplate =
53         v8::ObjectTemplate::New(isolate);
54     objTemplate->SetInternalFieldCount(2);
55     m_objTemplate.Reset(isolate, objTemplate);
56
57     // Document as the global object.
58     if (FXSYS_wcscmp(sObjName, L"Document") == 0) {
59       m_bSetAsGlobalObject = TRUE;
60     }
61   }
62   ~CJS_ObjDefintion() {
63     m_objTemplate.Reset();
64     m_StaticObj.Reset();
65   }
66
67  public:
68   const wchar_t* objName;
69   FXJSOBJTYPE objType;
70   LP_CONSTRUCTOR m_pConstructor;
71   LP_DESTRUCTOR m_pDestructor;
72   FX_BOOL m_bSetAsGlobalObject;
73
74   v8::Global<v8::ObjectTemplate> m_objTemplate;
75   v8::Global<v8::Object> m_StaticObj;
76 };
77
78 int JS_DefineObj(v8::Isolate* pIsolate,
79                  const wchar_t* sObjName,
80                  FXJSOBJTYPE eObjType,
81                  LP_CONSTRUCTOR pConstructor,
82                  LP_DESTRUCTOR pDestructor) {
83   v8::Isolate::Scope isolate_scope(pIsolate);
84   v8::HandleScope handle_scope(pIsolate);
85   CFX_PtrArray* pArray = (CFX_PtrArray*)pIsolate->GetData(g_embedderDataSlot);
86   if (!pArray) {
87     pArray = new CFX_PtrArray();
88     pIsolate->SetData(g_embedderDataSlot, pArray);
89   }
90   CJS_ObjDefintion* pObjDef = new CJS_ObjDefintion(pIsolate, sObjName, eObjType,
91                                                    pConstructor, pDestructor);
92   pArray->Add(pObjDef);
93   return pArray->GetSize() - 1;
94 }
95
96 void JS_DefineObjMethod(v8::Isolate* pIsolate,
97                         int nObjDefnID,
98                         const wchar_t* sMethodName,
99                         v8::FunctionCallback pMethodCall) {
100   v8::Isolate::Scope isolate_scope(pIsolate);
101   v8::HandleScope handle_scope(pIsolate);
102
103   CFX_WideString ws = CFX_WideString(sMethodName);
104   CFX_ByteString bsMethodName = ws.UTF8Encode();
105   CFX_PtrArray* pArray = (CFX_PtrArray*)pIsolate->GetData(g_embedderDataSlot);
106
107   // Note: GetAt() halts if out-of-range even in release builds.
108   CJS_ObjDefintion* pObjDef = (CJS_ObjDefintion*)pArray->GetAt(nObjDefnID);
109   v8::Local<v8::ObjectTemplate> objTemp =
110       v8::Local<v8::ObjectTemplate>::New(pIsolate, pObjDef->m_objTemplate);
111   objTemp->Set(
112       v8::String::NewFromUtf8(pIsolate, bsMethodName.c_str(),
113                               v8::NewStringType::kNormal).ToLocalChecked(),
114       v8::FunctionTemplate::New(pIsolate, pMethodCall), v8::ReadOnly);
115   pObjDef->m_objTemplate.Reset(pIsolate, objTemp);
116 }
117
118 void JS_DefineObjProperty(v8::Isolate* pIsolate,
119                           int nObjDefnID,
120                           const wchar_t* sPropName,
121                           v8::AccessorGetterCallback pPropGet,
122                           v8::AccessorSetterCallback pPropPut) {
123   v8::Isolate::Scope isolate_scope(pIsolate);
124   v8::HandleScope handle_scope(pIsolate);
125
126   CFX_WideString ws = CFX_WideString(sPropName);
127   CFX_ByteString bsPropertyName = ws.UTF8Encode();
128   CFX_PtrArray* pArray = (CFX_PtrArray*)pIsolate->GetData(g_embedderDataSlot);
129
130   // Note: GetAt() halts if out-of-range even in release builds.
131   CJS_ObjDefintion* pObjDef = (CJS_ObjDefintion*)pArray->GetAt(nObjDefnID);
132   v8::Local<v8::ObjectTemplate> objTemp =
133       v8::Local<v8::ObjectTemplate>::New(pIsolate, pObjDef->m_objTemplate);
134   objTemp->SetAccessor(
135       v8::String::NewFromUtf8(pIsolate, bsPropertyName.c_str(),
136                               v8::NewStringType::kNormal).ToLocalChecked(),
137       pPropGet, pPropPut);
138   pObjDef->m_objTemplate.Reset(pIsolate, objTemp);
139 }
140
141 void JS_DefineObjAllProperties(v8::Isolate* pIsolate,
142                                int nObjDefnID,
143                                v8::NamedPropertyQueryCallback pPropQurey,
144                                v8::NamedPropertyGetterCallback pPropGet,
145                                v8::NamedPropertySetterCallback pPropPut,
146                                v8::NamedPropertyDeleterCallback pPropDel) {
147   v8::Isolate::Scope isolate_scope(pIsolate);
148   v8::HandleScope handle_scope(pIsolate);
149   CFX_PtrArray* pArray = (CFX_PtrArray*)pIsolate->GetData(g_embedderDataSlot);
150
151   // Note: GetAt() halts if out-of-range even in release builds.
152   CJS_ObjDefintion* pObjDef = (CJS_ObjDefintion*)pArray->GetAt(nObjDefnID);
153   v8::Local<v8::ObjectTemplate> objTemp =
154       v8::Local<v8::ObjectTemplate>::New(pIsolate, pObjDef->m_objTemplate);
155   objTemp->SetNamedPropertyHandler(pPropGet, pPropPut, pPropQurey, pPropDel);
156   pObjDef->m_objTemplate.Reset(pIsolate, objTemp);
157 }
158
159 void JS_DefineObjConst(v8::Isolate* pIsolate,
160                        int nObjDefnID,
161                        const wchar_t* sConstName,
162                        v8::Local<v8::Value> pDefault) {
163   v8::Isolate::Scope isolate_scope(pIsolate);
164   v8::HandleScope handle_scope(pIsolate);
165
166   CFX_WideString ws = CFX_WideString(sConstName);
167   CFX_ByteString bsConstName = ws.UTF8Encode();
168   CFX_PtrArray* pArray = (CFX_PtrArray*)pIsolate->GetData(g_embedderDataSlot);
169
170   // Note: GetAt() halts if out-of-range even in release builds.
171   CJS_ObjDefintion* pObjDef = (CJS_ObjDefintion*)pArray->GetAt(nObjDefnID);
172   v8::Local<v8::ObjectTemplate> objTemp =
173       v8::Local<v8::ObjectTemplate>::New(pIsolate, pObjDef->m_objTemplate);
174   objTemp->Set(pIsolate, bsConstName.c_str(), pDefault);
175   pObjDef->m_objTemplate.Reset(pIsolate, objTemp);
176 }
177
178 static v8::Global<v8::ObjectTemplate>& _getGlobalObjectTemplate(
179     v8::Isolate* pIsolate) {
180   v8::Isolate::Scope isolate_scope(pIsolate);
181   v8::HandleScope handle_scope(pIsolate);
182
183   CFX_PtrArray* pArray = (CFX_PtrArray*)pIsolate->GetData(g_embedderDataSlot);
184   ASSERT(pArray != NULL);
185   for (int i = 0; i < pArray->GetSize(); i++) {
186     CJS_ObjDefintion* pObjDef = (CJS_ObjDefintion*)pArray->GetAt(i);
187     if (pObjDef->m_bSetAsGlobalObject)
188       return pObjDef->m_objTemplate;
189   }
190   static v8::Global<v8::ObjectTemplate> gloabalObjectTemplate;
191   return gloabalObjectTemplate;
192 }
193
194 void JS_DefineGlobalMethod(v8::Isolate* pIsolate,
195                            const wchar_t* sMethodName,
196                            v8::FunctionCallback pMethodCall) {
197   v8::Isolate::Scope isolate_scope(pIsolate);
198   v8::HandleScope handle_scope(pIsolate);
199
200   CFX_WideString ws = CFX_WideString(sMethodName);
201   CFX_ByteString bsMethodName = ws.UTF8Encode();
202
203   v8::Local<v8::FunctionTemplate> funTempl =
204       v8::FunctionTemplate::New(pIsolate, pMethodCall);
205   v8::Local<v8::ObjectTemplate> objTemp;
206
207   v8::Global<v8::ObjectTemplate>& globalObjTemp =
208       _getGlobalObjectTemplate(pIsolate);
209   if (globalObjTemp.IsEmpty())
210     objTemp = v8::ObjectTemplate::New(pIsolate);
211   else
212     objTemp = v8::Local<v8::ObjectTemplate>::New(pIsolate, globalObjTemp);
213   objTemp->Set(
214       v8::String::NewFromUtf8(pIsolate, bsMethodName.c_str(),
215                               v8::NewStringType::kNormal).ToLocalChecked(),
216       funTempl, v8::ReadOnly);
217
218   globalObjTemp.Reset(pIsolate, objTemp);
219 }
220
221 void JS_DefineGlobalConst(v8::Isolate* pIsolate,
222                           const wchar_t* sConstName,
223                           v8::Local<v8::Value> pDefault) {
224   v8::Isolate::Scope isolate_scope(pIsolate);
225   v8::HandleScope handle_scope(pIsolate);
226
227   CFX_WideString ws = CFX_WideString(sConstName);
228   CFX_ByteString bsConst = ws.UTF8Encode();
229
230   v8::Local<v8::ObjectTemplate> objTemp;
231
232   v8::Global<v8::ObjectTemplate>& globalObjTemp =
233       _getGlobalObjectTemplate(pIsolate);
234   if (globalObjTemp.IsEmpty())
235     objTemp = v8::ObjectTemplate::New(pIsolate);
236   else
237     objTemp = v8::Local<v8::ObjectTemplate>::New(pIsolate, globalObjTemp);
238   objTemp->Set(
239       v8::String::NewFromUtf8(pIsolate, bsConst.c_str(),
240                               v8::NewStringType::kNormal).ToLocalChecked(),
241       pDefault, v8::ReadOnly);
242
243   globalObjTemp.Reset(pIsolate, objTemp);
244 }
245
246 void JS_InitialRuntime(v8::Isolate* pIsolate,
247                        IFXJS_Runtime* pFXRuntime,
248                        IFXJS_Context* context,
249                        v8::Global<v8::Context>& v8PersistentContext) {
250   v8::Isolate::Scope isolate_scope(pIsolate);
251   v8::HandleScope handle_scope(pIsolate);
252
253   v8::Global<v8::ObjectTemplate>& globalObjTemp =
254       _getGlobalObjectTemplate(pIsolate);
255   v8::Local<v8::Context> v8Context = v8::Context::New(
256       pIsolate, NULL,
257       v8::Local<v8::ObjectTemplate>::New(pIsolate, globalObjTemp));
258   v8::Context::Scope context_scope(v8Context);
259
260   v8::Local<v8::External> ptr = v8::External::New(pIsolate, pFXRuntime);
261   v8Context->SetEmbedderData(1, ptr);
262
263   CFX_PtrArray* pArray = (CFX_PtrArray*)pIsolate->GetData(g_embedderDataSlot);
264   if (!pArray)
265     return;
266
267   for (int i = 0; i < pArray->GetSize(); i++) {
268     CJS_ObjDefintion* pObjDef = (CJS_ObjDefintion*)pArray->GetAt(i);
269     CFX_WideString ws = CFX_WideString(pObjDef->objName);
270     CFX_ByteString bs = ws.UTF8Encode();
271     v8::Local<v8::String> objName =
272         v8::String::NewFromUtf8(pIsolate, bs.c_str(),
273                                 v8::NewStringType::kNormal,
274                                 bs.GetLength()).ToLocalChecked();
275
276     if (pObjDef->objType == JS_DYNAMIC) {
277       // Document is set as global object, need to construct it first.
278       if (ws.Equal(L"Document")) {
279         CJS_PrivateData* pPrivateData = new CJS_PrivateData;
280         pPrivateData->ObjDefID = i;
281
282         v8Context->Global()
283             ->GetPrototype()
284             ->ToObject(v8Context)
285             .ToLocalChecked()
286             ->SetAlignedPointerInInternalField(0, pPrivateData);
287
288         if (pObjDef->m_pConstructor)
289           pObjDef->m_pConstructor(context, v8Context->Global()
290                                                ->GetPrototype()
291                                                ->ToObject(v8Context)
292                                                .ToLocalChecked(),
293                                   v8Context->Global()
294                                       ->GetPrototype()
295                                       ->ToObject(v8Context)
296                                       .ToLocalChecked());
297       }
298     } else {
299       v8::Local<v8::Object> obj = JS_NewFxDynamicObj(pIsolate, context, i);
300       v8Context->Global()->Set(v8Context, objName, obj).FromJust();
301       pObjDef->m_StaticObj.Reset(pIsolate, obj);
302     }
303   }
304   v8PersistentContext.Reset(pIsolate, v8Context);
305 }
306
307 void JS_ReleaseRuntime(v8::Isolate* pIsolate,
308                        v8::Global<v8::Context>& v8PersistentContext) {
309   v8::Isolate::Scope isolate_scope(pIsolate);
310   v8::HandleScope handle_scope(pIsolate);
311   v8::Local<v8::Context> context =
312       v8::Local<v8::Context>::New(pIsolate, v8PersistentContext);
313   v8::Context::Scope context_scope(context);
314
315   CFX_PtrArray* pArray = (CFX_PtrArray*)pIsolate->GetData(g_embedderDataSlot);
316   if (!pArray)
317     return;
318
319   for (int i = 0; i < pArray->GetSize(); i++) {
320     CJS_ObjDefintion* pObjDef = (CJS_ObjDefintion*)pArray->GetAt(i);
321     if (!pObjDef->m_StaticObj.IsEmpty()) {
322       v8::Local<v8::Object> pObj =
323           v8::Local<v8::Object>::New(pIsolate, pObjDef->m_StaticObj);
324       if (pObjDef->m_pDestructor)
325         pObjDef->m_pDestructor(pObj);
326       JS_FreePrivate(pObj);
327     }
328     delete pObjDef;
329   }
330   delete pArray;
331   pIsolate->SetData(g_embedderDataSlot, NULL);
332 }
333
334 void JS_Initial(unsigned int embedderDataSlot) {
335   g_embedderDataSlot = embedderDataSlot;
336 }
337
338 void JS_Release() {
339 }
340
341 int JS_Execute(v8::Isolate* pIsolate,
342                IFXJS_Context* pJSContext,
343                const wchar_t* script,
344                long length,
345                FXJSErr* perror) {
346   v8::Isolate::Scope isolate_scope(pIsolate);
347   v8::TryCatch try_catch(pIsolate);
348
349   CFX_WideString wsScript(script);
350   CFX_ByteString bsScript = wsScript.UTF8Encode();
351
352   v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
353   v8::Local<v8::Script> compiled_script;
354   if (!v8::Script::Compile(
355            context, v8::String::NewFromUtf8(
356                         pIsolate, bsScript.c_str(), v8::NewStringType::kNormal,
357                         bsScript.GetLength()).ToLocalChecked())
358            .ToLocal(&compiled_script)) {
359     v8::String::Utf8Value error(try_catch.Exception());
360     return -1;
361   }
362
363   v8::Local<v8::Value> result;
364   if (!compiled_script->Run(context).ToLocal(&result)) {
365     v8::String::Utf8Value error(try_catch.Exception());
366     return -1;
367   }
368   return 0;
369 }
370
371 v8::Local<v8::Object> JS_NewFxDynamicObj(v8::Isolate* pIsolate,
372                                          IFXJS_Context* pJSContext,
373                                          int nObjDefnID) {
374   v8::Isolate::Scope isolate_scope(pIsolate);
375   v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
376   if (-1 == nObjDefnID) {
377     v8::Local<v8::ObjectTemplate> objTempl = v8::ObjectTemplate::New(pIsolate);
378     v8::Local<v8::Object> obj;
379     if (objTempl->NewInstance(context).ToLocal(&obj))
380       return obj;
381     return v8::Local<v8::Object>();
382   }
383
384   CFX_PtrArray* pArray = (CFX_PtrArray*)pIsolate->GetData(g_embedderDataSlot);
385   if (!pArray)
386     return v8::Local<v8::Object>();
387
388   if (nObjDefnID < 0 || nObjDefnID >= pArray->GetSize())
389     return v8::Local<v8::Object>();
390   CJS_ObjDefintion* pObjDef = (CJS_ObjDefintion*)pArray->GetAt(nObjDefnID);
391
392   v8::Local<v8::ObjectTemplate> objTemp =
393       v8::Local<v8::ObjectTemplate>::New(pIsolate, pObjDef->m_objTemplate);
394   v8::Local<v8::Object> obj;
395   if (!objTemp->NewInstance(context).ToLocal(&obj))
396     return v8::Local<v8::Object>();
397
398   CJS_PrivateData* pPrivateData = new CJS_PrivateData;
399   pPrivateData->ObjDefID = nObjDefnID;
400
401   obj->SetAlignedPointerInInternalField(0, pPrivateData);
402   if (pObjDef->m_pConstructor)
403     pObjDef->m_pConstructor(
404         pJSContext, obj,
405         context->Global()->GetPrototype()->ToObject(context).ToLocalChecked());
406
407   return obj;
408 }
409
410 v8::Local<v8::Object> JS_GetStaticObj(v8::Isolate* pIsolate, int nObjDefnID) {
411   v8::Isolate::Scope isolate_scope(pIsolate);
412   CFX_PtrArray* pArray = (CFX_PtrArray*)pIsolate->GetData(g_embedderDataSlot);
413   if (!pArray)
414     return v8::Local<v8::Object>();
415
416   if (nObjDefnID < 0 || nObjDefnID >= pArray->GetSize())
417     return v8::Local<v8::Object>();
418   CJS_ObjDefintion* pObjDef = (CJS_ObjDefintion*)pArray->GetAt(nObjDefnID);
419   v8::Local<v8::Object> obj =
420       v8::Local<v8::Object>::New(pIsolate, pObjDef->m_StaticObj);
421   return obj;
422 }
423
424 v8::Local<v8::Object> JS_GetThisObj(v8::Isolate* pIsolate) {
425   // Return the global object.
426   v8::Isolate::Scope isolate_scope(pIsolate);
427   CFX_PtrArray* pArray = (CFX_PtrArray*)pIsolate->GetData(g_embedderDataSlot);
428   if (!pArray)
429     return v8::Local<v8::Object>();
430
431   v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
432   return context->Global()->GetPrototype()->ToObject(context).ToLocalChecked();
433 }
434
435 int JS_GetObjDefnID(v8::Local<v8::Object> pObj) {
436   if (pObj.IsEmpty() || !pObj->InternalFieldCount())
437     return -1;
438   CJS_PrivateData* pPrivateData =
439       (CJS_PrivateData*)pObj->GetAlignedPointerFromInternalField(0);
440   if (pPrivateData)
441     return pPrivateData->ObjDefID;
442   return -1;
443 }
444
445 v8::Isolate* JS_GetRuntime(v8::Local<v8::Object> pObj) {
446   if (pObj.IsEmpty())
447     return NULL;
448   v8::Local<v8::Context> context = pObj->CreationContext();
449   if (context.IsEmpty())
450     return NULL;
451   return context->GetIsolate();
452 }
453
454 int JS_GetObjDefnID(v8::Isolate* pIsolate, const wchar_t* pObjName) {
455   v8::Isolate::Scope isolate_scope(pIsolate);
456   CFX_PtrArray* pArray = (CFX_PtrArray*)pIsolate->GetData(g_embedderDataSlot);
457   if (!pArray)
458     return -1;
459
460   for (int i = 0; i < pArray->GetSize(); i++) {
461     CJS_ObjDefintion* pObjDef = (CJS_ObjDefintion*)pArray->GetAt(i);
462     if (FXSYS_wcscmp(pObjDef->objName, pObjName) == 0)
463       return i;
464   }
465   return -1;
466 }
467
468 void JS_Error(v8::Isolate* pIsolate, const CFX_WideString& message) {
469   // Conversion from pdfium's wchar_t wide-strings to v8's uint16_t
470   // wide-strings isn't handled by v8, so use UTF8 as a common
471   // intermediate format.
472   CFX_ByteString utf8_message = message.UTF8Encode();
473   pIsolate->ThrowException(
474       v8::String::NewFromUtf8(pIsolate, utf8_message.c_str(),
475                               v8::NewStringType::kNormal).ToLocalChecked());
476 }
477
478 unsigned JS_CalcHash(const wchar_t* main, unsigned nLen) {
479   return (unsigned)FX_HashCode_String_GetW(main, nLen);
480 }
481
482 unsigned JS_CalcHash(const wchar_t* main) {
483   return (unsigned)FX_HashCode_String_GetW(main, FXSYS_wcslen(main));
484 }
485 const wchar_t* JS_GetTypeof(v8::Local<v8::Value> pObj) {
486   if (pObj.IsEmpty())
487     return NULL;
488   if (pObj->IsString())
489     return kFXJSValueNameString;
490   if (pObj->IsNumber())
491     return kFXJSValueNameNumber;
492   if (pObj->IsBoolean())
493     return kFXJSValueNameBoolean;
494   if (pObj->IsDate())
495     return kFXJSValueNameDate;
496   if (pObj->IsObject())
497     return kFXJSValueNameObject;
498   if (pObj->IsNull())
499     return kFXJSValueNameNull;
500   if (pObj->IsUndefined())
501     return kFXJSValueNameUndefined;
502   return NULL;
503 }
504 void JS_SetPrivate(v8::Local<v8::Object> pObj, void* p) {
505   JS_SetPrivate(NULL, pObj, p);
506 }
507
508 void* JS_GetPrivate(v8::Local<v8::Object> pObj) {
509   return JS_GetPrivate(NULL, pObj);
510 }
511
512 void JS_SetPrivate(v8::Isolate* pIsolate, v8::Local<v8::Object> pObj, void* p) {
513   if (pObj.IsEmpty() || !pObj->InternalFieldCount())
514     return;
515   CJS_PrivateData* pPrivateData =
516       (CJS_PrivateData*)pObj->GetAlignedPointerFromInternalField(0);
517   if (!pPrivateData)
518     return;
519   pPrivateData->pPrivate = p;
520 }
521
522 void* JS_GetPrivate(v8::Isolate* pIsolate, v8::Local<v8::Object> pObj) {
523   if (pObj.IsEmpty())
524     return NULL;
525   CJS_PrivateData* pPrivateData = NULL;
526   if (pObj->InternalFieldCount())
527     pPrivateData =
528         (CJS_PrivateData*)pObj->GetAlignedPointerFromInternalField(0);
529   else {
530     // It could be a global proxy object.
531     v8::Local<v8::Value> v = pObj->GetPrototype();
532     v8::Isolate* isolate = (v8::Isolate*)pIsolate;
533     v8::Local<v8::Context> context = isolate->GetCurrentContext();
534     if (v->IsObject())
535       pPrivateData = (CJS_PrivateData*)v->ToObject(context)
536                          .ToLocalChecked()
537                          ->GetAlignedPointerFromInternalField(0);
538   }
539   if (!pPrivateData)
540     return NULL;
541   return pPrivateData->pPrivate;
542 }
543
544 void JS_FreePrivate(void* pPrivateData) {
545   delete (CJS_PrivateData*)pPrivateData;
546 }
547
548 void JS_FreePrivate(v8::Local<v8::Object> pObj) {
549   if (pObj.IsEmpty() || !pObj->InternalFieldCount())
550     return;
551   JS_FreePrivate(pObj->GetAlignedPointerFromInternalField(0));
552   pObj->SetAlignedPointerInInternalField(0, NULL);
553 }
554
555 v8::Local<v8::Value> JS_GetObjectValue(v8::Local<v8::Object> pObj) {
556   return pObj;
557 }
558
559 v8::Local<v8::String> WSToJSString(v8::Isolate* pIsolate,
560                                    const wchar_t* PropertyName,
561                                    int Len = -1) {
562   CFX_WideString ws = CFX_WideString(PropertyName, Len);
563   CFX_ByteString bs = ws.UTF8Encode();
564   if (!pIsolate)
565     pIsolate = v8::Isolate::GetCurrent();
566   return v8::String::NewFromUtf8(pIsolate, bs.c_str(),
567                                  v8::NewStringType::kNormal).ToLocalChecked();
568 }
569
570 v8::Local<v8::Value> JS_GetObjectElement(v8::Isolate* pIsolate,
571                                          v8::Local<v8::Object> pObj,
572                                          const wchar_t* PropertyName) {
573   if (pObj.IsEmpty())
574     return v8::Local<v8::Value>();
575   v8::Local<v8::Value> val;
576   if (!pObj->Get(pIsolate->GetCurrentContext(),
577                  WSToJSString(pIsolate, PropertyName)).ToLocal(&val))
578     return v8::Local<v8::Value>();
579   return val;
580 }
581
582 v8::Local<v8::Array> JS_GetObjectElementNames(v8::Isolate* pIsolate,
583                                               v8::Local<v8::Object> pObj) {
584   if (pObj.IsEmpty())
585     return v8::Local<v8::Array>();
586   v8::Local<v8::Array> val;
587   if (!pObj->GetPropertyNames(pIsolate->GetCurrentContext()).ToLocal(&val))
588     return v8::Local<v8::Array>();
589   return val;
590 }
591
592 void JS_PutObjectString(v8::Isolate* pIsolate,
593                         v8::Local<v8::Object> pObj,
594                         const wchar_t* PropertyName,
595                         const wchar_t* sValue)  // VT_string
596 {
597   if (pObj.IsEmpty())
598     return;
599   pObj->Set(pIsolate->GetCurrentContext(), WSToJSString(pIsolate, PropertyName),
600             WSToJSString(pIsolate, sValue)).FromJust();
601 }
602
603 void JS_PutObjectNumber(v8::Isolate* pIsolate,
604                         v8::Local<v8::Object> pObj,
605                         const wchar_t* PropertyName,
606                         int nValue) {
607   if (pObj.IsEmpty())
608     return;
609   pObj->Set(pIsolate->GetCurrentContext(), WSToJSString(pIsolate, PropertyName),
610             v8::Int32::New(pIsolate, nValue)).FromJust();
611 }
612
613 void JS_PutObjectNumber(v8::Isolate* pIsolate,
614                         v8::Local<v8::Object> pObj,
615                         const wchar_t* PropertyName,
616                         float fValue) {
617   if (pObj.IsEmpty())
618     return;
619   pObj->Set(pIsolate->GetCurrentContext(), WSToJSString(pIsolate, PropertyName),
620             v8::Number::New(pIsolate, (double)fValue)).FromJust();
621 }
622
623 void JS_PutObjectNumber(v8::Isolate* pIsolate,
624                         v8::Local<v8::Object> pObj,
625                         const wchar_t* PropertyName,
626                         double dValue) {
627   if (pObj.IsEmpty())
628     return;
629   pObj->Set(pIsolate->GetCurrentContext(), WSToJSString(pIsolate, PropertyName),
630             v8::Number::New(pIsolate, (double)dValue)).FromJust();
631 }
632
633 void JS_PutObjectBoolean(v8::Isolate* pIsolate,
634                          v8::Local<v8::Object> pObj,
635                          const wchar_t* PropertyName,
636                          bool bValue) {
637   if (pObj.IsEmpty())
638     return;
639   pObj->Set(pIsolate->GetCurrentContext(), WSToJSString(pIsolate, PropertyName),
640             v8::Boolean::New(pIsolate, bValue)).FromJust();
641 }
642
643 void JS_PutObjectObject(v8::Isolate* pIsolate,
644                         v8::Local<v8::Object> pObj,
645                         const wchar_t* PropertyName,
646                         v8::Local<v8::Object> pPut) {
647   if (pObj.IsEmpty())
648     return;
649   pObj->Set(pIsolate->GetCurrentContext(), WSToJSString(pIsolate, PropertyName),
650             pPut).FromJust();
651 }
652
653 void JS_PutObjectNull(v8::Isolate* pIsolate,
654                       v8::Local<v8::Object> pObj,
655                       const wchar_t* PropertyName) {
656   if (pObj.IsEmpty())
657     return;
658   pObj->Set(pIsolate->GetCurrentContext(), WSToJSString(pIsolate, PropertyName),
659             v8::Local<v8::Object>()).FromJust();
660 }
661
662 v8::Local<v8::Array> JS_NewArray(v8::Isolate* pIsolate) {
663   return v8::Array::New(pIsolate);
664 }
665
666 unsigned JS_PutArrayElement(v8::Isolate* pIsolate,
667                             v8::Local<v8::Array> pArray,
668                             unsigned index,
669                             v8::Local<v8::Value> pValue,
670                             FXJSVALUETYPE eType) {
671   if (pArray.IsEmpty())
672     return 0;
673   if (pArray->Set(pIsolate->GetCurrentContext(), index, pValue).IsNothing())
674     return 0;
675   return 1;
676 }
677
678 v8::Local<v8::Value> JS_GetArrayElement(v8::Isolate* pIsolate,
679                                         v8::Local<v8::Array> pArray,
680                                         unsigned index) {
681   if (pArray.IsEmpty())
682     return v8::Local<v8::Value>();
683   v8::Local<v8::Value> val;
684   if (!pArray->Get(pIsolate->GetCurrentContext(), index).ToLocal(&val))
685     return v8::Local<v8::Value>();
686   return val;
687 }
688
689 unsigned JS_GetArrayLength(v8::Local<v8::Array> pArray) {
690   if (pArray.IsEmpty())
691     return 0;
692   return pArray->Length();
693 }
694
695 v8::Local<v8::Value> JS_NewNumber(v8::Isolate* pIsolate, int number) {
696   return v8::Int32::New(pIsolate, number);
697 }
698
699 v8::Local<v8::Value> JS_NewNumber(v8::Isolate* pIsolate, double number) {
700   return v8::Number::New(pIsolate, number);
701 }
702
703 v8::Local<v8::Value> JS_NewNumber(v8::Isolate* pIsolate, float number) {
704   return v8::Number::New(pIsolate, (float)number);
705 }
706
707 v8::Local<v8::Value> JS_NewBoolean(v8::Isolate* pIsolate, bool b) {
708   return v8::Boolean::New(pIsolate, b);
709 }
710
711 v8::Local<v8::Value> JS_NewObject(v8::Isolate* pIsolate,
712                                   v8::Local<v8::Object> pObj) {
713   if (pObj.IsEmpty())
714     return v8::Local<v8::Value>();
715   return pObj->Clone();
716 }
717
718 v8::Local<v8::Value> JS_NewObject2(v8::Isolate* pIsolate,
719                                    v8::Local<v8::Array> pObj) {
720   if (pObj.IsEmpty())
721     return v8::Local<v8::Value>();
722   return pObj->Clone();
723 }
724
725 v8::Local<v8::Value> JS_NewString(v8::Isolate* pIsolate,
726                                   const wchar_t* string) {
727   return WSToJSString(pIsolate, string);
728 }
729
730 v8::Local<v8::Value> JS_NewString(v8::Isolate* pIsolate,
731                                   const wchar_t* string,
732                                   unsigned nLen) {
733   return WSToJSString(pIsolate, string, nLen);
734 }
735
736 v8::Local<v8::Value> JS_NewNull() {
737   return v8::Local<v8::Value>();
738 }
739
740 v8::Local<v8::Value> JS_NewDate(v8::Isolate* pIsolate, double d) {
741   return v8::Date::New(pIsolate->GetCurrentContext(), d).ToLocalChecked();
742 }
743
744 v8::Local<v8::Value> JS_NewValue(v8::Isolate* pIsolate) {
745   return v8::Local<v8::Value>();
746 }
747
748 v8::Local<v8::Value> JS_GetListValue(v8::Isolate* pIsolate,
749                                      v8::Local<v8::Value> pList,
750                                      int index) {
751   v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
752   if (!pList.IsEmpty() && pList->IsObject()) {
753     v8::Local<v8::Object> obj;
754     if (pList->ToObject(context).ToLocal(&obj)) {
755       v8::Local<v8::Value> val;
756       if (obj->Get(context, index).ToLocal(&val))
757         return val;
758     }
759   }
760   return v8::Local<v8::Value>();
761 }
762
763 int JS_ToInt32(v8::Isolate* pIsolate, v8::Local<v8::Value> pValue) {
764   if (pValue.IsEmpty())
765     return 0;
766   v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
767   return pValue->ToInt32(context).ToLocalChecked()->Value();
768 }
769
770 bool JS_ToBoolean(v8::Isolate* pIsolate, v8::Local<v8::Value> pValue) {
771   if (pValue.IsEmpty())
772     return false;
773   v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
774   return pValue->ToBoolean(context).ToLocalChecked()->Value();
775 }
776
777 double JS_ToNumber(v8::Isolate* pIsolate, v8::Local<v8::Value> pValue) {
778   if (pValue.IsEmpty())
779     return 0.0;
780   v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
781   return pValue->ToNumber(context).ToLocalChecked()->Value();
782 }
783
784 v8::Local<v8::Object> JS_ToObject(v8::Isolate* pIsolate,
785                                   v8::Local<v8::Value> pValue) {
786   if (pValue.IsEmpty())
787     return v8::Local<v8::Object>();
788   v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
789   return pValue->ToObject(context).ToLocalChecked();
790 }
791
792 CFX_WideString JS_ToString(v8::Isolate* pIsolate, v8::Local<v8::Value> pValue) {
793   if (pValue.IsEmpty())
794     return L"";
795   v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
796   v8::String::Utf8Value s(pValue->ToString(context).ToLocalChecked());
797   return CFX_WideString::FromUTF8(*s, s.length());
798 }
799
800 v8::Local<v8::Array> JS_ToArray(v8::Isolate* pIsolate,
801                                 v8::Local<v8::Value> pValue) {
802   if (pValue.IsEmpty())
803     return v8::Local<v8::Array>();
804   v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
805   return v8::Local<v8::Array>::Cast(pValue->ToObject(context).ToLocalChecked());
806 }
807
808 void JS_ValueCopy(v8::Local<v8::Value>& pTo, v8::Local<v8::Value> pFrom) {
809   pTo = pFrom;
810 }
811
812 // JavaScript time implement begin.
813
814 double _getLocalTZA() {
815   if (!FSDK_IsSandBoxPolicyEnabled(FPDF_POLICY_MACHINETIME_ACCESS))
816     return 0;
817   time_t t = 0;
818   time(&t);
819   localtime(&t);
820 #if _MSC_VER >= 1900
821   // In gcc and in Visual Studio prior to VS 2015 'timezone' is a global
822   // variable declared in time.h. That variable was deprecated and in VS 2015
823   // is removed, with _get_timezone replacing it.
824   long timezone = 0;
825   _get_timezone(&timezone);
826 #endif
827   return (double)(-(timezone * 1000));
828 }
829
830 int _getDaylightSavingTA(double d) {
831   if (!FSDK_IsSandBoxPolicyEnabled(FPDF_POLICY_MACHINETIME_ACCESS))
832     return 0;
833   time_t t = (time_t)(d / 1000);
834   struct tm* tmp = localtime(&t);
835   if (tmp == NULL)
836     return 0;
837   if (tmp->tm_isdst > 0)
838     // One hour.
839     return (int)60 * 60 * 1000;
840   return 0;
841 }
842
843 double _Mod(double x, double y) {
844   double r = fmod(x, y);
845   if (r < 0)
846     r += y;
847   return r;
848 }
849
850 int _isfinite(double v) {
851 #if _MSC_VER
852   return ::_finite(v);
853 #else
854   return std::fabs(v) < std::numeric_limits<double>::max();
855 #endif
856 }
857
858 double _toInteger(double n) {
859   return (n >= 0) ? FXSYS_floor(n) : -FXSYS_floor(-n);
860 }
861
862 bool _isLeapYear(int year) {
863   return (year % 4 == 0) && ((year % 100 != 0) || (year % 400 != 0));
864 }
865
866 int _DayFromYear(int y) {
867   return (int)(365 * (y - 1970.0) + FXSYS_floor((y - 1969.0) / 4) -
868                FXSYS_floor((y - 1901.0) / 100) +
869                FXSYS_floor((y - 1601.0) / 400));
870 }
871
872 double _TimeFromYear(int y) {
873   return ((double)86400000) * _DayFromYear(y);
874 }
875
876 double _TimeFromYearMonth(int y, int m) {
877   static int daysMonth[12] = {0,   31,  59,  90,  120, 151,
878                               181, 212, 243, 273, 304, 334};
879   static int leapDaysMonth[12] = {0,   31,  60,  91,  121, 152,
880                                   182, 213, 244, 274, 305, 335};
881   int* pMonth = daysMonth;
882   if (_isLeapYear(y))
883     pMonth = leapDaysMonth;
884   return _TimeFromYear(y) + ((double)pMonth[m]) * 86400000;
885 }
886
887 int _Day(double t) {
888   return (int)FXSYS_floor(t / 86400000);
889 }
890
891 int _YearFromTime(double t) {
892   // estimate the time.
893   int y = 1970 + (int)(t / (365.0 * 86400000));
894   if (_TimeFromYear(y) <= t) {
895     while (_TimeFromYear(y + 1) <= t)
896       y++;
897   } else
898     while (_TimeFromYear(y - 1) > t)
899       y--;
900   return y;
901 }
902
903 int _DayWithinYear(double t) {
904   int year = _YearFromTime(t);
905   int day = _Day(t);
906   return day - _DayFromYear(year);
907 }
908
909 int _MonthFromTime(double t) {
910   int day = _DayWithinYear(t);
911   int year = _YearFromTime(t);
912   if (0 <= day && day < 31)
913     return 0;
914   if (31 <= day && day < 59 + _isLeapYear(year))
915     return 1;
916   if ((59 + _isLeapYear(year)) <= day && day < (90 + _isLeapYear(year)))
917     return 2;
918   if ((90 + _isLeapYear(year)) <= day && day < (120 + _isLeapYear(year)))
919     return 3;
920   if ((120 + _isLeapYear(year)) <= day && day < (151 + _isLeapYear(year)))
921     return 4;
922   if ((151 + _isLeapYear(year)) <= day && day < (181 + _isLeapYear(year)))
923     return 5;
924   if ((181 + _isLeapYear(year)) <= day && day < (212 + _isLeapYear(year)))
925     return 6;
926   if ((212 + _isLeapYear(year)) <= day && day < (243 + _isLeapYear(year)))
927     return 7;
928   if ((243 + _isLeapYear(year)) <= day && day < (273 + _isLeapYear(year)))
929     return 8;
930   if ((273 + _isLeapYear(year)) <= day && day < (304 + _isLeapYear(year)))
931     return 9;
932   if ((304 + _isLeapYear(year)) <= day && day < (334 + _isLeapYear(year)))
933     return 10;
934   if ((334 + _isLeapYear(year)) <= day && day < (365 + _isLeapYear(year)))
935     return 11;
936
937   return -1;
938 }
939
940 int _DateFromTime(double t) {
941   int day = _DayWithinYear(t);
942   int year = _YearFromTime(t);
943   bool leap = _isLeapYear(year);
944   int month = _MonthFromTime(t);
945   switch (month) {
946     case 0:
947       return day + 1;
948     case 1:
949       return day - 30;
950     case 2:
951       return day - 58 - leap;
952     case 3:
953       return day - 89 - leap;
954     case 4:
955       return day - 119 - leap;
956     case 5:
957       return day - 150 - leap;
958     case 6:
959       return day - 180 - leap;
960     case 7:
961       return day - 211 - leap;
962     case 8:
963       return day - 242 - leap;
964     case 9:
965       return day - 272 - leap;
966     case 10:
967       return day - 303 - leap;
968     case 11:
969       return day - 333 - leap;
970     default:
971       return 0;
972   }
973 }
974
975 double JS_GetDateTime() {
976   if (!FSDK_IsSandBoxPolicyEnabled(FPDF_POLICY_MACHINETIME_ACCESS))
977     return 0;
978   time_t t = time(NULL);
979   struct tm* pTm = localtime(&t);
980
981   int year = pTm->tm_year + 1900;
982   double t1 = _TimeFromYear(year);
983
984   return t1 + pTm->tm_yday * 86400000.0 + pTm->tm_hour * 3600000.0 +
985          pTm->tm_min * 60000.0 + pTm->tm_sec * 1000.0;
986 }
987
988 int JS_GetYearFromTime(double dt) {
989   return _YearFromTime(dt);
990 }
991
992 int JS_GetMonthFromTime(double dt) {
993   return _MonthFromTime(dt);
994 }
995
996 int JS_GetDayFromTime(double dt) {
997   return _DateFromTime(dt);
998 }
999
1000 int JS_GetHourFromTime(double dt) {
1001   return (int)_Mod(FXSYS_floor((double)(dt / (60 * 60 * 1000))), 24);
1002 }
1003
1004 int JS_GetMinFromTime(double dt) {
1005   return (int)_Mod(FXSYS_floor((double)(dt / (60 * 1000))), 60);
1006 }
1007
1008 int JS_GetSecFromTime(double dt) {
1009   return (int)_Mod(FXSYS_floor((double)(dt / 1000)), 60);
1010 }
1011
1012 double JS_DateParse(const wchar_t* string) {
1013   v8::Isolate* pIsolate = v8::Isolate::GetCurrent();
1014   v8::Isolate::Scope isolate_scope(pIsolate);
1015   v8::HandleScope scope(pIsolate);
1016
1017   v8::Local<v8::Context> context = pIsolate->GetCurrentContext();
1018
1019   // Use the built-in object method.
1020   v8::Local<v8::Value> v =
1021       context->Global()
1022           ->Get(context, v8::String::NewFromUtf8(pIsolate, "Date",
1023                                                  v8::NewStringType::kNormal)
1024                              .ToLocalChecked())
1025           .ToLocalChecked();
1026   if (v->IsObject()) {
1027     v8::Local<v8::Object> o = v->ToObject(context).ToLocalChecked();
1028     v = o->Get(context, v8::String::NewFromUtf8(pIsolate, "parse",
1029                                                 v8::NewStringType::kNormal)
1030                             .ToLocalChecked())
1031             .ToLocalChecked();
1032     if (v->IsFunction()) {
1033       v8::Local<v8::Function> funC = v8::Local<v8::Function>::Cast(v);
1034
1035       const int argc = 1;
1036       v8::Local<v8::String> timeStr = WSToJSString(pIsolate, string);
1037       v8::Local<v8::Value> argv[argc] = {timeStr};
1038       v = funC->Call(context, context->Global(), argc, argv).ToLocalChecked();
1039       if (v->IsNumber()) {
1040         double date = v->ToNumber(context).ToLocalChecked()->Value();
1041         if (!_isfinite(date))
1042           return date;
1043         return date + _getLocalTZA() + _getDaylightSavingTA(date);
1044       }
1045     }
1046   }
1047   return 0;
1048 }
1049
1050 double JS_MakeDay(int nYear, int nMonth, int nDate) {
1051   if (!_isfinite(nYear) || !_isfinite(nMonth) || !_isfinite(nDate))
1052     return GetNan();
1053   double y = _toInteger(nYear);
1054   double m = _toInteger(nMonth);
1055   double dt = _toInteger(nDate);
1056   double ym = y + FXSYS_floor((double)m / 12);
1057   double mn = _Mod(m, 12);
1058
1059   double t = _TimeFromYearMonth((int)ym, (int)mn);
1060
1061   if (_YearFromTime(t) != ym || _MonthFromTime(t) != mn ||
1062       _DateFromTime(t) != 1)
1063     return GetNan();
1064   return _Day(t) + dt - 1;
1065 }
1066
1067 double JS_MakeTime(int nHour, int nMin, int nSec, int nMs) {
1068   if (!_isfinite(nHour) || !_isfinite(nMin) || !_isfinite(nSec) ||
1069       !_isfinite(nMs))
1070     return GetNan();
1071
1072   double h = _toInteger(nHour);
1073   double m = _toInteger(nMin);
1074   double s = _toInteger(nSec);
1075   double milli = _toInteger(nMs);
1076
1077   return h * 3600000 + m * 60000 + s * 1000 + milli;
1078 }
1079
1080 double JS_MakeDate(double day, double time) {
1081   if (!_isfinite(day) || !_isfinite(time))
1082     return GetNan();
1083
1084   return day * 86400000 + time;
1085 }
1086
1087 bool JS_PortIsNan(double d) {
1088   return d != d;
1089 }
1090
1091 double JS_LocalTime(double d) {
1092   return JS_GetDateTime() + _getDaylightSavingTA(d);
1093 }
1094
1095 // JavaScript time implement End.