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