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