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