Avoid duplicate definitions of JSCONST_n*Hash and QeTable variables.
[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 "../../include/javascript/JavaScript.h"
8 #include "../../include/javascript/IJavaScript.h"
9 #include "../../include/javascript/JS_Define.h"
10 #include "../../include/javascript/JS_Object.h"
11 #include "../../include/javascript/JS_Value.h"
12 #include "../../include/javascript/JS_GlobalData.h"
13 #include "../../include/javascript/global.h"
14 #include "../../include/javascript/JS_EventHandler.h"
15 #include "../../include/javascript/JS_Context.h"
16
17 /* ---------------------------- global ---------------------------- */
18
19 extern const unsigned int JSCONST_nStringHash = JS_CalcHash(VALUE_NAME_STRING,wcslen(VALUE_NAME_STRING));
20 extern const unsigned int JSCONST_nNumberHash = JS_CalcHash(VALUE_NAME_NUMBER,wcslen(VALUE_NAME_NUMBER));
21 extern const unsigned int JSCONST_nBoolHash = JS_CalcHash(VALUE_NAME_BOOLEAN,wcslen(VALUE_NAME_BOOLEAN));
22 extern const unsigned int JSCONST_nDateHash = JS_CalcHash(VALUE_NAME_DATE,wcslen(VALUE_NAME_DATE));
23 extern const unsigned int JSCONST_nObjectHash = JS_CalcHash(VALUE_NAME_OBJECT,wcslen(VALUE_NAME_OBJECT));
24 extern const unsigned int JSCONST_nFXobjHash = JS_CalcHash(VALUE_NAME_FXOBJ,wcslen(VALUE_NAME_FXOBJ));
25 extern const unsigned int JSCONST_nNullHash = JS_CalcHash(VALUE_NAME_NULL,wcslen(VALUE_NAME_NULL));
26 extern const unsigned int JSCONST_nUndefHash = JS_CalcHash(VALUE_NAME_UNDEFINED,wcslen(VALUE_NAME_UNDEFINED));
27
28 BEGIN_JS_STATIC_CONST(CJS_Global)
29 END_JS_STATIC_CONST()
30
31 BEGIN_JS_STATIC_PROP(CJS_Global)
32 END_JS_STATIC_PROP()
33
34 BEGIN_JS_STATIC_METHOD(CJS_Global)
35         JS_STATIC_METHOD_ENTRY(setPersistent, 2)
36 END_JS_STATIC_METHOD()
37
38 IMPLEMENT_SPECIAL_JS_CLASS(CJS_Global, global_alternate, global);
39
40 FX_BOOL CJS_Global::InitInstance(IFXJS_Context* cc)
41 {
42         CJS_Context* pContext = (CJS_Context*)cc;
43         ASSERT(pContext != NULL);
44
45         global_alternate* pGlobal = (global_alternate*)GetEmbedObject();
46         ASSERT(pGlobal != NULL);
47         
48         pGlobal->Initial(pContext->GetReaderApp());
49         
50         return TRUE;
51 };
52
53 global_alternate::global_alternate(CJS_Object* pJSObject)
54         : CJS_EmbedObj(pJSObject),
55         m_pApp(NULL)
56 {
57 }
58
59 global_alternate::~global_alternate(void)
60 {
61         ASSERT(m_pApp != NULL);
62
63 //      CommitGlobalPersisitentVariables();
64         DestroyGlobalPersisitentVariables();
65
66         CJS_RuntimeFactory* pFactory = m_pApp->m_pJSRuntimeFactory;
67         ASSERT(pFactory);
68
69         pFactory->ReleaseGlobalData();
70 }
71   
72 void global_alternate::Initial(CPDFDoc_Environment* pApp)
73 {
74         m_pApp = pApp;
75
76         CJS_RuntimeFactory* pFactory = pApp->m_pJSRuntimeFactory;
77         ASSERT(pFactory);
78         m_pGlobalData = pFactory->NewGlobalData(pApp);
79         UpdateGlobalPersistentVariables();
80 }
81
82 FX_BOOL global_alternate::QueryProperty(FX_LPCWSTR propname)
83 {
84         return CFX_WideString(propname) != L"setPersistent";
85 }
86
87 FX_BOOL global_alternate::DelProperty(IFXJS_Context* cc, FX_LPCWSTR propname, JS_ErrorString& sError)
88 {
89         js_global_data* pData = NULL;
90         CFX_ByteString sPropName = CFX_ByteString::FromUnicode(propname);
91
92         if (m_mapGlobal.Lookup(sPropName, (FX_LPVOID&)pData))
93         {
94                 pData->bDeleted = TRUE;
95                 return TRUE;
96         }
97
98         return FALSE;
99 }
100
101 FX_BOOL global_alternate::DoProperty(IFXJS_Context* cc, FX_LPCWSTR propname, CJS_PropValue& vp, JS_ErrorString& sError)
102 {
103         if (vp.IsSetting())
104         {
105                 CFX_ByteString sPropName = CFX_ByteString::FromUnicode(propname);
106                 switch (vp.GetType())
107                 {
108                 case VT_number:
109                         {
110                                 double dData;
111                                 vp >> dData;
112                                 return SetGlobalVariables(sPropName, JS_GLOBALDATA_TYPE_NUMBER, dData, false, "", v8::Handle<v8::Object>(), FALSE);
113                         }
114                 case VT_boolean:
115                         {
116                                 bool bData;
117                                 vp >> bData;
118                                 return SetGlobalVariables(sPropName, JS_GLOBALDATA_TYPE_BOOLEAN, 0, (bool)vp, "", v8::Handle<v8::Object>(), FALSE);
119                         }
120                 case VT_string:
121                         {
122                                 CFX_ByteString sData;
123                                 vp >> sData;
124                                 return SetGlobalVariables(sPropName, JS_GLOBALDATA_TYPE_STRING, 0, false, sData, v8::Handle<v8::Object>(), FALSE);
125                         }
126                 case VT_object:
127                         {
128                                 JSObject pData = (JSObject)vp;
129                                 return SetGlobalVariables(sPropName, JS_GLOBALDATA_TYPE_OBJECT, 0, false, "", pData, FALSE);
130 //                              else
131 //                              {
132 //                                      if (vp.IsArrayObject())
133 //                                      {
134 //                                              CJS_Array array;
135 //                                              vp.ConvertToArray(array);
136 //                                              return SetGlobalVariables(sPropName, JS_GLOBALDATA_TYPE_OBJECT, 0, false, "", 
137 //                                                      (Dobject*)(Darray*)array, FALSE);
138 //                                      }
139 //                                      else
140 //                                              return FALSE;
141 //                              }
142                         }
143                 case VT_null:
144                         {
145                                 return SetGlobalVariables(sPropName, JS_GLOBALDATA_TYPE_NULL, 0, false, "", v8::Handle<v8::Object>(), FALSE);
146                         }
147                 case VT_undefined:
148                         {
149                                 DelProperty(cc, propname, sError);
150                                 return TRUE;
151                         }
152                 default:
153                         return FALSE;
154                 }
155         }
156         else
157         {
158                 js_global_data* pData = NULL;
159                 CFX_ByteString sPropName = CFX_ByteString::FromUnicode(propname);
160
161                 if (m_mapGlobal.Lookup(sPropName, (FX_LPVOID&)pData))
162                 {
163                         if (pData)
164                         {
165                                 if (!pData->bDeleted)
166                                 {
167                                         switch (pData->nType)
168                                         {
169                                         case JS_GLOBALDATA_TYPE_NUMBER:
170                                                 vp << pData->dData;
171                                                 break;
172                                         case JS_GLOBALDATA_TYPE_BOOLEAN:
173                                                 vp << pData->bData;
174                                                 break;
175                                         case JS_GLOBALDATA_TYPE_STRING:
176                                                 vp << pData->sData;
177                                                 break;
178                                         case JS_GLOBALDATA_TYPE_OBJECT:
179                                                 {
180                                                         v8::Handle<v8::Object> obj = v8::Local<v8::Object>::New(vp.GetIsolate(),pData->pData);
181                                                         vp << obj;
182                                                         break;
183                                                 }
184                                         case JS_GLOBALDATA_TYPE_NULL:
185                                                 vp.SetNull();
186                                                 break;
187                                         default:
188                                                 return FALSE;
189                                         }
190                                         return TRUE;
191                                 }
192                                 else
193                                 {
194                                         return TRUE;
195                                 }
196                         }
197                         else
198                         {
199                                 vp.SetNull();
200                                 return TRUE;
201                         }
202                 }
203                 else
204                 {
205                         vp.SetNull();
206                         return TRUE;
207                 }
208         }
209
210         return FALSE;
211 }
212
213 FX_BOOL global_alternate::setPersistent(OBJ_METHOD_PARAMS)
214 {
215         if (params.size() != 2)
216         {
217                 //sError = JSGetStringFromID(IDS_STRING_JSPARAMERROR);  
218                 return FALSE;
219         }
220
221         CFX_ByteString sName = params[0];
222
223         js_global_data* pData = NULL;
224         if (m_mapGlobal.Lookup(sName, (FX_LPVOID&)pData))
225         {
226                 if (pData && !pData->bDeleted)
227                 {
228                         pData->bPersistent = (bool)params[1];
229                         return TRUE;
230                 }
231         }
232
233         //sError = JSGetStringFromID(IDS_JSPARAM_INCORRECT);    
234         return FALSE;
235 }
236
237 void global_alternate::UpdateGlobalPersistentVariables()
238 {
239         ASSERT(m_pGlobalData != NULL);
240
241         for (int i=0,sz=m_pGlobalData->GetSize(); i<sz; i++)
242         {
243                 CJS_GlobalData_Element* pData = m_pGlobalData->GetAt(i);
244                 ASSERT(pData != NULL);
245
246                 switch (pData->data.nType)
247                 {
248                 case JS_GLOBALDATA_TYPE_NUMBER:
249                         this->SetGlobalVariables(pData->data.sKey, JS_GLOBALDATA_TYPE_NUMBER, pData->data.dData, false, "", v8::Handle<v8::Object>(), pData->bPersistent == 1);
250                         JS_PutObjectNumber(NULL,(JSFXObject)(*m_pJSObject),
251                                 pData->data.sKey.UTF8Decode(), pData->data.dData);
252                         break;
253                 case JS_GLOBALDATA_TYPE_BOOLEAN:
254                         this->SetGlobalVariables(pData->data.sKey, JS_GLOBALDATA_TYPE_BOOLEAN, 0, (bool)(pData->data.bData == 1), "", v8::Handle<v8::Object>(), pData->bPersistent == 1);
255                         JS_PutObjectBoolean(NULL,(JSFXObject)(*m_pJSObject),
256                                 pData->data.sKey.UTF8Decode(), (bool)(pData->data.bData == 1));
257                         break;
258                 case JS_GLOBALDATA_TYPE_STRING:
259                         this->SetGlobalVariables(pData->data.sKey, JS_GLOBALDATA_TYPE_STRING, 0, false, pData->data.sData, v8::Handle<v8::Object>(), pData->bPersistent == 1);
260                         JS_PutObjectString(NULL,(JSFXObject)(*m_pJSObject),
261                                 pData->data.sKey.UTF8Decode(), 
262                                 pData->data.sData.UTF8Decode());
263                         break;
264                 case JS_GLOBALDATA_TYPE_OBJECT:
265                         {
266                                 IJS_Runtime* pRuntime = JS_GetRuntime((JSFXObject)(*m_pJSObject));
267                                 v8::Handle<v8::Object> pObj = JS_NewFxDynamicObj(pRuntime, NULL, -1);
268
269                                 PutObjectProperty(pObj, &pData->data);
270
271                                 this->SetGlobalVariables(pData->data.sKey, JS_GLOBALDATA_TYPE_OBJECT, 0, false, "", 
272                                         (JSObject)pObj, pData->bPersistent == 1);
273                                 JS_PutObjectObject(NULL,(JSFXObject)(*m_pJSObject),
274                                         pData->data.sKey.UTF8Decode(), (JSObject)pObj);
275                         }
276                         break;
277                 case JS_GLOBALDATA_TYPE_NULL:
278                         this->SetGlobalVariables(pData->data.sKey, JS_GLOBALDATA_TYPE_NULL, 0, false, "", v8::Handle<v8::Object>(), pData->bPersistent == 1);
279                         JS_PutObjectNull(NULL,(JSFXObject)(*m_pJSObject),
280                                 pData->data.sKey.UTF8Decode());
281                         break;
282                 }
283         }
284 }
285
286 void global_alternate::CommitGlobalPersisitentVariables()
287 {
288         ASSERT(m_pGlobalData != NULL);
289
290         FX_POSITION      pos = m_mapGlobal.GetStartPosition();
291         while (pos)
292         {
293                 CFX_ByteString name; 
294                 js_global_data* pData = NULL;
295                 m_mapGlobal.GetNextAssoc(pos, name, (FX_LPVOID&)pData);
296                 
297                 if (pData)
298                 {
299                         if (pData->bDeleted)
300                         {
301                                 m_pGlobalData->DeleteGlobalVariable(name);
302                         }
303                         else
304                         {
305                                 switch (pData->nType)
306                                 {
307                                 case JS_GLOBALDATA_TYPE_NUMBER:
308                                         m_pGlobalData->SetGlobalVariableNumber(name, pData->dData);
309                                         m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
310                                         break;
311                                 case JS_GLOBALDATA_TYPE_BOOLEAN:
312                                         m_pGlobalData->SetGlobalVariableBoolean(name, pData->bData);
313                                         m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
314                                         break;
315                                 case JS_GLOBALDATA_TYPE_STRING:
316                                         m_pGlobalData->SetGlobalVariableString(name, pData->sData);
317                                         m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
318                                         break;
319                                 case JS_GLOBALDATA_TYPE_OBJECT:
320                                         //if (pData->pData)
321                                         {
322                                                 CJS_GlobalVariableArray array;
323                                                 v8::Handle<v8::Object> obj = v8::Local<v8::Object>::New(GetJSObject()->GetIsolate(),pData->pData);
324                                                 ObjectToArray(obj, array);
325                                                 m_pGlobalData->SetGlobalVariableObject(name, array);
326                                                 m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
327                                         }
328                                         break;
329                                 case JS_GLOBALDATA_TYPE_NULL:
330                                         m_pGlobalData->SetGlobalVariableNull(name);
331                                         m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
332                                         break;
333                                 }
334                         }
335                 }
336         }
337 }
338
339 void global_alternate::ObjectToArray(v8::Handle<v8::Object> pObj, CJS_GlobalVariableArray& array)
340 {
341         v8::Handle<v8::Array> pKeyList = JS_GetObjectElementNames(pObj);
342         int     nObjElements = pKeyList->Length();
343
344         v8::Local<v8::Context> context = pObj->CreationContext();
345         v8::Isolate* isolate = context->GetIsolate();
346
347         for (int i=0; i<nObjElements; i++)
348         {
349                 
350                 CFX_WideString ws = JS_ToString(JS_GetArrayElemnet(pKeyList, i));
351                 CFX_ByteString sKey = ws.UTF8Encode();
352
353                 v8::Handle<v8::Value> v = JS_GetObjectElement(isolate, pObj, ws.c_str());
354                 FXJSVALUETYPE vt = GET_VALUE_TYPE(v);
355                 switch (vt)
356                 {
357                 case VT_number:
358                         {
359                                 CJS_KeyValue* pObjElement = new CJS_KeyValue;
360                                 pObjElement->nType = JS_GLOBALDATA_TYPE_NUMBER;
361                                 pObjElement->sKey = sKey;
362                                 pObjElement->dData = JS_ToNumber(v);
363                                 array.Add(pObjElement);
364                         }
365                         break;
366                 case VT_boolean:
367                         {
368                                 CJS_KeyValue* pObjElement = new CJS_KeyValue;
369                                 pObjElement->nType = JS_GLOBALDATA_TYPE_BOOLEAN;
370                                 pObjElement->sKey = sKey;
371                                 pObjElement->dData = JS_ToBoolean(v);
372                                 array.Add(pObjElement);
373                         }
374                         break;
375                 case VT_string:
376                         {
377                                 CFX_ByteString sValue = CJS_Value(isolate, v, VT_string);
378                                 CJS_KeyValue* pObjElement = new CJS_KeyValue;
379                                 pObjElement->nType = JS_GLOBALDATA_TYPE_STRING;
380                                 pObjElement->sKey = sKey;
381                                 pObjElement->sData = sValue;
382                                 array.Add(pObjElement);
383                         }
384                         break;
385                 case VT_object:
386                         {
387                                 CJS_KeyValue* pObjElement = new CJS_KeyValue;
388                                 pObjElement->nType = JS_GLOBALDATA_TYPE_OBJECT;
389                                 pObjElement->sKey = sKey;
390                                 ObjectToArray(JS_ToObject(v), pObjElement->objData);
391                                 array.Add(pObjElement);
392                         }
393                         break;
394                 case VT_null:
395                         {
396                                 CJS_KeyValue* pObjElement = new CJS_KeyValue;
397                                 pObjElement->nType = JS_GLOBALDATA_TYPE_NULL;
398                                 pObjElement->sKey = sKey;
399                                 array.Add(pObjElement);
400                         }
401                         break;
402                 default:
403                         break;
404                 }
405         }
406 }
407
408 void global_alternate::PutObjectProperty(v8::Handle<v8::Object> pObj, CJS_KeyValue* pData)
409 {
410         ASSERT(pData != NULL);
411
412         for (int i=0,sz=pData->objData.Count(); i<sz; i++)
413         {
414                 CJS_KeyValue* pObjData = pData->objData.GetAt(i);
415                 ASSERT(pObjData != NULL);
416
417                 switch (pObjData->nType)
418                 {
419                 case JS_GLOBALDATA_TYPE_NUMBER:
420                         JS_PutObjectNumber(NULL,(JSObject)pObj, pObjData->sKey.UTF8Decode(), pObjData->dData);
421                         break;
422                 case JS_GLOBALDATA_TYPE_BOOLEAN:
423                         JS_PutObjectBoolean(NULL,(JSObject)pObj, pObjData->sKey.UTF8Decode(), (bool)(pObjData->bData == 1));
424                         break;
425                 case JS_GLOBALDATA_TYPE_STRING:
426                         JS_PutObjectString(NULL,(JSObject)pObj, pObjData->sKey.UTF8Decode(), pObjData->sData.UTF8Decode());
427                         break;
428                 case JS_GLOBALDATA_TYPE_OBJECT:
429                         {
430                                 IJS_Runtime* pRuntime = JS_GetRuntime((JSFXObject)(*m_pJSObject));
431                                 v8::Handle<v8::Object> pNewObj = JS_NewFxDynamicObj(pRuntime, NULL, -1);
432                                 PutObjectProperty(pNewObj, pObjData);
433                                 JS_PutObjectObject(NULL, (JSObject)pObj, pObjData->sKey.UTF8Decode(), (JSObject)pNewObj);
434                         }
435                         break;
436                 case JS_GLOBALDATA_TYPE_NULL:
437                         JS_PutObjectNull(NULL,(JSObject)pObj, pObjData->sKey.UTF8Decode());
438                         break;
439                 }
440         }
441 }
442
443 void global_alternate::DestroyGlobalPersisitentVariables()
444 {
445         FX_POSITION      pos = m_mapGlobal.GetStartPosition();
446         while (pos)
447         {
448                 CFX_ByteString name; 
449                 js_global_data* pData = NULL;
450                 m_mapGlobal.GetNextAssoc(pos, name, (FX_LPVOID&)pData);
451                 delete pData;
452         }
453
454         m_mapGlobal.RemoveAll();
455 }
456
457
458 FX_BOOL global_alternate::SetGlobalVariables(FX_LPCSTR propname, int nType, 
459                                 double dData, bool bData, const CFX_ByteString& sData, JSObject pData, bool bDefaultPersistent)
460 {
461         if (propname == NULL) return FALSE;
462
463         js_global_data* pTemp = NULL;
464         m_mapGlobal.Lookup(propname, (FX_LPVOID&)pTemp);
465
466         if (pTemp)
467         {
468                 if (pTemp->bDeleted || pTemp->nType != nType)
469                 {
470                         pTemp->dData = 0;
471                         pTemp->bData = 0;
472                         pTemp->sData = "";
473                         pTemp->nType = nType;
474                 }
475
476                 pTemp->bDeleted = FALSE;
477
478                 switch (nType)
479                 {
480                 case JS_GLOBALDATA_TYPE_NUMBER:
481                         {
482                                 pTemp->dData = dData;
483                         }
484                         break;
485                 case JS_GLOBALDATA_TYPE_BOOLEAN:
486                         {
487                                 pTemp->bData = bData;
488                         }
489                         break;
490                 case JS_GLOBALDATA_TYPE_STRING:
491                         {
492                                 pTemp->sData = sData;
493                         }
494                         break;
495                 case JS_GLOBALDATA_TYPE_OBJECT:
496                         {
497                                 pTemp->pData.Reset(JS_GetRuntime(pData), pData);
498                         }
499                         break;
500                 case JS_GLOBALDATA_TYPE_NULL:
501                         break;
502                 default:
503                         return FALSE;
504                 }       
505
506                 return TRUE;
507         }
508
509         js_global_data* pNewData = NULL;
510
511         switch (nType)
512         {
513         case JS_GLOBALDATA_TYPE_NUMBER:
514                 {
515                         pNewData = new js_global_data;
516                         pNewData->nType = JS_GLOBALDATA_TYPE_NUMBER;
517                         pNewData->dData = dData;
518                         pNewData->bPersistent = bDefaultPersistent;
519                 }
520                 break;
521         case JS_GLOBALDATA_TYPE_BOOLEAN:
522                 {
523                         pNewData = new js_global_data;
524                         pNewData->nType = JS_GLOBALDATA_TYPE_BOOLEAN;
525                         pNewData->bData = bData;
526                         pNewData->bPersistent = bDefaultPersistent;
527                 }
528                 break;
529         case JS_GLOBALDATA_TYPE_STRING:
530                 {
531                         pNewData = new js_global_data;
532                         pNewData->nType = JS_GLOBALDATA_TYPE_STRING;
533                         pNewData->sData = sData;
534                         pNewData->bPersistent = bDefaultPersistent;
535                 }
536                 break;
537         case JS_GLOBALDATA_TYPE_OBJECT:
538                 {
539                         pNewData = new js_global_data;
540                         pNewData->nType = JS_GLOBALDATA_TYPE_OBJECT;
541                         pNewData->pData.Reset(JS_GetRuntime(pData), pData);
542                         pNewData->bPersistent = bDefaultPersistent;
543                 }
544                 break;
545         case JS_GLOBALDATA_TYPE_NULL:
546                 {
547                         pNewData = new js_global_data;
548                         pNewData->nType = JS_GLOBALDATA_TYPE_NULL;
549                         pNewData->bPersistent = bDefaultPersistent;
550                 }
551                 break;
552         default:
553                 return FALSE;
554         }       
555
556         m_mapGlobal.SetAt(propname, (FX_LPVOID)pNewData);
557
558         return TRUE;
559 }