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 "fpdfsdk/javascript/global.h"
8
9 #include <vector>
10
11 #include "core/fxcrt/fx_ext.h"
12 #include "fpdfsdk/javascript/JS_Define.h"
13 #include "fpdfsdk/javascript/JS_EventHandler.h"
14 #include "fpdfsdk/javascript/JS_GlobalData.h"
15 #include "fpdfsdk/javascript/JS_Object.h"
16 #include "fpdfsdk/javascript/JS_Value.h"
17 #include "fpdfsdk/javascript/cjs_event_context.h"
18 #include "fpdfsdk/javascript/resource.h"
19
20 JSConstSpec CJS_Global::ConstSpecs[] = {{0, JSConstSpec::Number, 0, 0}};
21
22 JSPropertySpec CJS_Global::PropertySpecs[] = {{0, 0, 0}};
23
24 JSMethodSpec CJS_Global::MethodSpecs[] = {
25 {"setPersistent", setPersistent_static},
26 {0, 0}};
27
28 IMPLEMENT_SPECIAL_JS_CLASS(CJS_Global, JSGlobalAlternate, global);
29
InitInstance(IJS_Runtime * pIRuntime)30 void CJS_Global::InitInstance(IJS_Runtime* pIRuntime) {
31 CJS_Runtime* pRuntime = static_cast<CJS_Runtime*>(pIRuntime);
32 JSGlobalAlternate* pGlobal =
33 static_cast<JSGlobalAlternate*>(GetEmbedObject());
34 pGlobal->Initial(pRuntime->GetFormFillEnv());
35 }
36
JSGlobalData()37 JSGlobalData::JSGlobalData()
38 : nType(JS_GlobalDataType::NUMBER),
39 dData(0),
40 bData(false),
41 sData(""),
42 bPersistent(false),
43 bDeleted(false) {}
44
~JSGlobalData()45 JSGlobalData::~JSGlobalData() {
46 pData.Reset();
47 }
48
JSGlobalAlternate(CJS_Object * pJSObject)49 JSGlobalAlternate::JSGlobalAlternate(CJS_Object* pJSObject)
50 : CJS_EmbedObj(pJSObject), m_pFormFillEnv(nullptr) {}
51
~JSGlobalAlternate()52 JSGlobalAlternate::~JSGlobalAlternate() {
53 DestroyGlobalPersisitentVariables();
54 m_pGlobalData->Release();
55 }
56
Initial(CPDFSDK_FormFillEnvironment * pFormFillEnv)57 void JSGlobalAlternate::Initial(CPDFSDK_FormFillEnvironment* pFormFillEnv) {
58 m_pFormFillEnv.Reset(pFormFillEnv);
59 m_pGlobalData = CJS_GlobalData::GetRetainedInstance(pFormFillEnv);
60 UpdateGlobalPersistentVariables();
61 }
62
QueryProperty(const FX_WCHAR * propname)63 bool JSGlobalAlternate::QueryProperty(const FX_WCHAR* propname) {
64 return CFX_WideString(propname) != L"setPersistent";
65 }
66
DelProperty(CJS_Runtime * pRuntime,const FX_WCHAR * propname,CFX_WideString & sError)67 bool JSGlobalAlternate::DelProperty(CJS_Runtime* pRuntime,
68 const FX_WCHAR* propname,
69 CFX_WideString& sError) {
70 auto it = m_mapGlobal.find(CFX_ByteString::FromUnicode(propname));
71 if (it == m_mapGlobal.end())
72 return false;
73
74 it->second->bDeleted = true;
75 return true;
76 }
77
DoProperty(CJS_Runtime * pRuntime,const FX_WCHAR * propname,CJS_PropValue & vp,CFX_WideString & sError)78 bool JSGlobalAlternate::DoProperty(CJS_Runtime* pRuntime,
79 const FX_WCHAR* propname,
80 CJS_PropValue& vp,
81 CFX_WideString& sError) {
82 if (vp.IsSetting()) {
83 CFX_ByteString sPropName = CFX_ByteString::FromUnicode(propname);
84 switch (vp.GetJSValue()->GetType()) {
85 case CJS_Value::VT_number: {
86 double dData;
87 vp >> dData;
88 return SetGlobalVariables(sPropName, JS_GlobalDataType::NUMBER, dData,
89 false, "", v8::Local<v8::Object>(), false);
90 }
91 case CJS_Value::VT_boolean: {
92 bool bData;
93 vp >> bData;
94 return SetGlobalVariables(sPropName, JS_GlobalDataType::BOOLEAN, 0,
95 bData, "", v8::Local<v8::Object>(), false);
96 }
97 case CJS_Value::VT_string: {
98 CFX_ByteString sData;
99 vp >> sData;
100 return SetGlobalVariables(sPropName, JS_GlobalDataType::STRING, 0,
101 false, sData, v8::Local<v8::Object>(), false);
102 }
103 case CJS_Value::VT_object: {
104 v8::Local<v8::Object> pData;
105 vp >> pData;
106 return SetGlobalVariables(sPropName, JS_GlobalDataType::OBJECT, 0,
107 false, "", pData, false);
108 }
109 case CJS_Value::VT_null: {
110 return SetGlobalVariables(sPropName, JS_GlobalDataType::NULLOBJ, 0,
111 false, "", v8::Local<v8::Object>(), false);
112 }
113 case CJS_Value::VT_undefined: {
114 DelProperty(pRuntime, propname, sError);
115 return true;
116 }
117 default:
118 break;
119 }
120 } else {
121 auto it = m_mapGlobal.find(CFX_ByteString::FromUnicode(propname));
122 if (it == m_mapGlobal.end()) {
123 vp.GetJSValue()->SetNull(pRuntime);
124 return true;
125 }
126 JSGlobalData* pData = it->second;
127 if (pData->bDeleted) {
128 vp.GetJSValue()->SetNull(pRuntime);
129 return true;
130 }
131 switch (pData->nType) {
132 case JS_GlobalDataType::NUMBER:
133 vp << pData->dData;
134 return true;
135 case JS_GlobalDataType::BOOLEAN:
136 vp << pData->bData;
137 return true;
138 case JS_GlobalDataType::STRING:
139 vp << pData->sData;
140 return true;
141 case JS_GlobalDataType::OBJECT: {
142 v8::Local<v8::Object> obj = v8::Local<v8::Object>::New(
143 vp.GetJSRuntime()->GetIsolate(), pData->pData);
144 vp << obj;
145 return true;
146 }
147 case JS_GlobalDataType::NULLOBJ:
148 vp.GetJSValue()->SetNull(pRuntime);
149 return true;
150 default:
151 break;
152 }
153 }
154 return false;
155 }
156
setPersistent(CJS_Runtime * pRuntime,const std::vector<CJS_Value> & params,CJS_Value & vRet,CFX_WideString & sError)157 bool JSGlobalAlternate::setPersistent(CJS_Runtime* pRuntime,
158 const std::vector<CJS_Value>& params,
159 CJS_Value& vRet,
160 CFX_WideString& sError) {
161 if (params.size() != 2) {
162 sError = JSGetStringFromID(IDS_STRING_JSPARAMERROR);
163 return false;
164 }
165
166 auto it = m_mapGlobal.find(params[0].ToCFXByteString(pRuntime));
167 if (it != m_mapGlobal.end()) {
168 JSGlobalData* pData = it->second;
169 if (!pData->bDeleted) {
170 pData->bPersistent = params[1].ToBool(pRuntime);
171 return true;
172 }
173 }
174
175 sError = JSGetStringFromID(IDS_STRING_JSNOGLOBAL);
176 return false;
177 }
178
UpdateGlobalPersistentVariables()179 void JSGlobalAlternate::UpdateGlobalPersistentVariables() {
180 CJS_Runtime* pRuntime =
181 static_cast<CJS_Runtime*>(CFXJS_Engine::CurrentEngineFromIsolate(
182 m_pJSObject->ToV8Object()->GetIsolate()));
183
184 for (int i = 0, sz = m_pGlobalData->GetSize(); i < sz; i++) {
185 CJS_GlobalData_Element* pData = m_pGlobalData->GetAt(i);
186 switch (pData->data.nType) {
187 case JS_GlobalDataType::NUMBER:
188 SetGlobalVariables(pData->data.sKey, JS_GlobalDataType::NUMBER,
189 pData->data.dData, false, "",
190 v8::Local<v8::Object>(), pData->bPersistent == 1);
191 pRuntime->PutObjectProperty(m_pJSObject->ToV8Object(),
192 pData->data.sKey.UTF8Decode(),
193 pRuntime->NewNumber(pData->data.dData));
194 break;
195 case JS_GlobalDataType::BOOLEAN:
196 SetGlobalVariables(pData->data.sKey, JS_GlobalDataType::BOOLEAN, 0,
197 pData->data.bData == 1, "", v8::Local<v8::Object>(),
198 pData->bPersistent == 1);
199 pRuntime->PutObjectProperty(
200 m_pJSObject->ToV8Object(), pData->data.sKey.UTF8Decode(),
201 pRuntime->NewBoolean(pData->data.bData == 1));
202 break;
203 case JS_GlobalDataType::STRING:
204 SetGlobalVariables(pData->data.sKey, JS_GlobalDataType::STRING, 0,
205 false, pData->data.sData, v8::Local<v8::Object>(),
206 pData->bPersistent == 1);
207 pRuntime->PutObjectProperty(
208 m_pJSObject->ToV8Object(), pData->data.sKey.UTF8Decode(),
209 pRuntime->NewString(pData->data.sData.UTF8Decode().AsStringC()));
210 break;
211 case JS_GlobalDataType::OBJECT: {
212 v8::Local<v8::Object> pObj = pRuntime->NewFxDynamicObj(-1);
213 PutObjectProperty(pObj, &pData->data);
214 SetGlobalVariables(pData->data.sKey, JS_GlobalDataType::OBJECT, 0,
215 false, "", pObj, pData->bPersistent == 1);
216 pRuntime->PutObjectProperty(m_pJSObject->ToV8Object(),
217 pData->data.sKey.UTF8Decode(), pObj);
218 } break;
219 case JS_GlobalDataType::NULLOBJ:
220 SetGlobalVariables(pData->data.sKey, JS_GlobalDataType::NULLOBJ, 0,
221 false, "", v8::Local<v8::Object>(),
222 pData->bPersistent == 1);
223 pRuntime->PutObjectProperty(m_pJSObject->ToV8Object(),
224 pData->data.sKey.UTF8Decode(),
225 pRuntime->NewNull());
226 break;
227 }
228 }
229 }
230
CommitGlobalPersisitentVariables(CJS_Runtime * pRuntime)231 void JSGlobalAlternate::CommitGlobalPersisitentVariables(
232 CJS_Runtime* pRuntime) {
233 for (auto it = m_mapGlobal.begin(); it != m_mapGlobal.end(); ++it) {
234 CFX_ByteString name = it->first;
235 JSGlobalData* pData = it->second;
236 if (pData->bDeleted) {
237 m_pGlobalData->DeleteGlobalVariable(name);
238 } else {
239 switch (pData->nType) {
240 case JS_GlobalDataType::NUMBER:
241 m_pGlobalData->SetGlobalVariableNumber(name, pData->dData);
242 m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
243 break;
244 case JS_GlobalDataType::BOOLEAN:
245 m_pGlobalData->SetGlobalVariableBoolean(name, pData->bData);
246 m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
247 break;
248 case JS_GlobalDataType::STRING:
249 m_pGlobalData->SetGlobalVariableString(name, pData->sData);
250 m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
251 break;
252 case JS_GlobalDataType::OBJECT: {
253 CJS_GlobalVariableArray array;
254 v8::Local<v8::Object> obj = v8::Local<v8::Object>::New(
255 GetJSObject()->GetIsolate(), pData->pData);
256 ObjectToArray(pRuntime, obj, array);
257 m_pGlobalData->SetGlobalVariableObject(name, array);
258 m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
259 } break;
260 case JS_GlobalDataType::NULLOBJ:
261 m_pGlobalData->SetGlobalVariableNull(name);
262 m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent);
263 break;
264 }
265 }
266 }
267 }
268
ObjectToArray(CJS_Runtime * pRuntime,v8::Local<v8::Object> pObj,CJS_GlobalVariableArray & array)269 void JSGlobalAlternate::ObjectToArray(CJS_Runtime* pRuntime,
270 v8::Local<v8::Object> pObj,
271 CJS_GlobalVariableArray& array) {
272 std::vector<CFX_WideString> pKeyList = pRuntime->GetObjectPropertyNames(pObj);
273 for (const auto& ws : pKeyList) {
274 CFX_ByteString sKey = ws.UTF8Encode();
275 v8::Local<v8::Value> v = pRuntime->GetObjectProperty(pObj, ws);
276 switch (CJS_Value::GetValueType(v)) {
277 case CJS_Value::VT_number: {
278 CJS_KeyValue* pObjElement = new CJS_KeyValue;
279 pObjElement->nType = JS_GlobalDataType::NUMBER;
280 pObjElement->sKey = sKey;
281 pObjElement->dData = pRuntime->ToDouble(v);
282 array.Add(pObjElement);
283 } break;
284 case CJS_Value::VT_boolean: {
285 CJS_KeyValue* pObjElement = new CJS_KeyValue;
286 pObjElement->nType = JS_GlobalDataType::BOOLEAN;
287 pObjElement->sKey = sKey;
288 pObjElement->dData = pRuntime->ToBoolean(v);
289 array.Add(pObjElement);
290 } break;
291 case CJS_Value::VT_string: {
292 CFX_ByteString sValue =
293 CJS_Value(pRuntime, v).ToCFXByteString(pRuntime);
294 CJS_KeyValue* pObjElement = new CJS_KeyValue;
295 pObjElement->nType = JS_GlobalDataType::STRING;
296 pObjElement->sKey = sKey;
297 pObjElement->sData = sValue;
298 array.Add(pObjElement);
299 } break;
300 case CJS_Value::VT_object: {
301 CJS_KeyValue* pObjElement = new CJS_KeyValue;
302 pObjElement->nType = JS_GlobalDataType::OBJECT;
303 pObjElement->sKey = sKey;
304 ObjectToArray(pRuntime, pRuntime->ToObject(v), pObjElement->objData);
305 array.Add(pObjElement);
306 } break;
307 case CJS_Value::VT_null: {
308 CJS_KeyValue* pObjElement = new CJS_KeyValue;
309 pObjElement->nType = JS_GlobalDataType::NULLOBJ;
310 pObjElement->sKey = sKey;
311 array.Add(pObjElement);
312 } break;
313 default:
314 break;
315 }
316 }
317 }
318
PutObjectProperty(v8::Local<v8::Object> pObj,CJS_KeyValue * pData)319 void JSGlobalAlternate::PutObjectProperty(v8::Local<v8::Object> pObj,
320 CJS_KeyValue* pData) {
321 CJS_Runtime* pRuntime = CJS_Runtime::CurrentRuntimeFromIsolate(
322 m_pJSObject->ToV8Object()->GetIsolate());
323
324 for (int i = 0, sz = pData->objData.Count(); i < sz; i++) {
325 CJS_KeyValue* pObjData = pData->objData.GetAt(i);
326 switch (pObjData->nType) {
327 case JS_GlobalDataType::NUMBER:
328 pRuntime->PutObjectProperty(pObj, pObjData->sKey.UTF8Decode(),
329 pRuntime->NewNumber(pObjData->dData));
330 break;
331 case JS_GlobalDataType::BOOLEAN:
332 pRuntime->PutObjectProperty(pObj, pObjData->sKey.UTF8Decode(),
333 pRuntime->NewBoolean(pObjData->bData == 1));
334 break;
335 case JS_GlobalDataType::STRING:
336 pRuntime->PutObjectProperty(
337 pObj, pObjData->sKey.UTF8Decode(),
338 pRuntime->NewString(pObjData->sData.UTF8Decode().AsStringC()));
339 break;
340 case JS_GlobalDataType::OBJECT: {
341 v8::Local<v8::Object> pNewObj = pRuntime->NewFxDynamicObj(-1);
342 PutObjectProperty(pNewObj, pObjData);
343 pRuntime->PutObjectProperty(pObj, pObjData->sKey.UTF8Decode(), pNewObj);
344 } break;
345 case JS_GlobalDataType::NULLOBJ:
346 pRuntime->PutObjectProperty(pObj, pObjData->sKey.UTF8Decode(),
347 pRuntime->NewNull());
348 break;
349 }
350 }
351 }
352
DestroyGlobalPersisitentVariables()353 void JSGlobalAlternate::DestroyGlobalPersisitentVariables() {
354 for (const auto& pair : m_mapGlobal) {
355 delete pair.second;
356 }
357 m_mapGlobal.clear();
358 }
359
SetGlobalVariables(const CFX_ByteString & propname,JS_GlobalDataType nType,double dData,bool bData,const CFX_ByteString & sData,v8::Local<v8::Object> pData,bool bDefaultPersistent)360 bool JSGlobalAlternate::SetGlobalVariables(const CFX_ByteString& propname,
361 JS_GlobalDataType nType,
362 double dData,
363 bool bData,
364 const CFX_ByteString& sData,
365 v8::Local<v8::Object> pData,
366 bool bDefaultPersistent) {
367 if (propname.IsEmpty())
368 return false;
369
370 auto it = m_mapGlobal.find(propname);
371 if (it != m_mapGlobal.end()) {
372 JSGlobalData* pTemp = it->second;
373 if (pTemp->bDeleted || pTemp->nType != nType) {
374 pTemp->dData = 0;
375 pTemp->bData = 0;
376 pTemp->sData = "";
377 pTemp->nType = nType;
378 }
379
380 pTemp->bDeleted = false;
381 switch (nType) {
382 case JS_GlobalDataType::NUMBER: {
383 pTemp->dData = dData;
384 } break;
385 case JS_GlobalDataType::BOOLEAN: {
386 pTemp->bData = bData;
387 } break;
388 case JS_GlobalDataType::STRING: {
389 pTemp->sData = sData;
390 } break;
391 case JS_GlobalDataType::OBJECT: {
392 pTemp->pData.Reset(pData->GetIsolate(), pData);
393 } break;
394 case JS_GlobalDataType::NULLOBJ:
395 break;
396 default:
397 return false;
398 }
399 return true;
400 }
401
402 JSGlobalData* pNewData = nullptr;
403
404 switch (nType) {
405 case JS_GlobalDataType::NUMBER: {
406 pNewData = new JSGlobalData;
407 pNewData->nType = JS_GlobalDataType::NUMBER;
408 pNewData->dData = dData;
409 pNewData->bPersistent = bDefaultPersistent;
410 } break;
411 case JS_GlobalDataType::BOOLEAN: {
412 pNewData = new JSGlobalData;
413 pNewData->nType = JS_GlobalDataType::BOOLEAN;
414 pNewData->bData = bData;
415 pNewData->bPersistent = bDefaultPersistent;
416 } break;
417 case JS_GlobalDataType::STRING: {
418 pNewData = new JSGlobalData;
419 pNewData->nType = JS_GlobalDataType::STRING;
420 pNewData->sData = sData;
421 pNewData->bPersistent = bDefaultPersistent;
422 } break;
423 case JS_GlobalDataType::OBJECT: {
424 pNewData = new JSGlobalData;
425 pNewData->nType = JS_GlobalDataType::OBJECT;
426 pNewData->pData.Reset(pData->GetIsolate(), pData);
427 pNewData->bPersistent = bDefaultPersistent;
428 } break;
429 case JS_GlobalDataType::NULLOBJ: {
430 pNewData = new JSGlobalData;
431 pNewData->nType = JS_GlobalDataType::NULLOBJ;
432 pNewData->bPersistent = bDefaultPersistent;
433 } break;
434 default:
435 return false;
436 }
437
438 m_mapGlobal[propname] = pNewData;
439 return true;
440 }
441