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 "fxjs/fxjs_v8.h" 8 9 #include <vector> 10 11 #include "core/fxcrt/fx_basic.h" 12 13 // Keep this consistent with the values defined in gin/public/context_holder.h 14 // (without actually requiring a dependency on gin itself for the standalone 15 // embedders of PDFIum). The value we want to use is: 16 // kPerContextDataStartIndex + kEmbedderPDFium, which is 3. 17 static const unsigned int kPerContextDataIndex = 3u; 18 static unsigned int g_embedderDataSlot = 1u; 19 static v8::Isolate* g_isolate = nullptr; 20 static size_t g_isolate_ref_count = 0; 21 static FXJS_ArrayBufferAllocator* g_arrayBufferAllocator = nullptr; 22 static v8::Global<v8::ObjectTemplate>* g_DefaultGlobalObjectTemplate = nullptr; 23 static wchar_t kPerObjectDataTag[] = L"CFXJS_PerObjectData"; 24 25 class CFXJS_PerObjectData { 26 public: 27 explicit CFXJS_PerObjectData(int nObjDefID) 28 : m_ObjDefID(nObjDefID), m_pPrivate(nullptr) {} 29 30 static void SetInObject(CFXJS_PerObjectData* pData, 31 v8::Local<v8::Object> pObj) { 32 if (pObj->InternalFieldCount() == 2) { 33 pObj->SetAlignedPointerInInternalField(0, pData); 34 pObj->SetAlignedPointerInInternalField( 35 1, static_cast<void*>(kPerObjectDataTag)); 36 } 37 } 38 39 static CFXJS_PerObjectData* GetFromObject(v8::Local<v8::Object> pObj) { 40 if (pObj.IsEmpty() || pObj->InternalFieldCount() != 2 || 41 pObj->GetAlignedPointerFromInternalField(1) != 42 static_cast<void*>(kPerObjectDataTag)) { 43 return nullptr; 44 } 45 return static_cast<CFXJS_PerObjectData*>( 46 pObj->GetAlignedPointerFromInternalField(0)); 47 } 48 49 const int m_ObjDefID; 50 void* m_pPrivate; 51 }; 52 53 class CFXJS_ObjDefinition { 54 public: 55 static int MaxID(v8::Isolate* pIsolate) { 56 return FXJS_PerIsolateData::Get(pIsolate)->m_ObjectDefnArray.size(); 57 } 58 59 static CFXJS_ObjDefinition* ForID(v8::Isolate* pIsolate, int id) { 60 // Note: GetAt() halts if out-of-range even in release builds. 61 return FXJS_PerIsolateData::Get(pIsolate)->m_ObjectDefnArray[id].get(); 62 } 63 64 CFXJS_ObjDefinition(v8::Isolate* isolate, 65 const char* sObjName, 66 FXJSOBJTYPE eObjType, 67 CFXJS_Engine::Constructor pConstructor, 68 CFXJS_Engine::Destructor pDestructor) 69 : m_ObjName(sObjName), 70 m_ObjType(eObjType), 71 m_pConstructor(pConstructor), 72 m_pDestructor(pDestructor), 73 m_pIsolate(isolate) { 74 v8::Isolate::Scope isolate_scope(isolate); 75 v8::HandleScope handle_scope(isolate); 76 77 v8::Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(isolate); 78 fun->InstanceTemplate()->SetInternalFieldCount(2); 79 fun->SetCallHandler([](const v8::FunctionCallbackInfo<v8::Value>& info) { 80 v8::Local<v8::Object> holder = info.Holder(); 81 ASSERT(holder->InternalFieldCount() == 2); 82 holder->SetAlignedPointerInInternalField(0, nullptr); 83 holder->SetAlignedPointerInInternalField(1, nullptr); 84 }); 85 if (eObjType == FXJSOBJTYPE_GLOBAL) { 86 fun->InstanceTemplate()->Set( 87 v8::Symbol::GetToStringTag(isolate), 88 v8::String::NewFromUtf8(isolate, "global", v8::NewStringType::kNormal) 89 .ToLocalChecked()); 90 } 91 m_FunctionTemplate.Reset(isolate, fun); 92 93 v8::Local<v8::Signature> sig = v8::Signature::New(isolate, fun); 94 m_Signature.Reset(isolate, sig); 95 } 96 97 int AssignID() { 98 FXJS_PerIsolateData* pData = FXJS_PerIsolateData::Get(m_pIsolate); 99 pData->m_ObjectDefnArray.emplace_back(this); 100 return pData->m_ObjectDefnArray.size() - 1; 101 } 102 103 v8::Local<v8::ObjectTemplate> GetInstanceTemplate() { 104 v8::EscapableHandleScope scope(m_pIsolate); 105 v8::Local<v8::FunctionTemplate> function = 106 m_FunctionTemplate.Get(m_pIsolate); 107 return scope.Escape(function->InstanceTemplate()); 108 } 109 110 v8::Local<v8::Signature> GetSignature() { 111 v8::EscapableHandleScope scope(m_pIsolate); 112 return scope.Escape(m_Signature.Get(m_pIsolate)); 113 } 114 115 const char* const m_ObjName; 116 const FXJSOBJTYPE m_ObjType; 117 const CFXJS_Engine::Constructor m_pConstructor; 118 const CFXJS_Engine::Destructor m_pDestructor; 119 120 v8::Isolate* m_pIsolate; 121 v8::Global<v8::FunctionTemplate> m_FunctionTemplate; 122 v8::Global<v8::Signature> m_Signature; 123 }; 124 125 static v8::Local<v8::ObjectTemplate> GetGlobalObjectTemplate( 126 v8::Isolate* pIsolate) { 127 int maxID = CFXJS_ObjDefinition::MaxID(pIsolate); 128 for (int i = 0; i < maxID; ++i) { 129 CFXJS_ObjDefinition* pObjDef = CFXJS_ObjDefinition::ForID(pIsolate, i); 130 if (pObjDef->m_ObjType == FXJSOBJTYPE_GLOBAL) 131 return pObjDef->GetInstanceTemplate(); 132 } 133 if (!g_DefaultGlobalObjectTemplate) { 134 v8::Local<v8::ObjectTemplate> hGlobalTemplate = 135 v8::ObjectTemplate::New(pIsolate); 136 hGlobalTemplate->Set( 137 v8::Symbol::GetToStringTag(pIsolate), 138 v8::String::NewFromUtf8(pIsolate, "global", v8::NewStringType::kNormal) 139 .ToLocalChecked()); 140 g_DefaultGlobalObjectTemplate = 141 new v8::Global<v8::ObjectTemplate>(pIsolate, hGlobalTemplate); 142 } 143 return g_DefaultGlobalObjectTemplate->Get(pIsolate); 144 } 145 146 void* FXJS_ArrayBufferAllocator::Allocate(size_t length) { 147 return calloc(1, length); 148 } 149 150 void* FXJS_ArrayBufferAllocator::AllocateUninitialized(size_t length) { 151 return malloc(length); 152 } 153 154 void FXJS_ArrayBufferAllocator::Free(void* data, size_t length) { 155 free(data); 156 } 157 158 void V8TemplateMapTraits::Dispose(v8::Isolate* isolate, 159 v8::Global<v8::Object> value, 160 void* key) { 161 v8::Local<v8::Object> obj = value.Get(isolate); 162 if (obj.IsEmpty()) 163 return; 164 CFXJS_Engine* pEngine = CFXJS_Engine::CurrentEngineFromIsolate(isolate); 165 int id = pEngine->GetObjDefnID(obj); 166 if (id == -1) 167 return; 168 CFXJS_ObjDefinition* pObjDef = CFXJS_ObjDefinition::ForID(isolate, id); 169 if (!pObjDef) 170 return; 171 if (pObjDef->m_pDestructor) 172 pObjDef->m_pDestructor(pEngine, obj); 173 CFXJS_Engine::FreeObjectPrivate(obj); 174 } 175 176 V8TemplateMapTraits::MapType* V8TemplateMapTraits::MapFromWeakCallbackInfo( 177 const v8::WeakCallbackInfo<WeakCallbackDataType>& data) { 178 V8TemplateMap* pMap = 179 (FXJS_PerIsolateData::Get(data.GetIsolate()))->m_pDynamicObjsMap.get(); 180 return pMap ? &pMap->m_map : nullptr; 181 } 182 183 void FXJS_Initialize(unsigned int embedderDataSlot, v8::Isolate* pIsolate) { 184 if (g_isolate) { 185 ASSERT(g_embedderDataSlot == embedderDataSlot); 186 ASSERT(g_isolate == pIsolate); 187 return; 188 } 189 g_embedderDataSlot = embedderDataSlot; 190 g_isolate = pIsolate; 191 } 192 193 void FXJS_Release() { 194 ASSERT(!g_isolate || g_isolate_ref_count == 0); 195 delete g_DefaultGlobalObjectTemplate; 196 g_DefaultGlobalObjectTemplate = nullptr; 197 g_isolate = nullptr; 198 199 delete g_arrayBufferAllocator; 200 g_arrayBufferAllocator = nullptr; 201 } 202 203 bool FXJS_GetIsolate(v8::Isolate** pResultIsolate) { 204 if (g_isolate) { 205 *pResultIsolate = g_isolate; 206 return false; 207 } 208 // Provide backwards compatibility when no external isolate. 209 if (!g_arrayBufferAllocator) 210 g_arrayBufferAllocator = new FXJS_ArrayBufferAllocator(); 211 v8::Isolate::CreateParams params; 212 params.array_buffer_allocator = g_arrayBufferAllocator; 213 *pResultIsolate = v8::Isolate::New(params); 214 return true; 215 } 216 217 size_t FXJS_GlobalIsolateRefCount() { 218 return g_isolate_ref_count; 219 } 220 221 V8TemplateMap::V8TemplateMap(v8::Isolate* isolate) : m_map(isolate) {} 222 223 V8TemplateMap::~V8TemplateMap() {} 224 225 void V8TemplateMap::set(void* key, v8::Local<v8::Object> handle) { 226 ASSERT(!m_map.Contains(key)); 227 m_map.Set(key, handle); 228 } 229 230 FXJS_PerIsolateData::~FXJS_PerIsolateData() {} 231 232 // static 233 void FXJS_PerIsolateData::SetUp(v8::Isolate* pIsolate) { 234 if (!pIsolate->GetData(g_embedderDataSlot)) 235 pIsolate->SetData(g_embedderDataSlot, new FXJS_PerIsolateData(pIsolate)); 236 } 237 238 // static 239 FXJS_PerIsolateData* FXJS_PerIsolateData::Get(v8::Isolate* pIsolate) { 240 return static_cast<FXJS_PerIsolateData*>( 241 pIsolate->GetData(g_embedderDataSlot)); 242 } 243 244 FXJS_PerIsolateData::FXJS_PerIsolateData(v8::Isolate* pIsolate) 245 : m_pDynamicObjsMap(new V8TemplateMap(pIsolate)) {} 246 247 CFXJS_Engine::CFXJS_Engine() : m_isolate(nullptr) {} 248 249 CFXJS_Engine::CFXJS_Engine(v8::Isolate* pIsolate) : m_isolate(pIsolate) {} 250 251 CFXJS_Engine::~CFXJS_Engine() { 252 m_V8PersistentContext.Reset(); 253 } 254 255 // static 256 CFXJS_Engine* CFXJS_Engine::CurrentEngineFromIsolate(v8::Isolate* pIsolate) { 257 return static_cast<CFXJS_Engine*>( 258 pIsolate->GetCurrentContext()->GetAlignedPointerFromEmbedderData( 259 kPerContextDataIndex)); 260 } 261 262 // static 263 int CFXJS_Engine::GetObjDefnID(v8::Local<v8::Object> pObj) { 264 CFXJS_PerObjectData* pData = CFXJS_PerObjectData::GetFromObject(pObj); 265 return pData ? pData->m_ObjDefID : -1; 266 } 267 268 // static 269 void CFXJS_Engine::FreeObjectPrivate(void* pPerObjectData) { 270 delete static_cast<CFXJS_PerObjectData*>(pPerObjectData); 271 } 272 273 // static 274 void CFXJS_Engine::FreeObjectPrivate(v8::Local<v8::Object> pObj) { 275 CFXJS_PerObjectData* pData = CFXJS_PerObjectData::GetFromObject(pObj); 276 pObj->SetAlignedPointerInInternalField(0, nullptr); 277 pObj->SetAlignedPointerInInternalField(1, nullptr); 278 delete pData; 279 } 280 281 int CFXJS_Engine::DefineObj(const char* sObjName, 282 FXJSOBJTYPE eObjType, 283 CFXJS_Engine::Constructor pConstructor, 284 CFXJS_Engine::Destructor pDestructor) { 285 v8::Isolate::Scope isolate_scope(m_isolate); 286 v8::HandleScope handle_scope(m_isolate); 287 FXJS_PerIsolateData::SetUp(m_isolate); 288 CFXJS_ObjDefinition* pObjDef = new CFXJS_ObjDefinition( 289 m_isolate, sObjName, eObjType, pConstructor, pDestructor); 290 return pObjDef->AssignID(); 291 } 292 293 void CFXJS_Engine::DefineObjMethod(int nObjDefnID, 294 const char* sMethodName, 295 v8::FunctionCallback pMethodCall) { 296 v8::Isolate::Scope isolate_scope(m_isolate); 297 v8::HandleScope handle_scope(m_isolate); 298 CFXJS_ObjDefinition* pObjDef = 299 CFXJS_ObjDefinition::ForID(m_isolate, nObjDefnID); 300 v8::Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New( 301 m_isolate, pMethodCall, v8::Local<v8::Value>(), pObjDef->GetSignature()); 302 fun->RemovePrototype(); 303 pObjDef->GetInstanceTemplate()->Set( 304 v8::String::NewFromUtf8(m_isolate, sMethodName, 305 v8::NewStringType::kNormal) 306 .ToLocalChecked(), 307 fun, v8::ReadOnly); 308 } 309 310 void CFXJS_Engine::DefineObjProperty(int nObjDefnID, 311 const char* sPropName, 312 v8::AccessorGetterCallback pPropGet, 313 v8::AccessorSetterCallback pPropPut) { 314 v8::Isolate::Scope isolate_scope(m_isolate); 315 v8::HandleScope handle_scope(m_isolate); 316 CFXJS_ObjDefinition* pObjDef = 317 CFXJS_ObjDefinition::ForID(m_isolate, nObjDefnID); 318 pObjDef->GetInstanceTemplate()->SetAccessor( 319 v8::String::NewFromUtf8(m_isolate, sPropName, v8::NewStringType::kNormal) 320 .ToLocalChecked(), 321 pPropGet, pPropPut); 322 } 323 324 void CFXJS_Engine::DefineObjAllProperties( 325 int nObjDefnID, 326 v8::NamedPropertyQueryCallback pPropQurey, 327 v8::NamedPropertyGetterCallback pPropGet, 328 v8::NamedPropertySetterCallback pPropPut, 329 v8::NamedPropertyDeleterCallback pPropDel) { 330 v8::Isolate::Scope isolate_scope(m_isolate); 331 v8::HandleScope handle_scope(m_isolate); 332 CFXJS_ObjDefinition* pObjDef = 333 CFXJS_ObjDefinition::ForID(m_isolate, nObjDefnID); 334 pObjDef->GetInstanceTemplate()->SetNamedPropertyHandler(pPropGet, pPropPut, 335 pPropQurey, pPropDel); 336 } 337 338 void CFXJS_Engine::DefineObjConst(int nObjDefnID, 339 const char* sConstName, 340 v8::Local<v8::Value> pDefault) { 341 v8::Isolate::Scope isolate_scope(m_isolate); 342 v8::HandleScope handle_scope(m_isolate); 343 CFXJS_ObjDefinition* pObjDef = 344 CFXJS_ObjDefinition::ForID(m_isolate, nObjDefnID); 345 pObjDef->GetInstanceTemplate()->Set(m_isolate, sConstName, pDefault); 346 } 347 348 void CFXJS_Engine::DefineGlobalMethod(const char* sMethodName, 349 v8::FunctionCallback pMethodCall) { 350 v8::Isolate::Scope isolate_scope(m_isolate); 351 v8::HandleScope handle_scope(m_isolate); 352 v8::Local<v8::FunctionTemplate> fun = 353 v8::FunctionTemplate::New(m_isolate, pMethodCall); 354 fun->RemovePrototype(); 355 GetGlobalObjectTemplate(m_isolate)->Set( 356 v8::String::NewFromUtf8(m_isolate, sMethodName, 357 v8::NewStringType::kNormal) 358 .ToLocalChecked(), 359 fun, v8::ReadOnly); 360 } 361 362 void CFXJS_Engine::DefineGlobalConst(const wchar_t* sConstName, 363 v8::FunctionCallback pConstGetter) { 364 v8::Isolate::Scope isolate_scope(m_isolate); 365 v8::HandleScope handle_scope(m_isolate); 366 CFX_ByteString bsConst = FX_UTF8Encode(CFX_WideStringC(sConstName)); 367 v8::Local<v8::FunctionTemplate> fun = 368 v8::FunctionTemplate::New(m_isolate, pConstGetter); 369 fun->RemovePrototype(); 370 GetGlobalObjectTemplate(m_isolate)->SetAccessorProperty( 371 v8::String::NewFromUtf8(m_isolate, bsConst.c_str(), 372 v8::NewStringType::kNormal) 373 .ToLocalChecked(), 374 fun); 375 } 376 377 void CFXJS_Engine::InitializeEngine() { 378 if (m_isolate == g_isolate) 379 ++g_isolate_ref_count; 380 381 v8::Isolate::Scope isolate_scope(m_isolate); 382 v8::HandleScope handle_scope(m_isolate); 383 384 // This has to happen before we call GetGlobalObjectTemplate because that 385 // method gets the PerIsolateData from m_isolate. 386 FXJS_PerIsolateData::SetUp(m_isolate); 387 388 v8::Local<v8::Context> v8Context = 389 v8::Context::New(m_isolate, nullptr, GetGlobalObjectTemplate(m_isolate)); 390 v8::Context::Scope context_scope(v8Context); 391 392 v8Context->SetAlignedPointerInEmbedderData(kPerContextDataIndex, this); 393 394 int maxID = CFXJS_ObjDefinition::MaxID(m_isolate); 395 m_StaticObjects.resize(maxID + 1); 396 for (int i = 0; i < maxID; ++i) { 397 CFXJS_ObjDefinition* pObjDef = CFXJS_ObjDefinition::ForID(m_isolate, i); 398 if (pObjDef->m_ObjType == FXJSOBJTYPE_GLOBAL) { 399 CFXJS_PerObjectData::SetInObject(new CFXJS_PerObjectData(i), 400 v8Context->Global() 401 ->GetPrototype() 402 ->ToObject(v8Context) 403 .ToLocalChecked()); 404 if (pObjDef->m_pConstructor) { 405 pObjDef->m_pConstructor(this, v8Context->Global() 406 ->GetPrototype() 407 ->ToObject(v8Context) 408 .ToLocalChecked()); 409 } 410 } else if (pObjDef->m_ObjType == FXJSOBJTYPE_STATIC) { 411 v8::Local<v8::String> pObjName = 412 v8::String::NewFromUtf8(m_isolate, pObjDef->m_ObjName, 413 v8::NewStringType::kNormal, 414 strlen(pObjDef->m_ObjName)) 415 .ToLocalChecked(); 416 417 v8::Local<v8::Object> obj = NewFxDynamicObj(i, true); 418 v8Context->Global()->Set(v8Context, pObjName, obj).FromJust(); 419 m_StaticObjects[i] = new v8::Global<v8::Object>(m_isolate, obj); 420 } 421 } 422 m_V8PersistentContext.Reset(m_isolate, v8Context); 423 } 424 425 void CFXJS_Engine::ReleaseEngine() { 426 v8::Isolate::Scope isolate_scope(m_isolate); 427 v8::HandleScope handle_scope(m_isolate); 428 v8::Local<v8::Context> context = 429 v8::Local<v8::Context>::New(m_isolate, m_V8PersistentContext); 430 v8::Context::Scope context_scope(context); 431 432 FXJS_PerIsolateData* pData = FXJS_PerIsolateData::Get(m_isolate); 433 if (!pData) 434 return; 435 436 m_ConstArrays.clear(); 437 438 int maxID = CFXJS_ObjDefinition::MaxID(m_isolate); 439 for (int i = 0; i < maxID; ++i) { 440 CFXJS_ObjDefinition* pObjDef = CFXJS_ObjDefinition::ForID(m_isolate, i); 441 v8::Local<v8::Object> pObj; 442 if (pObjDef->m_ObjType == FXJSOBJTYPE_GLOBAL) { 443 pObj = 444 context->Global()->GetPrototype()->ToObject(context).ToLocalChecked(); 445 } else if (m_StaticObjects[i] && !m_StaticObjects[i]->IsEmpty()) { 446 pObj = v8::Local<v8::Object>::New(m_isolate, *m_StaticObjects[i]); 447 delete m_StaticObjects[i]; 448 m_StaticObjects[i] = nullptr; 449 } 450 451 if (!pObj.IsEmpty()) { 452 if (pObjDef->m_pDestructor) 453 pObjDef->m_pDestructor(this, pObj); 454 FreeObjectPrivate(pObj); 455 } 456 } 457 458 m_V8PersistentContext.Reset(); 459 460 if (m_isolate == g_isolate && --g_isolate_ref_count > 0) 461 return; 462 463 delete pData; 464 m_isolate->SetData(g_embedderDataSlot, nullptr); 465 } 466 467 int CFXJS_Engine::Execute(const CFX_WideString& script, FXJSErr* pError) { 468 v8::Isolate::Scope isolate_scope(m_isolate); 469 v8::TryCatch try_catch(m_isolate); 470 CFX_ByteString bsScript = script.UTF8Encode(); 471 v8::Local<v8::Context> context = m_isolate->GetCurrentContext(); 472 v8::Local<v8::Script> compiled_script; 473 if (!v8::Script::Compile(context, 474 v8::String::NewFromUtf8(m_isolate, bsScript.c_str(), 475 v8::NewStringType::kNormal, 476 bsScript.GetLength()) 477 .ToLocalChecked()) 478 .ToLocal(&compiled_script)) { 479 v8::String::Utf8Value error(try_catch.Exception()); 480 // TODO(tsepez): return error via pError->message. 481 return -1; 482 } 483 484 v8::Local<v8::Value> result; 485 if (!compiled_script->Run(context).ToLocal(&result)) { 486 v8::String::Utf8Value error(try_catch.Exception()); 487 // TODO(tsepez): return error via pError->message. 488 return -1; 489 } 490 return 0; 491 } 492 493 v8::Local<v8::Object> CFXJS_Engine::NewFxDynamicObj(int nObjDefnID, 494 bool bStatic) { 495 v8::Isolate::Scope isolate_scope(m_isolate); 496 v8::Local<v8::Context> context = m_isolate->GetCurrentContext(); 497 if (nObjDefnID == -1) { 498 v8::Local<v8::ObjectTemplate> objTempl = v8::ObjectTemplate::New(m_isolate); 499 v8::Local<v8::Object> obj; 500 if (!objTempl->NewInstance(context).ToLocal(&obj)) 501 return v8::Local<v8::Object>(); 502 return obj; 503 } 504 505 FXJS_PerIsolateData* pData = FXJS_PerIsolateData::Get(m_isolate); 506 if (!pData) 507 return v8::Local<v8::Object>(); 508 509 if (nObjDefnID < 0 || nObjDefnID >= CFXJS_ObjDefinition::MaxID(m_isolate)) 510 return v8::Local<v8::Object>(); 511 512 CFXJS_ObjDefinition* pObjDef = 513 CFXJS_ObjDefinition::ForID(m_isolate, nObjDefnID); 514 v8::Local<v8::Object> obj; 515 if (!pObjDef->GetInstanceTemplate()->NewInstance(context).ToLocal(&obj)) 516 return v8::Local<v8::Object>(); 517 518 CFXJS_PerObjectData* pObjData = new CFXJS_PerObjectData(nObjDefnID); 519 CFXJS_PerObjectData::SetInObject(pObjData, obj); 520 if (pObjDef->m_pConstructor) 521 pObjDef->m_pConstructor(this, obj); 522 523 if (!bStatic && FXJS_PerIsolateData::Get(m_isolate)->m_pDynamicObjsMap) 524 FXJS_PerIsolateData::Get(m_isolate)->m_pDynamicObjsMap->set(pObjData, obj); 525 526 return obj; 527 } 528 529 v8::Local<v8::Object> CFXJS_Engine::GetThisObj() { 530 v8::Isolate::Scope isolate_scope(m_isolate); 531 if (!FXJS_PerIsolateData::Get(m_isolate)) 532 return v8::Local<v8::Object>(); 533 534 // Return the global object. 535 v8::Local<v8::Context> context = m_isolate->GetCurrentContext(); 536 return context->Global()->GetPrototype()->ToObject(context).ToLocalChecked(); 537 } 538 539 void CFXJS_Engine::Error(const CFX_WideString& message) { 540 // Conversion from pdfium's wchar_t wide-strings to v8's uint16_t 541 // wide-strings isn't handled by v8, so use UTF8 as a common 542 // intermediate format. 543 CFX_ByteString utf8_message = message.UTF8Encode(); 544 m_isolate->ThrowException(v8::String::NewFromUtf8(m_isolate, 545 utf8_message.c_str(), 546 v8::NewStringType::kNormal) 547 .ToLocalChecked()); 548 } 549 550 void CFXJS_Engine::SetObjectPrivate(v8::Local<v8::Object> pObj, void* p) { 551 CFXJS_PerObjectData* pPerObjectData = 552 CFXJS_PerObjectData::GetFromObject(pObj); 553 if (!pPerObjectData) 554 return; 555 pPerObjectData->m_pPrivate = p; 556 } 557 558 void* CFXJS_Engine::GetObjectPrivate(v8::Local<v8::Object> pObj) { 559 CFXJS_PerObjectData* pData = CFXJS_PerObjectData::GetFromObject(pObj); 560 if (!pData && !pObj.IsEmpty()) { 561 // It could be a global proxy object. 562 v8::Local<v8::Value> v = pObj->GetPrototype(); 563 v8::Local<v8::Context> context = m_isolate->GetCurrentContext(); 564 if (v->IsObject()) { 565 pData = CFXJS_PerObjectData::GetFromObject( 566 v->ToObject(context).ToLocalChecked()); 567 } 568 } 569 return pData ? pData->m_pPrivate : nullptr; 570 } 571 572 v8::Local<v8::Value> CFXJS_Engine::GetObjectProperty( 573 v8::Local<v8::Object> pObj, 574 const CFX_WideString& wsPropertyName) { 575 if (pObj.IsEmpty()) 576 return v8::Local<v8::Value>(); 577 v8::Local<v8::Value> val; 578 if (!pObj->Get(m_isolate->GetCurrentContext(), 579 NewString(wsPropertyName.AsStringC())) 580 .ToLocal(&val)) 581 return v8::Local<v8::Value>(); 582 return val; 583 } 584 585 std::vector<CFX_WideString> CFXJS_Engine::GetObjectPropertyNames( 586 v8::Local<v8::Object> pObj) { 587 if (pObj.IsEmpty()) 588 return std::vector<CFX_WideString>(); 589 590 v8::Local<v8::Array> val; 591 v8::Local<v8::Context> context = m_isolate->GetCurrentContext(); 592 if (!pObj->GetPropertyNames(context).ToLocal(&val)) 593 return std::vector<CFX_WideString>(); 594 595 std::vector<CFX_WideString> result; 596 for (uint32_t i = 0; i < val->Length(); ++i) { 597 result.push_back(ToWideString(val->Get(context, i).ToLocalChecked())); 598 } 599 600 return result; 601 } 602 603 void CFXJS_Engine::PutObjectProperty(v8::Local<v8::Object> pObj, 604 const CFX_WideString& wsPropertyName, 605 v8::Local<v8::Value> pPut) { 606 if (pObj.IsEmpty()) 607 return; 608 pObj->Set(m_isolate->GetCurrentContext(), 609 NewString(wsPropertyName.AsStringC()), pPut) 610 .FromJust(); 611 } 612 613 614 v8::Local<v8::Array> CFXJS_Engine::NewArray() { 615 return v8::Array::New(m_isolate); 616 } 617 618 unsigned CFXJS_Engine::PutArrayElement(v8::Local<v8::Array> pArray, 619 unsigned index, 620 v8::Local<v8::Value> pValue) { 621 if (pArray.IsEmpty()) 622 return 0; 623 if (pArray->Set(m_isolate->GetCurrentContext(), index, pValue).IsNothing()) 624 return 0; 625 return 1; 626 } 627 628 v8::Local<v8::Value> CFXJS_Engine::GetArrayElement(v8::Local<v8::Array> pArray, 629 unsigned index) { 630 if (pArray.IsEmpty()) 631 return v8::Local<v8::Value>(); 632 v8::Local<v8::Value> val; 633 if (!pArray->Get(m_isolate->GetCurrentContext(), index).ToLocal(&val)) 634 return v8::Local<v8::Value>(); 635 return val; 636 } 637 638 unsigned CFXJS_Engine::GetArrayLength(v8::Local<v8::Array> pArray) { 639 if (pArray.IsEmpty()) 640 return 0; 641 return pArray->Length(); 642 } 643 644 v8::Local<v8::Context> CFXJS_Engine::NewLocalContext() { 645 return v8::Local<v8::Context>::New(m_isolate, m_V8PersistentContext); 646 } 647 648 v8::Local<v8::Context> CFXJS_Engine::GetPersistentContext() { 649 return m_V8PersistentContext.Get(m_isolate); 650 } 651 652 v8::Local<v8::Value> CFXJS_Engine::NewNumber(int number) { 653 return v8::Int32::New(m_isolate, number); 654 } 655 656 v8::Local<v8::Value> CFXJS_Engine::NewNumber(double number) { 657 return v8::Number::New(m_isolate, number); 658 } 659 660 v8::Local<v8::Value> CFXJS_Engine::NewNumber(float number) { 661 return v8::Number::New(m_isolate, (float)number); 662 } 663 664 v8::Local<v8::Value> CFXJS_Engine::NewBoolean(bool b) { 665 return v8::Boolean::New(m_isolate, b); 666 } 667 668 v8::Local<v8::Value> CFXJS_Engine::NewString(const CFX_ByteStringC& str) { 669 v8::Isolate* pIsolate = m_isolate ? m_isolate : v8::Isolate::GetCurrent(); 670 return v8::String::NewFromUtf8(pIsolate, str.c_str(), 671 v8::NewStringType::kNormal, str.GetLength()) 672 .ToLocalChecked(); 673 } 674 675 v8::Local<v8::Value> CFXJS_Engine::NewString(const CFX_WideStringC& str) { 676 return NewString(FX_UTF8Encode(str).AsStringC()); 677 } 678 679 v8::Local<v8::Value> CFXJS_Engine::NewNull() { 680 return v8::Local<v8::Value>(); 681 } 682 683 v8::Local<v8::Date> CFXJS_Engine::NewDate(double d) { 684 return v8::Date::New(m_isolate->GetCurrentContext(), d) 685 .ToLocalChecked() 686 .As<v8::Date>(); 687 } 688 689 int CFXJS_Engine::ToInt32(v8::Local<v8::Value> pValue) { 690 if (pValue.IsEmpty()) 691 return 0; 692 v8::Local<v8::Context> context = m_isolate->GetCurrentContext(); 693 return pValue->ToInt32(context).ToLocalChecked()->Value(); 694 } 695 696 bool CFXJS_Engine::ToBoolean(v8::Local<v8::Value> pValue) { 697 if (pValue.IsEmpty()) 698 return false; 699 v8::Local<v8::Context> context = m_isolate->GetCurrentContext(); 700 return pValue->ToBoolean(context).ToLocalChecked()->Value(); 701 } 702 703 double CFXJS_Engine::ToDouble(v8::Local<v8::Value> pValue) { 704 if (pValue.IsEmpty()) 705 return 0.0; 706 v8::Local<v8::Context> context = m_isolate->GetCurrentContext(); 707 return pValue->ToNumber(context).ToLocalChecked()->Value(); 708 } 709 710 CFX_WideString CFXJS_Engine::ToWideString(v8::Local<v8::Value> pValue) { 711 if (pValue.IsEmpty()) 712 return CFX_WideString(); 713 v8::Local<v8::Context> context = m_isolate->GetCurrentContext(); 714 v8::String::Utf8Value s(pValue->ToString(context).ToLocalChecked()); 715 return CFX_WideString::FromUTF8(CFX_ByteStringC(*s, s.length())); 716 } 717 718 v8::Local<v8::Object> CFXJS_Engine::ToObject(v8::Local<v8::Value> pValue) { 719 if (pValue.IsEmpty() || !pValue->IsObject()) 720 return v8::Local<v8::Object>(); 721 v8::Local<v8::Context> context = m_isolate->GetCurrentContext(); 722 return pValue->ToObject(context).ToLocalChecked(); 723 } 724 725 v8::Local<v8::Array> CFXJS_Engine::ToArray(v8::Local<v8::Value> pValue) { 726 if (pValue.IsEmpty() || !pValue->IsArray()) 727 return v8::Local<v8::Array>(); 728 v8::Local<v8::Context> context = m_isolate->GetCurrentContext(); 729 return v8::Local<v8::Array>::Cast(pValue->ToObject(context).ToLocalChecked()); 730 } 731 732 void CFXJS_Engine::SetConstArray(const CFX_WideString& name, 733 v8::Local<v8::Array> array) { 734 m_ConstArrays[name] = v8::Global<v8::Array>(GetIsolate(), array); 735 } 736 737 v8::Local<v8::Array> CFXJS_Engine::GetConstArray(const CFX_WideString& name) { 738 return v8::Local<v8::Array>::New(GetIsolate(), m_ConstArrays[name]); 739 } 740