Remove JavaScript.h
[pdfium.git] / fpdfsdk / src / javascript / global.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_ext.h"
8 #include "../../include/javascript/IJavaScript.h"
9 #include "../../include/javascript/JS_Context.h"
10 #include "../../include/javascript/JS_Define.h"
11 #include "../../include/javascript/JS_EventHandler.h"
12 #include "../../include/javascript/JS_GlobalData.h"
13 #include "../../include/javascript/JS_Object.h"
14 #include "../../include/javascript/JS_Value.h"
15 #include "../../include/javascript/global.h"
16 #include "../../include/javascript/resource.h"
17
18 /* ---------------------------- global ---------------------------- */
19
20 // Helper class for compile-time calculation of hash values in order to
21 // avoid having global object initializers.
22 template <unsigned ACC, wchar_t... Ns>
23 struct CHash;
24
25 // Only needed to hash single-character strings.
26 template <wchar_t N>
27 struct CHash<N> {
28   static const unsigned value = N;
29 };
30
31 template <unsigned ACC, wchar_t N>
32 struct CHash<ACC, N> {
33   static const unsigned value = (ACC * 1313LLU + N) & 0xFFFFFFFF;
34 };
35
36 template <unsigned ACC, wchar_t N, wchar_t... Ns>
37 struct CHash<ACC, N, Ns...> {
38   static const unsigned value = CHash<CHash<ACC, N>::value, Ns...>::value;
39 };
40
41 const unsigned int JSCONST_nStringHash =
42     CHash<'s', 't', 'r', 'i', 'n', 'g'>::value;
43 const unsigned int JSCONST_nNumberHash =
44     CHash<'n', 'u', 'm', 'b', 'e', 'r'>::value;
45 const unsigned int JSCONST_nBoolHash =
46     CHash<'b', 'o', 'o', 'l', 'e', 'a', 'n'>::value;
47 const unsigned int JSCONST_nDateHash = CHash<'d', 'a', 't', 'e'>::value;
48 const unsigned int JSCONST_nObjectHash =
49     CHash<'o', 'b', 'j', 'e', 'c', 't'>::value;
50 const unsigned int JSCONST_nFXobjHash = CHash<'f', 'x', 'o', 'b', 'j'>::value;
51 const unsigned int JSCONST_nNullHash = CHash<'n', 'u', 'l', 'l'>::value;
52 const unsigned int JSCONST_nUndefHash =
53     CHash<'u', 'n', 'd', 'e', 'f', 'i', 'n', 'e', 'd'>::value;
54
55 static unsigned JS_CalcHash(const wchar_t* main) {
56   return (unsigned)FX_HashCode_String_GetW(main, FXSYS_wcslen(main));
57 }
58
59 #ifdef _DEBUG
60 class HashVerify {
61  public:
62   HashVerify();
63 } g_hashVerify;
64
65 HashVerify::HashVerify() {
66   ASSERT(JSCONST_nStringHash == JS_CalcHash(kFXJSValueNameString));
67   ASSERT(JSCONST_nNumberHash == JS_CalcHash(kFXJSValueNameNumber));
68   ASSERT(JSCONST_nBoolHash == JS_CalcHash(kFXJSValueNameBoolean));
69   ASSERT(JSCONST_nDateHash == JS_CalcHash(kFXJSValueNameDate));
70   ASSERT(JSCONST_nObjectHash == JS_CalcHash(kFXJSValueNameObject));
71   ASSERT(JSCONST_nFXobjHash == JS_CalcHash(kFXJSValueNameFxobj));
72   ASSERT(JSCONST_nNullHash == JS_CalcHash(kFXJSValueNameNull));
73   ASSERT(JSCONST_nUndefHash == JS_CalcHash(kFXJSValueNameUndefined));
74 }
75 #endif
76
77 BEGIN_JS_STATIC_CONST(CJS_Global)
78 END_JS_STATIC_CONST()
79
80 BEGIN_JS_STATIC_PROP(CJS_Global)
81 END_JS_STATIC_PROP()
82
83 BEGIN_JS_STATIC_METHOD(CJS_Global)
84 JS_STATIC_METHOD_ENTRY(setPersistent)
85 END_JS_STATIC_METHOD()
86
87 IMPLEMENT_SPECIAL_JS_CLASS(CJS_Global, JSGlobalAlternate, global);
88
89 FX_BOOL CJS_Global::InitInstance(IFXJS_Context* cc) {
90   CJS_Context* pContext = (CJS_Context*)cc;
91   ASSERT(pContext != NULL);
92
93   JSGlobalAlternate* pGlobal = (JSGlobalAlternate*)GetEmbedObject();
94   ASSERT(pGlobal != NULL);
95
96   pGlobal->Initial(pContext->GetReaderApp());
97
98   return TRUE;
99 };
100
101 JSGlobalAlternate::JSGlobalAlternate(CJS_Object* pJSObject)
102     : CJS_EmbedObj(pJSObject), m_pApp(NULL) {
103 }
104
105 JSGlobalAlternate::~JSGlobalAlternate() {
106   DestroyGlobalPersisitentVariables();
107   m_pGlobalData->Release();
108 }
109
110 void JSGlobalAlternate::Initial(CPDFDoc_Environment* pApp) {
111   m_pApp = pApp;
112   m_pGlobalData = CJS_GlobalData::GetRetainedInstance(pApp);
113   UpdateGlobalPersistentVariables();
114 }
115
116 FX_BOOL JSGlobalAlternate::QueryProperty(const FX_WCHAR* propname) {
117   return CFX_WideString(propname) != L"setPersistent";
118 }
119
120 FX_BOOL JSGlobalAlternate::DelProperty(IFXJS_Context* cc,
121                                        const FX_WCHAR* propname,
122                                        CFX_WideString& sError) {
123   auto it = m_mapGlobal.find(CFX_ByteString::FromUnicode(propname));
124   if (it == m_mapGlobal.end())
125     return FALSE;
126
127   it->second->bDeleted = TRUE;
128   return TRUE;
129 }
130
131 FX_BOOL JSGlobalAlternate::DoProperty(IFXJS_Context* cc,
132                                       const FX_WCHAR* propname,
133                                       CJS_PropValue& vp,
134                                       CFX_WideString& sError) {
135   if (vp.IsSetting()) {
136     CFX_ByteString sPropName = CFX_ByteString::FromUnicode(propname);
137     switch (vp.GetType()) {
138       case CJS_Value::VT_number: {
139         double dData;
140         vp >> dData;
141         return SetGlobalVariables(sPropName, JS_GLOBALDATA_TYPE_NUMBER, dData,
142                                   false, "", v8::Local<v8::Object>(), FALSE);
143       }
144       case CJS_Value::VT_boolean: {
145         bool bData;
146         vp >> bData;
147         return SetGlobalVariables(sPropName, JS_GLOBALDATA_TYPE_BOOLEAN, 0,
148                                   bData, "", v8::Local<v8::Object>(), FALSE);
149       }
150       case CJS_Value::VT_string: {
151         CFX_ByteString sData;
152         vp >> sData;
153         return SetGlobalVariables(sPropName, JS_GLOBALDATA_TYPE_STRING, 0,
154                                   false, sData, v8::Local<v8::Object>(), FALSE);
155       }
156       case CJS_Value::VT_object: {
157         v8::Local<v8::Object> pData;
158         vp >> pData;
159         return SetGlobalVariables(sPropName, JS_GLOBALDATA_TYPE_OBJECT, 0,
160                                   false, "", pData, FALSE);
161       }
162       case CJS_Value::VT_null: {
163         return SetGlobalVariables(sPropName, JS_GLOBALDATA_TYPE_NULL, 0, false,
164                                   "", v8::Local<v8::Object>(), FALSE);
165       }
166       case CJS_Value::VT_undefined: {
167         DelProperty(cc, propname, sError);
168         return TRUE;
169       }
170       default:
171         break;
172     }
173   } else {
174     auto it = m_mapGlobal.find(CFX_ByteString::FromUnicode(propname));
175     if (it == m_mapGlobal.end()) {
176       vp.SetNull();
177       return TRUE;
178     }
179     JSGlobalData* pData = it->second;
180     if (pData->bDeleted) {
181       vp.SetNull();
182       return TRUE;
183     }
184     switch (pData->nType) {
185       case JS_GLOBALDATA_TYPE_NUMBER:
186         vp << pData->dData;
187         return TRUE;
188       case JS_GLOBALDATA_TYPE_BOOLEAN:
189         vp << pData->bData;
190         return TRUE;
191       case JS_GLOBALDATA_TYPE_STRING:
192         vp << pData->sData;
193         return TRUE;
194       case JS_GLOBALDATA_TYPE_OBJECT: {
195         v8::Local<v8::Object> obj =
196             v8::Local<v8::Object>::New(vp.GetIsolate(), pData->pData);
197         vp << obj;
198         return TRUE;
199       }
200       case JS_GLOBALDATA_TYPE_NULL:
201         vp.SetNull();
202         return TRUE;
203       default:
204         break;
205     }
206   }
207   return FALSE;
208 }
209
210 FX_BOOL JSGlobalAlternate::setPersistent(IFXJS_Context* cc,
211                                          const CJS_Parameters& params,
212                                          CJS_Value& vRet,
213                                          CFX_WideString& sError) {
214   CJS_Context* pContext = static_cast<CJS_Context*>(cc);
215   if (params.size() != 2) {
216     sError = JSGetStringFromID(pContext, IDS_STRING_JSPARAMERROR);
217     return FALSE;
218   }
219
220   auto it = m_mapGlobal.find(params[0].ToCFXByteString());
221   if (it != m_mapGlobal.end()) {
222     JSGlobalData* pData = it->second;
223     if (!pData->bDeleted) {
224       pData->bPersistent = params[1].ToBool();
225       return TRUE;
226     }
227   }
228
229   sError = JSGetStringFromID(pContext, IDS_STRING_JSNOGLOBAL);
230   return FALSE;
231 }
232
233 void JSGlobalAlternate::UpdateGlobalPersistentVariables() {
234   ASSERT(m_pGlobalData != NULL);
235
236   for (int i = 0, sz = m_pGlobalData->GetSize(); i < sz; i++) {
237     CJS_GlobalData_Element* pData = m_pGlobalData->GetAt(i);
238     ASSERT(pData != NULL);
239
240     switch (pData->data.nType) {
241       case JS_GLOBALDATA_TYPE_NUMBER:
242         SetGlobalVariables(pData->data.sKey, JS_GLOBALDATA_TYPE_NUMBER,
243                            pData->data.dData, false, "",
244                            v8::Local<v8::Object>(), pData->bPersistent == 1);
245         FXJS_PutObjectNumber(NULL, m_pJSObject->ToV8Object(),
246                              pData->data.sKey.UTF8Decode().c_str(),
247                              pData->data.dData);
248         break;
249       case JS_GLOBALDATA_TYPE_BOOLEAN:
250         SetGlobalVariables(pData->data.sKey, JS_GLOBALDATA_TYPE_BOOLEAN, 0,
251                            (bool)(pData->data.bData == 1), "",
252                            v8::Local<v8::Object>(), pData->bPersistent == 1);
253         FXJS_PutObjectBoolean(NULL, m_pJSObject->ToV8Object(),
254                               pData->data.sKey.UTF8Decode().c_str(),
255                               (bool)(pData->data.bData == 1));
256         break;
257       case JS_GLOBALDATA_TYPE_STRING:
258         SetGlobalVariables(pData->data.sKey, JS_GLOBALDATA_TYPE_STRING, 0,
259                            false, pData->data.sData, v8::Local<v8::Object>(),
260                            pData->bPersistent == 1);
261         FXJS_PutObjectString(NULL, m_pJSObject->ToV8Object(),
262                              pData->data.sKey.UTF8Decode().c_str(),
263                              pData->data.sData.UTF8Decode().c_str());
264         break;
265       case JS_GLOBALDATA_TYPE_OBJECT: {
266         v8::Isolate* pRuntime = FXJS_GetRuntime(m_pJSObject->ToV8Object());
267         v8::Local<v8::Object> pObj = FXJS_NewFxDynamicObj(pRuntime, NULL, -1);
268
269         PutObjectProperty(pObj, &pData->data);
270
271         SetGlobalVariables(pData->data.sKey, JS_GLOBALDATA_TYPE_OBJECT, 0,
272                            false, "", pObj, pData->bPersistent == 1);
273         FXJS_PutObjectObject(NULL, m_pJSObject->ToV8Object(),
274                              pData->data.sKey.UTF8Decode().c_str(), pObj);
275
276       } break;
277       case JS_GLOBALDATA_TYPE_NULL:
278         SetGlobalVariables(pData->data.sKey, JS_GLOBALDATA_TYPE_NULL, 0, false,
279                            "", v8::Local<v8::Object>(),
280                            pData->bPersistent == 1);
281         FXJS_PutObjectNull(NULL, m_pJSObject->ToV8Object(),
282                            pData->data.sKey.UTF8Decode().c_str());
283         break;
284     }
285   }
286 }
287
288 void JSGlobalAlternate::CommitGlobalPersisitentVariables() {
289   ASSERT(m_pGlobalData);
290   for (auto it = m_mapGlobal.begin(); it != m_mapGlobal.end(); ++it) {
291     CFX_ByteString name = it->first;
292     JSGlobalData* pData = it->second;
293     if (pData->bDeleted) {
294       m_pGlobalData->DeleteGlobalVariable(name);
295     } else {
296       switch (pData->nType) {
297         case JS_GLOBALDATA_TYPE_NUMBER:
298           m_pGlobalData->SetGlobalVariableNumber(name, pData->dData);
299           m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
300           break;
301         case JS_GLOBALDATA_TYPE_BOOLEAN:
302           m_pGlobalData->SetGlobalVariableBoolean(name, pData->bData);
303           m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
304           break;
305         case JS_GLOBALDATA_TYPE_STRING:
306           m_pGlobalData->SetGlobalVariableString(name, pData->sData);
307           m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
308           break;
309         case JS_GLOBALDATA_TYPE_OBJECT:
310           // if (pData->pData)
311           {
312             CJS_GlobalVariableArray array;
313             v8::Local<v8::Object> obj = v8::Local<v8::Object>::New(
314                 GetJSObject()->GetIsolate(), pData->pData);
315             ObjectToArray(obj, array);
316             m_pGlobalData->SetGlobalVariableObject(name, array);
317             m_pGlobalData->SetGlobalVariablePersistent(name,
318                                                        pData->bPersistent);
319           }
320           break;
321         case JS_GLOBALDATA_TYPE_NULL:
322           m_pGlobalData->SetGlobalVariableNull(name);
323           m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
324           break;
325       }
326     }
327   }
328 }
329
330 void JSGlobalAlternate::ObjectToArray(v8::Local<v8::Object> pObj,
331                                       CJS_GlobalVariableArray& array) {
332   v8::Local<v8::Context> context = pObj->CreationContext();
333   v8::Isolate* isolate = context->GetIsolate();
334   v8::Local<v8::Array> pKeyList = FXJS_GetObjectElementNames(isolate, pObj);
335   int nObjElements = pKeyList->Length();
336
337   for (int i = 0; i < nObjElements; i++) {
338     CFX_WideString ws =
339         FXJS_ToString(isolate, FXJS_GetArrayElement(isolate, pKeyList, i));
340     CFX_ByteString sKey = ws.UTF8Encode();
341
342     v8::Local<v8::Value> v = FXJS_GetObjectElement(isolate, pObj, ws.c_str());
343     switch (GET_VALUE_TYPE(v)) {
344       case CJS_Value::VT_number: {
345         CJS_KeyValue* pObjElement = new CJS_KeyValue;
346         pObjElement->nType = JS_GLOBALDATA_TYPE_NUMBER;
347         pObjElement->sKey = sKey;
348         pObjElement->dData = FXJS_ToNumber(isolate, v);
349         array.Add(pObjElement);
350       } break;
351       case CJS_Value::VT_boolean: {
352         CJS_KeyValue* pObjElement = new CJS_KeyValue;
353         pObjElement->nType = JS_GLOBALDATA_TYPE_BOOLEAN;
354         pObjElement->sKey = sKey;
355         pObjElement->dData = FXJS_ToBoolean(isolate, v);
356         array.Add(pObjElement);
357       } break;
358       case CJS_Value::VT_string: {
359         CFX_ByteString sValue =
360             CJS_Value(isolate, v, CJS_Value::VT_string).ToCFXByteString();
361         CJS_KeyValue* pObjElement = new CJS_KeyValue;
362         pObjElement->nType = JS_GLOBALDATA_TYPE_STRING;
363         pObjElement->sKey = sKey;
364         pObjElement->sData = sValue;
365         array.Add(pObjElement);
366       } break;
367       case CJS_Value::VT_object: {
368         CJS_KeyValue* pObjElement = new CJS_KeyValue;
369         pObjElement->nType = JS_GLOBALDATA_TYPE_OBJECT;
370         pObjElement->sKey = sKey;
371         ObjectToArray(FXJS_ToObject(isolate, v), pObjElement->objData);
372         array.Add(pObjElement);
373       } break;
374       case CJS_Value::VT_null: {
375         CJS_KeyValue* pObjElement = new CJS_KeyValue;
376         pObjElement->nType = JS_GLOBALDATA_TYPE_NULL;
377         pObjElement->sKey = sKey;
378         array.Add(pObjElement);
379       } break;
380       default:
381         break;
382     }
383   }
384 }
385
386 void JSGlobalAlternate::PutObjectProperty(v8::Local<v8::Object> pObj,
387                                           CJS_KeyValue* pData) {
388   ASSERT(pData != NULL);
389
390   for (int i = 0, sz = pData->objData.Count(); i < sz; i++) {
391     CJS_KeyValue* pObjData = pData->objData.GetAt(i);
392     ASSERT(pObjData != NULL);
393
394     switch (pObjData->nType) {
395       case JS_GLOBALDATA_TYPE_NUMBER:
396         FXJS_PutObjectNumber(NULL, pObj, pObjData->sKey.UTF8Decode().c_str(),
397                              pObjData->dData);
398         break;
399       case JS_GLOBALDATA_TYPE_BOOLEAN:
400         FXJS_PutObjectBoolean(NULL, pObj, pObjData->sKey.UTF8Decode().c_str(),
401                               pObjData->bData == 1);
402         break;
403       case JS_GLOBALDATA_TYPE_STRING:
404         FXJS_PutObjectString(NULL, pObj, pObjData->sKey.UTF8Decode().c_str(),
405                              pObjData->sData.UTF8Decode().c_str());
406         break;
407       case JS_GLOBALDATA_TYPE_OBJECT: {
408         v8::Isolate* pRuntime = FXJS_GetRuntime(m_pJSObject->ToV8Object());
409         v8::Local<v8::Object> pNewObj =
410             FXJS_NewFxDynamicObj(pRuntime, NULL, -1);
411         PutObjectProperty(pNewObj, pObjData);
412         FXJS_PutObjectObject(NULL, pObj, pObjData->sKey.UTF8Decode().c_str(),
413                              pNewObj);
414       } break;
415       case JS_GLOBALDATA_TYPE_NULL:
416         FXJS_PutObjectNull(NULL, pObj, pObjData->sKey.UTF8Decode().c_str());
417         break;
418     }
419   }
420 }
421
422 void JSGlobalAlternate::DestroyGlobalPersisitentVariables() {
423   for (const auto& pair : m_mapGlobal) {
424     delete pair.second;
425   }
426   m_mapGlobal.clear();
427 }
428
429 FX_BOOL JSGlobalAlternate::SetGlobalVariables(const FX_CHAR* propname,
430                                               int nType,
431                                               double dData,
432                                               bool bData,
433                                               const CFX_ByteString& sData,
434                                               v8::Local<v8::Object> pData,
435                                               bool bDefaultPersistent) {
436   if (!propname)
437     return FALSE;
438
439   auto it = m_mapGlobal.find(propname);
440   if (it != m_mapGlobal.end()) {
441     JSGlobalData* pTemp = it->second;
442     if (pTemp->bDeleted || pTemp->nType != nType) {
443       pTemp->dData = 0;
444       pTemp->bData = 0;
445       pTemp->sData = "";
446       pTemp->nType = nType;
447     }
448
449     pTemp->bDeleted = FALSE;
450     switch (nType) {
451       case JS_GLOBALDATA_TYPE_NUMBER: {
452         pTemp->dData = dData;
453       } break;
454       case JS_GLOBALDATA_TYPE_BOOLEAN: {
455         pTemp->bData = bData;
456       } break;
457       case JS_GLOBALDATA_TYPE_STRING: {
458         pTemp->sData = sData;
459       } break;
460       case JS_GLOBALDATA_TYPE_OBJECT: {
461         pTemp->pData.Reset(FXJS_GetRuntime(pData), pData);
462       } break;
463       case JS_GLOBALDATA_TYPE_NULL:
464         break;
465       default:
466         return FALSE;
467     }
468     return TRUE;
469   }
470
471   JSGlobalData* pNewData = NULL;
472
473   switch (nType) {
474     case JS_GLOBALDATA_TYPE_NUMBER: {
475       pNewData = new JSGlobalData;
476       pNewData->nType = JS_GLOBALDATA_TYPE_NUMBER;
477       pNewData->dData = dData;
478       pNewData->bPersistent = bDefaultPersistent;
479     } break;
480     case JS_GLOBALDATA_TYPE_BOOLEAN: {
481       pNewData = new JSGlobalData;
482       pNewData->nType = JS_GLOBALDATA_TYPE_BOOLEAN;
483       pNewData->bData = bData;
484       pNewData->bPersistent = bDefaultPersistent;
485     } break;
486     case JS_GLOBALDATA_TYPE_STRING: {
487       pNewData = new JSGlobalData;
488       pNewData->nType = JS_GLOBALDATA_TYPE_STRING;
489       pNewData->sData = sData;
490       pNewData->bPersistent = bDefaultPersistent;
491     } break;
492     case JS_GLOBALDATA_TYPE_OBJECT: {
493       pNewData = new JSGlobalData;
494       pNewData->nType = JS_GLOBALDATA_TYPE_OBJECT;
495       pNewData->pData.Reset(FXJS_GetRuntime(pData), pData);
496       pNewData->bPersistent = bDefaultPersistent;
497     } break;
498     case JS_GLOBALDATA_TYPE_NULL: {
499       pNewData = new JSGlobalData;
500       pNewData->nType = JS_GLOBALDATA_TYPE_NULL;
501       pNewData->bPersistent = bDefaultPersistent;
502     } break;
503     default:
504       return FALSE;
505   }
506
507   m_mapGlobal[propname] = pNewData;
508   return TRUE;
509 }
510
511 CJS_Value::Type GET_VALUE_TYPE(v8::Local<v8::Value> p) {
512   const unsigned int nHash = JS_CalcHash(FXJS_GetTypeof(p));
513
514   if (nHash == JSCONST_nUndefHash)
515     return CJS_Value::VT_undefined;
516   if (nHash == JSCONST_nNullHash)
517     return CJS_Value::VT_null;
518   if (nHash == JSCONST_nStringHash)
519     return CJS_Value::VT_string;
520   if (nHash == JSCONST_nNumberHash)
521     return CJS_Value::VT_number;
522   if (nHash == JSCONST_nBoolHash)
523     return CJS_Value::VT_boolean;
524   if (nHash == JSCONST_nDateHash)
525     return CJS_Value::VT_date;
526   if (nHash == JSCONST_nObjectHash)
527     return CJS_Value::VT_object;
528   if (nHash == JSCONST_nFXobjHash)
529     return CJS_Value::VT_fxobject;
530
531   return CJS_Value::VT_unknown;
532 }