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 "global.h" 8 9 #include "JS_Context.h" 10 #include "JS_Define.h" 11 #include "JS_EventHandler.h" 12 #include "JS_GlobalData.h" 13 #include "JS_Object.h" 14 #include "JS_Value.h" 15 #include "core/include/fxcrt/fx_ext.h" 16 #include "fpdfsdk/include/javascript/IJavaScript.h" 17 #include "resource.h" 18 19 /* ---------------------------- global ---------------------------- */ 20 21 // Helper class for compile-time calculation of hash values in order to 22 // avoid having global object initializers. 23 template <unsigned ACC, wchar_t... Ns> 24 struct CHash; 25 26 // Only needed to hash single-character strings. 27 template <wchar_t N> 28 struct CHash<N> { 29 static const unsigned value = N; 30 }; 31 32 template <unsigned ACC, wchar_t N> 33 struct CHash<ACC, N> { 34 static const unsigned value = (ACC * 1313LLU + N) & 0xFFFFFFFF; 35 }; 36 37 template <unsigned ACC, wchar_t N, wchar_t... Ns> 38 struct CHash<ACC, N, Ns...> { 39 static const unsigned value = CHash<CHash<ACC, N>::value, Ns...>::value; 40 }; 41 42 const unsigned int JSCONST_nStringHash = 43 CHash<'s', 't', 'r', 'i', 'n', 'g'>::value; 44 const unsigned int JSCONST_nNumberHash = 45 CHash<'n', 'u', 'm', 'b', 'e', 'r'>::value; 46 const unsigned int JSCONST_nBoolHash = 47 CHash<'b', 'o', 'o', 'l', 'e', 'a', 'n'>::value; 48 const unsigned int JSCONST_nDateHash = CHash<'d', 'a', 't', 'e'>::value; 49 const unsigned int JSCONST_nObjectHash = 50 CHash<'o', 'b', 'j', 'e', 'c', 't'>::value; 51 const unsigned int JSCONST_nFXobjHash = CHash<'f', 'x', 'o', 'b', 'j'>::value; 52 const unsigned int JSCONST_nNullHash = CHash<'n', 'u', 'l', 'l'>::value; 53 const unsigned int JSCONST_nUndefHash = 54 CHash<'u', 'n', 'd', 'e', 'f', 'i', 'n', 'e', 'd'>::value; 55 56 static unsigned JS_CalcHash(const wchar_t* main) { 57 return (unsigned)FX_HashCode_String_GetW(main, FXSYS_wcslen(main)); 58 } 59 60 #ifdef _DEBUG 61 class HashVerify { 62 public: 63 HashVerify(); 64 } g_hashVerify; 65 66 HashVerify::HashVerify() { 67 ASSERT(JSCONST_nStringHash == JS_CalcHash(kFXJSValueNameString)); 68 ASSERT(JSCONST_nNumberHash == JS_CalcHash(kFXJSValueNameNumber)); 69 ASSERT(JSCONST_nBoolHash == JS_CalcHash(kFXJSValueNameBoolean)); 70 ASSERT(JSCONST_nDateHash == JS_CalcHash(kFXJSValueNameDate)); 71 ASSERT(JSCONST_nObjectHash == JS_CalcHash(kFXJSValueNameObject)); 72 ASSERT(JSCONST_nFXobjHash == JS_CalcHash(kFXJSValueNameFxobj)); 73 ASSERT(JSCONST_nNullHash == JS_CalcHash(kFXJSValueNameNull)); 74 ASSERT(JSCONST_nUndefHash == JS_CalcHash(kFXJSValueNameUndefined)); 75 } 76 #endif 77 78 BEGIN_JS_STATIC_CONST(CJS_Global) 79 END_JS_STATIC_CONST() 80 81 BEGIN_JS_STATIC_PROP(CJS_Global) 82 END_JS_STATIC_PROP() 83 84 BEGIN_JS_STATIC_METHOD(CJS_Global) 85 JS_STATIC_METHOD_ENTRY(setPersistent) 86 END_JS_STATIC_METHOD() 87 88 IMPLEMENT_SPECIAL_JS_CLASS(CJS_Global, JSGlobalAlternate, global); 89 90 void CJS_Global::InitInstance(IJS_Runtime* pIRuntime) { 91 CJS_Runtime* pRuntime = static_cast<CJS_Runtime*>(pIRuntime); 92 JSGlobalAlternate* pGlobal = 93 static_cast<JSGlobalAlternate*>(GetEmbedObject()); 94 pGlobal->Initial(pRuntime->GetReaderApp()); 95 } 96 97 JSGlobalAlternate::JSGlobalAlternate(CJS_Object* pJSObject) 98 : CJS_EmbedObj(pJSObject), m_pApp(NULL) { 99 } 100 101 JSGlobalAlternate::~JSGlobalAlternate() { 102 DestroyGlobalPersisitentVariables(); 103 m_pGlobalData->Release(); 104 } 105 106 void JSGlobalAlternate::Initial(CPDFDoc_Environment* pApp) { 107 m_pApp = pApp; 108 m_pGlobalData = CJS_GlobalData::GetRetainedInstance(pApp); 109 UpdateGlobalPersistentVariables(); 110 } 111 112 FX_BOOL JSGlobalAlternate::QueryProperty(const FX_WCHAR* propname) { 113 return CFX_WideString(propname) != L"setPersistent"; 114 } 115 116 FX_BOOL JSGlobalAlternate::DelProperty(IJS_Context* cc, 117 const FX_WCHAR* propname, 118 CFX_WideString& sError) { 119 auto it = m_mapGlobal.find(CFX_ByteString::FromUnicode(propname)); 120 if (it == m_mapGlobal.end()) 121 return FALSE; 122 123 it->second->bDeleted = TRUE; 124 return TRUE; 125 } 126 127 FX_BOOL JSGlobalAlternate::DoProperty(IJS_Context* cc, 128 const FX_WCHAR* propname, 129 CJS_PropValue& vp, 130 CFX_WideString& sError) { 131 if (vp.IsSetting()) { 132 CFX_ByteString sPropName = CFX_ByteString::FromUnicode(propname); 133 switch (vp.GetType()) { 134 case CJS_Value::VT_number: { 135 double dData; 136 vp >> dData; 137 return SetGlobalVariables(sPropName, JS_GLOBALDATA_TYPE_NUMBER, dData, 138 false, "", v8::Local<v8::Object>(), FALSE); 139 } 140 case CJS_Value::VT_boolean: { 141 bool bData; 142 vp >> bData; 143 return SetGlobalVariables(sPropName, JS_GLOBALDATA_TYPE_BOOLEAN, 0, 144 bData, "", v8::Local<v8::Object>(), FALSE); 145 } 146 case CJS_Value::VT_string: { 147 CFX_ByteString sData; 148 vp >> sData; 149 return SetGlobalVariables(sPropName, JS_GLOBALDATA_TYPE_STRING, 0, 150 false, sData, v8::Local<v8::Object>(), FALSE); 151 } 152 case CJS_Value::VT_object: { 153 v8::Local<v8::Object> pData; 154 vp >> pData; 155 return SetGlobalVariables(sPropName, JS_GLOBALDATA_TYPE_OBJECT, 0, 156 false, "", pData, FALSE); 157 } 158 case CJS_Value::VT_null: { 159 return SetGlobalVariables(sPropName, JS_GLOBALDATA_TYPE_NULL, 0, false, 160 "", v8::Local<v8::Object>(), FALSE); 161 } 162 case CJS_Value::VT_undefined: { 163 DelProperty(cc, propname, sError); 164 return TRUE; 165 } 166 default: 167 break; 168 } 169 } else { 170 auto it = m_mapGlobal.find(CFX_ByteString::FromUnicode(propname)); 171 if (it == m_mapGlobal.end()) { 172 vp.SetNull(); 173 return TRUE; 174 } 175 JSGlobalData* pData = it->second; 176 if (pData->bDeleted) { 177 vp.SetNull(); 178 return TRUE; 179 } 180 switch (pData->nType) { 181 case JS_GLOBALDATA_TYPE_NUMBER: 182 vp << pData->dData; 183 return TRUE; 184 case JS_GLOBALDATA_TYPE_BOOLEAN: 185 vp << pData->bData; 186 return TRUE; 187 case JS_GLOBALDATA_TYPE_STRING: 188 vp << pData->sData; 189 return TRUE; 190 case JS_GLOBALDATA_TYPE_OBJECT: { 191 v8::Local<v8::Object> obj = v8::Local<v8::Object>::New( 192 vp.GetJSRuntime()->GetIsolate(), pData->pData); 193 vp << obj; 194 return TRUE; 195 } 196 case JS_GLOBALDATA_TYPE_NULL: 197 vp.SetNull(); 198 return TRUE; 199 default: 200 break; 201 } 202 } 203 return FALSE; 204 } 205 206 FX_BOOL JSGlobalAlternate::setPersistent(IJS_Context* cc, 207 const std::vector<CJS_Value>& params, 208 CJS_Value& vRet, 209 CFX_WideString& sError) { 210 CJS_Context* pContext = static_cast<CJS_Context*>(cc); 211 if (params.size() != 2) { 212 sError = JSGetStringFromID(pContext, IDS_STRING_JSPARAMERROR); 213 return FALSE; 214 } 215 216 auto it = m_mapGlobal.find(params[0].ToCFXByteString()); 217 if (it != m_mapGlobal.end()) { 218 JSGlobalData* pData = it->second; 219 if (!pData->bDeleted) { 220 pData->bPersistent = params[1].ToBool(); 221 return TRUE; 222 } 223 } 224 225 sError = JSGetStringFromID(pContext, IDS_STRING_JSNOGLOBAL); 226 return FALSE; 227 } 228 229 void JSGlobalAlternate::UpdateGlobalPersistentVariables() { 230 for (int i = 0, sz = m_pGlobalData->GetSize(); i < sz; i++) { 231 CJS_GlobalData_Element* pData = m_pGlobalData->GetAt(i); 232 switch (pData->data.nType) { 233 case JS_GLOBALDATA_TYPE_NUMBER: 234 SetGlobalVariables(pData->data.sKey, JS_GLOBALDATA_TYPE_NUMBER, 235 pData->data.dData, false, "", 236 v8::Local<v8::Object>(), pData->bPersistent == 1); 237 FXJS_PutObjectNumber(NULL, m_pJSObject->ToV8Object(), 238 pData->data.sKey.UTF8Decode().c_str(), 239 pData->data.dData); 240 break; 241 case JS_GLOBALDATA_TYPE_BOOLEAN: 242 SetGlobalVariables(pData->data.sKey, JS_GLOBALDATA_TYPE_BOOLEAN, 0, 243 (bool)(pData->data.bData == 1), "", 244 v8::Local<v8::Object>(), pData->bPersistent == 1); 245 FXJS_PutObjectBoolean(NULL, m_pJSObject->ToV8Object(), 246 pData->data.sKey.UTF8Decode().c_str(), 247 (bool)(pData->data.bData == 1)); 248 break; 249 case JS_GLOBALDATA_TYPE_STRING: 250 SetGlobalVariables(pData->data.sKey, JS_GLOBALDATA_TYPE_STRING, 0, 251 false, pData->data.sData, v8::Local<v8::Object>(), 252 pData->bPersistent == 1); 253 FXJS_PutObjectString(NULL, m_pJSObject->ToV8Object(), 254 pData->data.sKey.UTF8Decode().c_str(), 255 pData->data.sData.UTF8Decode().c_str()); 256 break; 257 case JS_GLOBALDATA_TYPE_OBJECT: { 258 v8::Isolate* pRuntime = m_pJSObject->ToV8Object()->GetIsolate(); 259 v8::Local<v8::Object> pObj = FXJS_NewFxDynamicObj(pRuntime, NULL, -1); 260 261 PutObjectProperty(pObj, &pData->data); 262 263 SetGlobalVariables(pData->data.sKey, JS_GLOBALDATA_TYPE_OBJECT, 0, 264 false, "", pObj, pData->bPersistent == 1); 265 FXJS_PutObjectObject(NULL, m_pJSObject->ToV8Object(), 266 pData->data.sKey.UTF8Decode().c_str(), pObj); 267 268 } break; 269 case JS_GLOBALDATA_TYPE_NULL: 270 SetGlobalVariables(pData->data.sKey, JS_GLOBALDATA_TYPE_NULL, 0, false, 271 "", v8::Local<v8::Object>(), 272 pData->bPersistent == 1); 273 FXJS_PutObjectNull(NULL, m_pJSObject->ToV8Object(), 274 pData->data.sKey.UTF8Decode().c_str()); 275 break; 276 } 277 } 278 } 279 280 void JSGlobalAlternate::CommitGlobalPersisitentVariables(IJS_Context* cc) { 281 for (auto it = m_mapGlobal.begin(); it != m_mapGlobal.end(); ++it) { 282 CFX_ByteString name = it->first; 283 JSGlobalData* pData = it->second; 284 if (pData->bDeleted) { 285 m_pGlobalData->DeleteGlobalVariable(name); 286 } else { 287 switch (pData->nType) { 288 case JS_GLOBALDATA_TYPE_NUMBER: 289 m_pGlobalData->SetGlobalVariableNumber(name, pData->dData); 290 m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent); 291 break; 292 case JS_GLOBALDATA_TYPE_BOOLEAN: 293 m_pGlobalData->SetGlobalVariableBoolean(name, pData->bData); 294 m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent); 295 break; 296 case JS_GLOBALDATA_TYPE_STRING: 297 m_pGlobalData->SetGlobalVariableString(name, pData->sData); 298 m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent); 299 break; 300 case JS_GLOBALDATA_TYPE_OBJECT: 301 { 302 CJS_GlobalVariableArray array; 303 v8::Local<v8::Object> obj = v8::Local<v8::Object>::New( 304 GetJSObject()->GetIsolate(), pData->pData); 305 ObjectToArray(cc, obj, array); 306 m_pGlobalData->SetGlobalVariableObject(name, array); 307 m_pGlobalData->SetGlobalVariablePersistent(name, 308 pData->bPersistent); 309 } 310 break; 311 case JS_GLOBALDATA_TYPE_NULL: 312 m_pGlobalData->SetGlobalVariableNull(name); 313 m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent); 314 break; 315 } 316 } 317 } 318 } 319 320 void JSGlobalAlternate::ObjectToArray(IJS_Context* cc, 321 v8::Local<v8::Object> pObj, 322 CJS_GlobalVariableArray& array) { 323 v8::Isolate* isolate = pObj->GetIsolate(); 324 CJS_Runtime* pRuntime = CJS_Runtime::FromContext(cc); 325 326 v8::Local<v8::Array> pKeyList = FXJS_GetObjectElementNames(isolate, pObj); 327 int nObjElements = pKeyList->Length(); 328 for (int i = 0; i < nObjElements; i++) { 329 CFX_WideString ws = 330 FXJS_ToString(isolate, FXJS_GetArrayElement(isolate, pKeyList, i)); 331 CFX_ByteString sKey = ws.UTF8Encode(); 332 333 v8::Local<v8::Value> v = FXJS_GetObjectElement(isolate, pObj, ws.c_str()); 334 switch (GET_VALUE_TYPE(v)) { 335 case CJS_Value::VT_number: { 336 CJS_KeyValue* pObjElement = new CJS_KeyValue; 337 pObjElement->nType = JS_GLOBALDATA_TYPE_NUMBER; 338 pObjElement->sKey = sKey; 339 pObjElement->dData = FXJS_ToNumber(isolate, v); 340 array.Add(pObjElement); 341 } break; 342 case CJS_Value::VT_boolean: { 343 CJS_KeyValue* pObjElement = new CJS_KeyValue; 344 pObjElement->nType = JS_GLOBALDATA_TYPE_BOOLEAN; 345 pObjElement->sKey = sKey; 346 pObjElement->dData = FXJS_ToBoolean(isolate, v); 347 array.Add(pObjElement); 348 } break; 349 case CJS_Value::VT_string: { 350 CFX_ByteString sValue = 351 CJS_Value(pRuntime, v, CJS_Value::VT_string).ToCFXByteString(); 352 CJS_KeyValue* pObjElement = new CJS_KeyValue; 353 pObjElement->nType = JS_GLOBALDATA_TYPE_STRING; 354 pObjElement->sKey = sKey; 355 pObjElement->sData = sValue; 356 array.Add(pObjElement); 357 } break; 358 case CJS_Value::VT_object: { 359 CJS_KeyValue* pObjElement = new CJS_KeyValue; 360 pObjElement->nType = JS_GLOBALDATA_TYPE_OBJECT; 361 pObjElement->sKey = sKey; 362 ObjectToArray(cc, FXJS_ToObject(isolate, v), pObjElement->objData); 363 array.Add(pObjElement); 364 } break; 365 case CJS_Value::VT_null: { 366 CJS_KeyValue* pObjElement = new CJS_KeyValue; 367 pObjElement->nType = JS_GLOBALDATA_TYPE_NULL; 368 pObjElement->sKey = sKey; 369 array.Add(pObjElement); 370 } break; 371 default: 372 break; 373 } 374 } 375 } 376 377 void JSGlobalAlternate::PutObjectProperty(v8::Local<v8::Object> pObj, 378 CJS_KeyValue* pData) { 379 for (int i = 0, sz = pData->objData.Count(); i < sz; i++) { 380 CJS_KeyValue* pObjData = pData->objData.GetAt(i); 381 switch (pObjData->nType) { 382 case JS_GLOBALDATA_TYPE_NUMBER: 383 FXJS_PutObjectNumber(NULL, pObj, pObjData->sKey.UTF8Decode().c_str(), 384 pObjData->dData); 385 break; 386 case JS_GLOBALDATA_TYPE_BOOLEAN: 387 FXJS_PutObjectBoolean(NULL, pObj, pObjData->sKey.UTF8Decode().c_str(), 388 pObjData->bData == 1); 389 break; 390 case JS_GLOBALDATA_TYPE_STRING: 391 FXJS_PutObjectString(NULL, pObj, pObjData->sKey.UTF8Decode().c_str(), 392 pObjData->sData.UTF8Decode().c_str()); 393 break; 394 case JS_GLOBALDATA_TYPE_OBJECT: { 395 v8::Isolate* pRuntime = m_pJSObject->ToV8Object()->GetIsolate(); 396 v8::Local<v8::Object> pNewObj = 397 FXJS_NewFxDynamicObj(pRuntime, NULL, -1); 398 PutObjectProperty(pNewObj, pObjData); 399 FXJS_PutObjectObject(NULL, pObj, pObjData->sKey.UTF8Decode().c_str(), 400 pNewObj); 401 } break; 402 case JS_GLOBALDATA_TYPE_NULL: 403 FXJS_PutObjectNull(NULL, pObj, pObjData->sKey.UTF8Decode().c_str()); 404 break; 405 } 406 } 407 } 408 409 void JSGlobalAlternate::DestroyGlobalPersisitentVariables() { 410 for (const auto& pair : m_mapGlobal) { 411 delete pair.second; 412 } 413 m_mapGlobal.clear(); 414 } 415 416 FX_BOOL JSGlobalAlternate::SetGlobalVariables(const FX_CHAR* propname, 417 int nType, 418 double dData, 419 bool bData, 420 const CFX_ByteString& sData, 421 v8::Local<v8::Object> pData, 422 bool bDefaultPersistent) { 423 if (!propname) 424 return FALSE; 425 426 auto it = m_mapGlobal.find(propname); 427 if (it != m_mapGlobal.end()) { 428 JSGlobalData* pTemp = it->second; 429 if (pTemp->bDeleted || pTemp->nType != nType) { 430 pTemp->dData = 0; 431 pTemp->bData = 0; 432 pTemp->sData = ""; 433 pTemp->nType = nType; 434 } 435 436 pTemp->bDeleted = FALSE; 437 switch (nType) { 438 case JS_GLOBALDATA_TYPE_NUMBER: { 439 pTemp->dData = dData; 440 } break; 441 case JS_GLOBALDATA_TYPE_BOOLEAN: { 442 pTemp->bData = bData; 443 } break; 444 case JS_GLOBALDATA_TYPE_STRING: { 445 pTemp->sData = sData; 446 } break; 447 case JS_GLOBALDATA_TYPE_OBJECT: { 448 pTemp->pData.Reset(pData->GetIsolate(), pData); 449 } break; 450 case JS_GLOBALDATA_TYPE_NULL: 451 break; 452 default: 453 return FALSE; 454 } 455 return TRUE; 456 } 457 458 JSGlobalData* pNewData = NULL; 459 460 switch (nType) { 461 case JS_GLOBALDATA_TYPE_NUMBER: { 462 pNewData = new JSGlobalData; 463 pNewData->nType = JS_GLOBALDATA_TYPE_NUMBER; 464 pNewData->dData = dData; 465 pNewData->bPersistent = bDefaultPersistent; 466 } break; 467 case JS_GLOBALDATA_TYPE_BOOLEAN: { 468 pNewData = new JSGlobalData; 469 pNewData->nType = JS_GLOBALDATA_TYPE_BOOLEAN; 470 pNewData->bData = bData; 471 pNewData->bPersistent = bDefaultPersistent; 472 } break; 473 case JS_GLOBALDATA_TYPE_STRING: { 474 pNewData = new JSGlobalData; 475 pNewData->nType = JS_GLOBALDATA_TYPE_STRING; 476 pNewData->sData = sData; 477 pNewData->bPersistent = bDefaultPersistent; 478 } break; 479 case JS_GLOBALDATA_TYPE_OBJECT: { 480 pNewData = new JSGlobalData; 481 pNewData->nType = JS_GLOBALDATA_TYPE_OBJECT; 482 pNewData->pData.Reset(pData->GetIsolate(), pData); 483 pNewData->bPersistent = bDefaultPersistent; 484 } break; 485 case JS_GLOBALDATA_TYPE_NULL: { 486 pNewData = new JSGlobalData; 487 pNewData->nType = JS_GLOBALDATA_TYPE_NULL; 488 pNewData->bPersistent = bDefaultPersistent; 489 } break; 490 default: 491 return FALSE; 492 } 493 494 m_mapGlobal[propname] = pNewData; 495 return TRUE; 496 } 497 498 CJS_Value::Type GET_VALUE_TYPE(v8::Local<v8::Value> p) { 499 const unsigned int nHash = JS_CalcHash(FXJS_GetTypeof(p)); 500 501 if (nHash == JSCONST_nUndefHash) 502 return CJS_Value::VT_undefined; 503 if (nHash == JSCONST_nNullHash) 504 return CJS_Value::VT_null; 505 if (nHash == JSCONST_nStringHash) 506 return CJS_Value::VT_string; 507 if (nHash == JSCONST_nNumberHash) 508 return CJS_Value::VT_number; 509 if (nHash == JSCONST_nBoolHash) 510 return CJS_Value::VT_boolean; 511 if (nHash == JSCONST_nDateHash) 512 return CJS_Value::VT_date; 513 if (nHash == JSCONST_nObjectHash) 514 return CJS_Value::VT_object; 515 if (nHash == JSCONST_nFXobjHash) 516 return CJS_Value::VT_fxobject; 517 518 return CJS_Value::VT_unknown; 519 } 520