1/* 2 * Copyright (C) 2011 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31#include "src/inspector/v8-runtime-agent-impl.h" 32 33#include <inttypes.h> 34 35#include "../../third_party/inspector_protocol/crdtp/json.h" 36#include "include/v8-container.h" 37#include "include/v8-context.h" 38#include "include/v8-function.h" 39#include "include/v8-inspector.h" 40#include "include/v8-microtask-queue.h" 41#include "src/debug/debug-interface.h" 42#include "src/inspector/injected-script.h" 43#include "src/inspector/inspected-context.h" 44#include "src/inspector/protocol/Protocol.h" 45#include "src/inspector/remote-object-id.h" 46#include "src/inspector/v8-console-message.h" 47#include "src/inspector/v8-debugger-agent-impl.h" 48#include "src/inspector/v8-debugger.h" 49#include "src/inspector/v8-inspector-impl.h" 50#include "src/inspector/v8-inspector-session-impl.h" 51#include "src/inspector/v8-stack-trace-impl.h" 52#include "src/inspector/v8-value-utils.h" 53#include "src/tracing/trace-event.h" 54 55namespace v8_inspector { 56 57namespace V8RuntimeAgentImplState { 58static const char customObjectFormatterEnabled[] = 59 "customObjectFormatterEnabled"; 60static const char maxCallStackSizeToCapture[] = "maxCallStackSizeToCapture"; 61static const char runtimeEnabled[] = "runtimeEnabled"; 62static const char bindings[] = "bindings"; 63static const char globalBindingsKey[] = ""; 64} // namespace V8RuntimeAgentImplState 65 66using protocol::Runtime::RemoteObject; 67 68namespace { 69 70template <typename ProtocolCallback> 71class EvaluateCallbackWrapper : public EvaluateCallback { 72 public: 73 static std::unique_ptr<EvaluateCallback> wrap( 74 std::unique_ptr<ProtocolCallback> callback) { 75 return std::unique_ptr<EvaluateCallback>( 76 new EvaluateCallbackWrapper(std::move(callback))); 77 } 78 void sendSuccess(std::unique_ptr<protocol::Runtime::RemoteObject> result, 79 protocol::Maybe<protocol::Runtime::ExceptionDetails> 80 exceptionDetails) override { 81 return m_callback->sendSuccess(std::move(result), 82 std::move(exceptionDetails)); 83 } 84 void sendFailure(const protocol::DispatchResponse& response) override { 85 return m_callback->sendFailure(response); 86 } 87 88 private: 89 explicit EvaluateCallbackWrapper(std::unique_ptr<ProtocolCallback> callback) 90 : m_callback(std::move(callback)) {} 91 92 std::unique_ptr<ProtocolCallback> m_callback; 93}; 94 95template <typename ProtocolCallback> 96bool wrapEvaluateResultAsync(InjectedScript* injectedScript, 97 v8::MaybeLocal<v8::Value> maybeResultValue, 98 const v8::TryCatch& tryCatch, 99 const String16& objectGroup, WrapMode wrapMode, 100 ProtocolCallback* callback) { 101 std::unique_ptr<RemoteObject> result; 102 Maybe<protocol::Runtime::ExceptionDetails> exceptionDetails; 103 104 Response response = injectedScript->wrapEvaluateResult( 105 maybeResultValue, tryCatch, objectGroup, wrapMode, &result, 106 &exceptionDetails); 107 if (response.IsSuccess()) { 108 callback->sendSuccess(std::move(result), std::move(exceptionDetails)); 109 return true; 110 } 111 callback->sendFailure(response); 112 return false; 113} 114 115void innerCallFunctionOn( 116 V8InspectorSessionImpl* session, InjectedScript::Scope& scope, 117 v8::Local<v8::Value> recv, const String16& expression, 118 Maybe<protocol::Array<protocol::Runtime::CallArgument>> optionalArguments, 119 bool silent, WrapMode wrapMode, bool userGesture, bool awaitPromise, 120 const String16& objectGroup, bool throw_on_side_effect, 121 std::unique_ptr<V8RuntimeAgentImpl::CallFunctionOnCallback> callback) { 122 V8InspectorImpl* inspector = session->inspector(); 123 124 std::unique_ptr<v8::Local<v8::Value>[]> argv = nullptr; 125 int argc = 0; 126 if (optionalArguments.isJust()) { 127 protocol::Array<protocol::Runtime::CallArgument>* arguments = 128 optionalArguments.fromJust(); 129 argc = static_cast<int>(arguments->size()); 130 argv.reset(new v8::Local<v8::Value>[argc]); 131 for (int i = 0; i < argc; ++i) { 132 v8::Local<v8::Value> argumentValue; 133 Response response = scope.injectedScript()->resolveCallArgument( 134 (*arguments)[i].get(), &argumentValue); 135 if (!response.IsSuccess()) { 136 callback->sendFailure(response); 137 return; 138 } 139 argv[i] = argumentValue; 140 } 141 } 142 143 if (silent) scope.ignoreExceptionsAndMuteConsole(); 144 if (userGesture) scope.pretendUserGesture(); 145 146 // Temporarily enable allow evals for inspector. 147 scope.allowCodeGenerationFromStrings(); 148 149 v8::MaybeLocal<v8::Value> maybeFunctionValue; 150 v8::Local<v8::Script> functionScript; 151 if (inspector 152 ->compileScript(scope.context(), "(" + expression + ")", String16()) 153 .ToLocal(&functionScript)) { 154 v8::MicrotasksScope microtasksScope(inspector->isolate(), 155 v8::MicrotasksScope::kRunMicrotasks); 156 maybeFunctionValue = functionScript->Run(scope.context()); 157 } 158 // Re-initialize after running client's code, as it could have destroyed 159 // context or session. 160 Response response = scope.initialize(); 161 if (!response.IsSuccess()) { 162 callback->sendFailure(response); 163 return; 164 } 165 166 if (scope.tryCatch().HasCaught()) { 167 wrapEvaluateResultAsync(scope.injectedScript(), maybeFunctionValue, 168 scope.tryCatch(), objectGroup, WrapMode::kNoPreview, 169 callback.get()); 170 return; 171 } 172 173 v8::Local<v8::Value> functionValue; 174 if (!maybeFunctionValue.ToLocal(&functionValue) || 175 !functionValue->IsFunction()) { 176 callback->sendFailure(Response::ServerError( 177 "Given expression does not evaluate to a function")); 178 return; 179 } 180 181 v8::MaybeLocal<v8::Value> maybeResultValue; 182 { 183 v8::MicrotasksScope microtasksScope(inspector->isolate(), 184 v8::MicrotasksScope::kRunMicrotasks); 185 maybeResultValue = v8::debug::CallFunctionOn( 186 scope.context(), functionValue.As<v8::Function>(), recv, argc, 187 argv.get(), throw_on_side_effect); 188 } 189 // Re-initialize after running client's code, as it could have destroyed 190 // context or session. 191 response = scope.initialize(); 192 if (!response.IsSuccess()) { 193 callback->sendFailure(response); 194 return; 195 } 196 197 if (!awaitPromise || scope.tryCatch().HasCaught()) { 198 wrapEvaluateResultAsync(scope.injectedScript(), maybeResultValue, 199 scope.tryCatch(), objectGroup, wrapMode, 200 callback.get()); 201 return; 202 } 203 204 scope.injectedScript()->addPromiseCallback( 205 session, maybeResultValue, objectGroup, wrapMode, false /* replMode */, 206 EvaluateCallbackWrapper<V8RuntimeAgentImpl::CallFunctionOnCallback>::wrap( 207 std::move(callback))); 208} 209 210Response ensureContext(V8InspectorImpl* inspector, int contextGroupId, 211 Maybe<int> executionContextId, 212 Maybe<String16> uniqueContextId, int* contextId) { 213 if (executionContextId.isJust()) { 214 if (uniqueContextId.isJust()) { 215 return Response::InvalidParams( 216 "contextId and uniqueContextId are mutually exclusive"); 217 } 218 *contextId = executionContextId.fromJust(); 219 } else if (uniqueContextId.isJust()) { 220 internal::V8DebuggerId uniqueId(uniqueContextId.fromJust()); 221 if (!uniqueId.isValid()) 222 return Response::InvalidParams("invalid uniqueContextId"); 223 int id = inspector->resolveUniqueContextId(uniqueId); 224 if (!id) return Response::InvalidParams("uniqueContextId not found"); 225 *contextId = id; 226 } else { 227 v8::HandleScope handles(inspector->isolate()); 228 v8::Local<v8::Context> defaultContext = 229 inspector->client()->ensureDefaultContextInGroup(contextGroupId); 230 if (defaultContext.IsEmpty()) 231 return Response::ServerError("Cannot find default execution context"); 232 *contextId = InspectedContext::contextId(defaultContext); 233 } 234 235 return Response::Success(); 236} 237 238} // namespace 239 240V8RuntimeAgentImpl::V8RuntimeAgentImpl( 241 V8InspectorSessionImpl* session, protocol::FrontendChannel* FrontendChannel, 242 protocol::DictionaryValue* state) 243 : m_session(session), 244 m_state(state), 245 m_frontend(FrontendChannel), 246 m_inspector(session->inspector()), 247 m_enabled(false) {} 248 249V8RuntimeAgentImpl::~V8RuntimeAgentImpl() = default; 250 251void V8RuntimeAgentImpl::evaluate( 252 const String16& expression, Maybe<String16> objectGroup, 253 Maybe<bool> includeCommandLineAPI, Maybe<bool> silent, 254 Maybe<int> executionContextId, Maybe<bool> returnByValue, 255 Maybe<bool> generatePreview, Maybe<bool> userGesture, 256 Maybe<bool> maybeAwaitPromise, Maybe<bool> throwOnSideEffect, 257 Maybe<double> timeout, Maybe<bool> disableBreaks, Maybe<bool> maybeReplMode, 258 Maybe<bool> allowUnsafeEvalBlockedByCSP, Maybe<String16> uniqueContextId, 259 Maybe<bool> generateWebDriverValue, 260 std::unique_ptr<EvaluateCallback> callback) { 261 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), 262 "EvaluateScript"); 263 int contextId = 0; 264 Response response = ensureContext(m_inspector, m_session->contextGroupId(), 265 std::move(executionContextId), 266 std::move(uniqueContextId), &contextId); 267 if (!response.IsSuccess()) { 268 callback->sendFailure(response); 269 return; 270 } 271 272 InjectedScript::ContextScope scope(m_session, contextId); 273 response = scope.initialize(); 274 if (!response.IsSuccess()) { 275 callback->sendFailure(response); 276 return; 277 } 278 279 if (silent.fromMaybe(false)) scope.ignoreExceptionsAndMuteConsole(); 280 if (userGesture.fromMaybe(false)) scope.pretendUserGesture(); 281 282 if (includeCommandLineAPI.fromMaybe(false)) scope.installCommandLineAPI(); 283 284 const bool replMode = maybeReplMode.fromMaybe(false); 285 286 if (allowUnsafeEvalBlockedByCSP.fromMaybe(true)) { 287 // Temporarily enable allow evals for inspector. 288 scope.allowCodeGenerationFromStrings(); 289 } 290 v8::MaybeLocal<v8::Value> maybeResultValue; 291 { 292 V8InspectorImpl::EvaluateScope evaluateScope(scope); 293 if (timeout.isJust()) { 294 response = evaluateScope.setTimeout(timeout.fromJust() / 1000.0); 295 if (!response.IsSuccess()) { 296 callback->sendFailure(response); 297 return; 298 } 299 } 300 v8::MicrotasksScope microtasksScope(m_inspector->isolate(), 301 v8::MicrotasksScope::kRunMicrotasks); 302 v8::debug::EvaluateGlobalMode mode = 303 v8::debug::EvaluateGlobalMode::kDefault; 304 if (throwOnSideEffect.fromMaybe(false)) { 305 mode = v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect; 306 } else if (disableBreaks.fromMaybe(false)) { 307 mode = v8::debug::EvaluateGlobalMode::kDisableBreaks; 308 } 309 const v8::Local<v8::String> source = 310 toV8String(m_inspector->isolate(), expression); 311 maybeResultValue = v8::debug::EvaluateGlobal(m_inspector->isolate(), source, 312 mode, replMode); 313 } // Run microtasks before returning result. 314 315 // Re-initialize after running client's code, as it could have destroyed 316 // context or session. 317 response = scope.initialize(); 318 if (!response.IsSuccess()) { 319 callback->sendFailure(response); 320 return; 321 } 322 323 WrapMode wrap_mode = generatePreview.fromMaybe(false) ? WrapMode::kWithPreview 324 : WrapMode::kNoPreview; 325 if (returnByValue.fromMaybe(false)) wrap_mode = WrapMode::kForceValue; 326 if (generateWebDriverValue.fromMaybe(false)) 327 wrap_mode = WrapMode::kGenerateWebDriverValue; 328 329 // REPL mode always returns a promise that must be awaited. 330 const bool await = replMode || maybeAwaitPromise.fromMaybe(false); 331 if (!await || scope.tryCatch().HasCaught()) { 332 wrapEvaluateResultAsync(scope.injectedScript(), maybeResultValue, 333 scope.tryCatch(), objectGroup.fromMaybe(""), 334 wrap_mode, callback.get()); 335 return; 336 } 337 scope.injectedScript()->addPromiseCallback( 338 m_session, maybeResultValue, objectGroup.fromMaybe(""), wrap_mode, 339 replMode, 340 EvaluateCallbackWrapper<EvaluateCallback>::wrap(std::move(callback))); 341} 342 343void V8RuntimeAgentImpl::awaitPromise( 344 const String16& promiseObjectId, Maybe<bool> returnByValue, 345 Maybe<bool> generatePreview, 346 std::unique_ptr<AwaitPromiseCallback> callback) { 347 InjectedScript::ObjectScope scope(m_session, promiseObjectId); 348 Response response = scope.initialize(); 349 if (!response.IsSuccess()) { 350 callback->sendFailure(response); 351 return; 352 } 353 if (!scope.object()->IsPromise()) { 354 callback->sendFailure( 355 Response::ServerError("Could not find promise with given id")); 356 return; 357 } 358 WrapMode mode = generatePreview.fromMaybe(false) ? WrapMode::kWithPreview 359 : WrapMode::kNoPreview; 360 if (returnByValue.fromMaybe(false)) mode = WrapMode::kForceValue; 361 scope.injectedScript()->addPromiseCallback( 362 m_session, scope.object(), scope.objectGroupName(), mode, 363 false /* replMode */, 364 EvaluateCallbackWrapper<AwaitPromiseCallback>::wrap(std::move(callback))); 365} 366 367void V8RuntimeAgentImpl::callFunctionOn( 368 const String16& expression, Maybe<String16> objectId, 369 Maybe<protocol::Array<protocol::Runtime::CallArgument>> optionalArguments, 370 Maybe<bool> silent, Maybe<bool> returnByValue, Maybe<bool> generatePreview, 371 Maybe<bool> userGesture, Maybe<bool> awaitPromise, 372 Maybe<int> executionContextId, Maybe<String16> objectGroup, 373 Maybe<bool> throwOnSideEffect, Maybe<bool> generateWebDriverValue, 374 std::unique_ptr<CallFunctionOnCallback> callback) { 375 if (objectId.isJust() && executionContextId.isJust()) { 376 callback->sendFailure(Response::ServerError( 377 "ObjectId must not be specified together with executionContextId")); 378 return; 379 } 380 if (!objectId.isJust() && !executionContextId.isJust()) { 381 callback->sendFailure(Response::ServerError( 382 "Either ObjectId or executionContextId must be specified")); 383 return; 384 } 385 WrapMode wrap_mode = generatePreview.fromMaybe(false) ? WrapMode::kWithPreview 386 : WrapMode::kNoPreview; 387 if (returnByValue.fromMaybe(false)) wrap_mode = WrapMode::kForceValue; 388 if (generateWebDriverValue.fromMaybe(false)) 389 wrap_mode = WrapMode::kGenerateWebDriverValue; 390 if (objectId.isJust()) { 391 InjectedScript::ObjectScope scope(m_session, objectId.fromJust()); 392 Response response = scope.initialize(); 393 if (!response.IsSuccess()) { 394 callback->sendFailure(response); 395 return; 396 } 397 innerCallFunctionOn( 398 m_session, scope, scope.object(), expression, 399 std::move(optionalArguments), silent.fromMaybe(false), wrap_mode, 400 userGesture.fromMaybe(false), awaitPromise.fromMaybe(false), 401 objectGroup.isJust() ? objectGroup.fromMaybe(String16()) 402 : scope.objectGroupName(), 403 throwOnSideEffect.fromMaybe(false), std::move(callback)); 404 } else { 405 int contextId = 0; 406 Response response = ensureContext(m_inspector, m_session->contextGroupId(), 407 std::move(executionContextId.fromJust()), 408 /* uniqueContextId */ {}, &contextId); 409 if (!response.IsSuccess()) { 410 callback->sendFailure(response); 411 return; 412 } 413 InjectedScript::ContextScope scope(m_session, contextId); 414 response = scope.initialize(); 415 if (!response.IsSuccess()) { 416 callback->sendFailure(response); 417 return; 418 } 419 innerCallFunctionOn( 420 m_session, scope, scope.context()->Global(), expression, 421 std::move(optionalArguments), silent.fromMaybe(false), wrap_mode, 422 userGesture.fromMaybe(false), awaitPromise.fromMaybe(false), 423 objectGroup.fromMaybe(""), throwOnSideEffect.fromMaybe(false), 424 std::move(callback)); 425 } 426} 427 428Response V8RuntimeAgentImpl::getProperties( 429 const String16& objectId, Maybe<bool> ownProperties, 430 Maybe<bool> accessorPropertiesOnly, Maybe<bool> generatePreview, 431 Maybe<bool> nonIndexedPropertiesOnly, 432 std::unique_ptr<protocol::Array<protocol::Runtime::PropertyDescriptor>>* 433 result, 434 Maybe<protocol::Array<protocol::Runtime::InternalPropertyDescriptor>>* 435 internalProperties, 436 Maybe<protocol::Array<protocol::Runtime::PrivatePropertyDescriptor>>* 437 privateProperties, 438 Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) { 439 using protocol::Runtime::InternalPropertyDescriptor; 440 using protocol::Runtime::PrivatePropertyDescriptor; 441 442 InjectedScript::ObjectScope scope(m_session, objectId); 443 Response response = scope.initialize(); 444 if (!response.IsSuccess()) return response; 445 446 scope.ignoreExceptionsAndMuteConsole(); 447 v8::MicrotasksScope microtasks_scope(m_inspector->isolate(), 448 v8::MicrotasksScope::kRunMicrotasks); 449 if (!scope.object()->IsObject()) 450 return Response::ServerError("Value with given id is not an object"); 451 452 v8::Local<v8::Object> object = scope.object().As<v8::Object>(); 453 response = scope.injectedScript()->getProperties( 454 object, scope.objectGroupName(), ownProperties.fromMaybe(false), 455 accessorPropertiesOnly.fromMaybe(false), 456 nonIndexedPropertiesOnly.fromMaybe(false), 457 generatePreview.fromMaybe(false) ? WrapMode::kWithPreview 458 : WrapMode::kNoPreview, 459 result, exceptionDetails); 460 if (!response.IsSuccess()) return response; 461 if (exceptionDetails->isJust()) return Response::Success(); 462 std::unique_ptr<protocol::Array<InternalPropertyDescriptor>> 463 internalPropertiesProtocolArray; 464 std::unique_ptr<protocol::Array<PrivatePropertyDescriptor>> 465 privatePropertiesProtocolArray; 466 response = scope.injectedScript()->getInternalAndPrivateProperties( 467 object, scope.objectGroupName(), accessorPropertiesOnly.fromMaybe(false), 468 &internalPropertiesProtocolArray, &privatePropertiesProtocolArray); 469 if (!response.IsSuccess()) return response; 470 if (!internalPropertiesProtocolArray->empty()) 471 *internalProperties = std::move(internalPropertiesProtocolArray); 472 if (!privatePropertiesProtocolArray->empty()) 473 *privateProperties = std::move(privatePropertiesProtocolArray); 474 return Response::Success(); 475} 476 477Response V8RuntimeAgentImpl::releaseObject(const String16& objectId) { 478 InjectedScript::ObjectScope scope(m_session, objectId); 479 Response response = scope.initialize(); 480 if (!response.IsSuccess()) return response; 481 scope.injectedScript()->releaseObject(objectId); 482 return Response::Success(); 483} 484 485Response V8RuntimeAgentImpl::releaseObjectGroup(const String16& objectGroup) { 486 m_session->releaseObjectGroup(objectGroup); 487 return Response::Success(); 488} 489 490Response V8RuntimeAgentImpl::runIfWaitingForDebugger() { 491 m_inspector->client()->runIfWaitingForDebugger(m_session->contextGroupId()); 492 return Response::Success(); 493} 494 495Response V8RuntimeAgentImpl::setCustomObjectFormatterEnabled(bool enabled) { 496 m_state->setBoolean(V8RuntimeAgentImplState::customObjectFormatterEnabled, 497 enabled); 498 if (!m_enabled) return Response::ServerError("Runtime agent is not enabled"); 499 m_session->setCustomObjectFormatterEnabled(enabled); 500 return Response::Success(); 501} 502 503Response V8RuntimeAgentImpl::setMaxCallStackSizeToCapture(int size) { 504 if (size < 0) { 505 return Response::ServerError( 506 "maxCallStackSizeToCapture should be non-negative"); 507 } 508 TRACE_EVENT_WITH_FLOW1( 509 TRACE_DISABLED_BY_DEFAULT("v8.inspector"), 510 "V8RuntimeAgentImpl::setMaxCallStackSizeToCapture", this, 511 TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "size", size); 512 if (!m_enabled) return Response::ServerError("Runtime agent is not enabled"); 513 m_state->setInteger(V8RuntimeAgentImplState::maxCallStackSizeToCapture, size); 514 m_inspector->debugger()->setMaxCallStackSizeToCapture(this, size); 515 return Response::Success(); 516} 517 518Response V8RuntimeAgentImpl::discardConsoleEntries() { 519 V8ConsoleMessageStorage* storage = 520 m_inspector->ensureConsoleMessageStorage(m_session->contextGroupId()); 521 storage->clear(); 522 return Response::Success(); 523} 524 525Response V8RuntimeAgentImpl::compileScript( 526 const String16& expression, const String16& sourceURL, bool persistScript, 527 Maybe<int> executionContextId, Maybe<String16>* scriptId, 528 Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) { 529 if (!m_enabled) return Response::ServerError("Runtime agent is not enabled"); 530 531 int contextId = 0; 532 Response response = ensureContext(m_inspector, m_session->contextGroupId(), 533 std::move(executionContextId), 534 /*uniqueContextId*/ {}, &contextId); 535 if (!response.IsSuccess()) return response; 536 InjectedScript::ContextScope scope(m_session, contextId); 537 response = scope.initialize(); 538 if (!response.IsSuccess()) return response; 539 540 if (!persistScript) m_inspector->debugger()->muteScriptParsedEvents(); 541 v8::Local<v8::Script> script; 542 bool isOk = m_inspector->compileScript(scope.context(), expression, sourceURL) 543 .ToLocal(&script); 544 if (!persistScript) m_inspector->debugger()->unmuteScriptParsedEvents(); 545 if (!isOk) { 546 if (scope.tryCatch().HasCaught()) { 547 response = scope.injectedScript()->createExceptionDetails( 548 scope.tryCatch(), String16(), exceptionDetails); 549 if (!response.IsSuccess()) return response; 550 return Response::Success(); 551 } else { 552 return Response::ServerError("Script compilation failed"); 553 } 554 } 555 556 if (!persistScript) return Response::Success(); 557 558 String16 scriptValueId = 559 String16::fromInteger(script->GetUnboundScript()->GetId()); 560 std::unique_ptr<v8::Global<v8::Script>> global( 561 new v8::Global<v8::Script>(m_inspector->isolate(), script)); 562 m_compiledScripts[scriptValueId] = std::move(global); 563 *scriptId = scriptValueId; 564 return Response::Success(); 565} 566 567void V8RuntimeAgentImpl::runScript( 568 const String16& scriptId, Maybe<int> executionContextId, 569 Maybe<String16> objectGroup, Maybe<bool> silent, 570 Maybe<bool> includeCommandLineAPI, Maybe<bool> returnByValue, 571 Maybe<bool> generatePreview, Maybe<bool> awaitPromise, 572 std::unique_ptr<RunScriptCallback> callback) { 573 if (!m_enabled) { 574 callback->sendFailure( 575 Response::ServerError("Runtime agent is not enabled")); 576 return; 577 } 578 579 auto it = m_compiledScripts.find(scriptId); 580 if (it == m_compiledScripts.end()) { 581 callback->sendFailure(Response::ServerError("No script with given id")); 582 return; 583 } 584 585 int contextId = 0; 586 Response response = ensureContext(m_inspector, m_session->contextGroupId(), 587 std::move(executionContextId), 588 /*uniqueContextId*/ {}, &contextId); 589 if (!response.IsSuccess()) { 590 callback->sendFailure(response); 591 return; 592 } 593 594 InjectedScript::ContextScope scope(m_session, contextId); 595 response = scope.initialize(); 596 if (!response.IsSuccess()) { 597 callback->sendFailure(response); 598 return; 599 } 600 601 if (silent.fromMaybe(false)) scope.ignoreExceptionsAndMuteConsole(); 602 603 std::unique_ptr<v8::Global<v8::Script>> scriptWrapper = std::move(it->second); 604 m_compiledScripts.erase(it); 605 v8::Local<v8::Script> script = scriptWrapper->Get(m_inspector->isolate()); 606 if (script.IsEmpty()) { 607 callback->sendFailure(Response::ServerError("Script execution failed")); 608 return; 609 } 610 611 if (includeCommandLineAPI.fromMaybe(false)) scope.installCommandLineAPI(); 612 613 v8::MaybeLocal<v8::Value> maybeResultValue; 614 { 615 v8::MicrotasksScope microtasksScope(m_inspector->isolate(), 616 v8::MicrotasksScope::kRunMicrotasks); 617 maybeResultValue = script->Run(scope.context()); 618 } 619 620 // Re-initialize after running client's code, as it could have destroyed 621 // context or session. 622 response = scope.initialize(); 623 if (!response.IsSuccess()) { 624 callback->sendFailure(response); 625 return; 626 } 627 628 WrapMode mode = generatePreview.fromMaybe(false) ? WrapMode::kWithPreview 629 : WrapMode::kNoPreview; 630 if (returnByValue.fromMaybe(false)) mode = WrapMode::kForceValue; 631 if (!awaitPromise.fromMaybe(false) || scope.tryCatch().HasCaught()) { 632 wrapEvaluateResultAsync(scope.injectedScript(), maybeResultValue, 633 scope.tryCatch(), objectGroup.fromMaybe(""), mode, 634 callback.get()); 635 return; 636 } 637 scope.injectedScript()->addPromiseCallback( 638 m_session, maybeResultValue.ToLocalChecked(), objectGroup.fromMaybe(""), 639 mode, false /* replMode */, 640 EvaluateCallbackWrapper<RunScriptCallback>::wrap(std::move(callback))); 641} 642 643Response V8RuntimeAgentImpl::queryObjects( 644 const String16& prototypeObjectId, Maybe<String16> objectGroup, 645 std::unique_ptr<protocol::Runtime::RemoteObject>* objects) { 646 InjectedScript::ObjectScope scope(m_session, prototypeObjectId); 647 Response response = scope.initialize(); 648 if (!response.IsSuccess()) return response; 649 if (!scope.object()->IsObject()) { 650 return Response::ServerError("Prototype should be instance of Object"); 651 } 652 v8::Local<v8::Array> resultArray = m_inspector->debugger()->queryObjects( 653 scope.context(), scope.object().As<v8::Object>()); 654 return scope.injectedScript()->wrapObject( 655 resultArray, objectGroup.fromMaybe(scope.objectGroupName()), 656 WrapMode::kNoPreview, objects); 657} 658 659Response V8RuntimeAgentImpl::globalLexicalScopeNames( 660 Maybe<int> executionContextId, 661 std::unique_ptr<protocol::Array<String16>>* outNames) { 662 int contextId = 0; 663 Response response = ensureContext(m_inspector, m_session->contextGroupId(), 664 std::move(executionContextId), 665 /*uniqueContextId*/ {}, &contextId); 666 if (!response.IsSuccess()) return response; 667 668 InjectedScript::ContextScope scope(m_session, contextId); 669 response = scope.initialize(); 670 if (!response.IsSuccess()) return response; 671 672 v8::PersistentValueVector<v8::String> names(m_inspector->isolate()); 673 v8::debug::GlobalLexicalScopeNames(scope.context(), &names); 674 *outNames = std::make_unique<protocol::Array<String16>>(); 675 for (size_t i = 0; i < names.Size(); ++i) { 676 (*outNames)->emplace_back( 677 toProtocolString(m_inspector->isolate(), names.Get(i))); 678 } 679 return Response::Success(); 680} 681 682Response V8RuntimeAgentImpl::getIsolateId(String16* outIsolateId) { 683 char buf[40]; 684 std::snprintf(buf, sizeof(buf), "%" PRIx64, m_inspector->isolateId()); 685 *outIsolateId = buf; 686 return Response::Success(); 687} 688 689Response V8RuntimeAgentImpl::getHeapUsage(double* out_usedSize, 690 double* out_totalSize) { 691 v8::HeapStatistics stats; 692 m_inspector->isolate()->GetHeapStatistics(&stats); 693 *out_usedSize = stats.used_heap_size(); 694 *out_totalSize = stats.total_heap_size(); 695 return Response::Success(); 696} 697 698void V8RuntimeAgentImpl::terminateExecution( 699 std::unique_ptr<TerminateExecutionCallback> callback) { 700 m_inspector->debugger()->terminateExecution(std::move(callback)); 701} 702 703namespace { 704protocol::DictionaryValue* getOrCreateDictionary( 705 protocol::DictionaryValue* dict, const String16& key) { 706 if (protocol::DictionaryValue* bindings = dict->getObject(key)) 707 return bindings; 708 dict->setObject(key, protocol::DictionaryValue::create()); 709 return dict->getObject(key); 710} 711} // namespace 712 713Response V8RuntimeAgentImpl::addBinding(const String16& name, 714 Maybe<int> executionContextId, 715 Maybe<String16> executionContextName) { 716 if (executionContextId.isJust()) { 717 if (executionContextName.isJust()) { 718 return Response::InvalidParams( 719 "executionContextName is mutually exclusive with executionContextId"); 720 } 721 int contextId = executionContextId.fromJust(); 722 InspectedContext* context = 723 m_inspector->getContext(m_session->contextGroupId(), contextId); 724 if (!context) { 725 return Response::InvalidParams( 726 "Cannot find execution context with given executionContextId"); 727 } 728 addBinding(context, name); 729 return Response::Success(); 730 } 731 732 // If it's a globally exposed binding, i.e. no context name specified, use 733 // a special value for the context name. 734 String16 contextKey = V8RuntimeAgentImplState::globalBindingsKey; 735 if (executionContextName.isJust()) { 736 contextKey = executionContextName.fromJust(); 737 if (contextKey == V8RuntimeAgentImplState::globalBindingsKey) { 738 return Response::InvalidParams("Invalid executionContextName"); 739 } 740 } 741 // Only persist non context-specific bindings, as contextIds don't make 742 // any sense when state is restored in a different process. 743 protocol::DictionaryValue* bindings = 744 getOrCreateDictionary(m_state, V8RuntimeAgentImplState::bindings); 745 protocol::DictionaryValue* contextBindings = 746 getOrCreateDictionary(bindings, contextKey); 747 contextBindings->setBoolean(name, true); 748 749 m_inspector->forEachContext( 750 m_session->contextGroupId(), 751 [&name, &executionContextName, this](InspectedContext* context) { 752 if (executionContextName.isJust() && 753 executionContextName.fromJust() != context->humanReadableName()) 754 return; 755 addBinding(context, name); 756 }); 757 return Response::Success(); 758} 759 760void V8RuntimeAgentImpl::bindingCallback( 761 const v8::FunctionCallbackInfo<v8::Value>& info) { 762 v8::Isolate* isolate = info.GetIsolate(); 763 if (info.Length() != 1 || !info[0]->IsString()) { 764 info.GetIsolate()->ThrowError( 765 "Invalid arguments: should be exactly one string."); 766 return; 767 } 768 V8InspectorImpl* inspector = 769 static_cast<V8InspectorImpl*>(v8::debug::GetInspector(isolate)); 770 int contextId = InspectedContext::contextId(isolate->GetCurrentContext()); 771 int contextGroupId = inspector->contextGroupId(contextId); 772 773 String16 name = toProtocolString(isolate, info.Data().As<v8::String>()); 774 String16 payload = toProtocolString(isolate, info[0].As<v8::String>()); 775 776 inspector->forEachSession( 777 contextGroupId, 778 [&name, &payload, &contextId](V8InspectorSessionImpl* session) { 779 session->runtimeAgent()->bindingCalled(name, payload, contextId); 780 }); 781} 782 783void V8RuntimeAgentImpl::addBinding(InspectedContext* context, 784 const String16& name) { 785 auto it = m_activeBindings.find(name); 786 if (it != m_activeBindings.end() && it->second.count(context->contextId())) { 787 return; 788 } 789 v8::HandleScope handles(m_inspector->isolate()); 790 v8::Local<v8::Context> localContext = context->context(); 791 v8::Local<v8::Object> global = localContext->Global(); 792 v8::Local<v8::String> v8Name = toV8String(m_inspector->isolate(), name); 793 v8::Local<v8::Value> functionValue; 794 v8::MicrotasksScope microtasks(m_inspector->isolate(), 795 v8::MicrotasksScope::kDoNotRunMicrotasks); 796 if (v8::Function::New(localContext, bindingCallback, v8Name) 797 .ToLocal(&functionValue)) { 798 v8::Maybe<bool> success = global->Set(localContext, v8Name, functionValue); 799 USE(success); 800 if (it == m_activeBindings.end()) { 801 m_activeBindings.emplace(name, 802 std::unordered_set<int>(context->contextId())); 803 } else { 804 m_activeBindings.at(name).insert(context->contextId()); 805 } 806 } 807} 808 809Response V8RuntimeAgentImpl::removeBinding(const String16& name) { 810 protocol::DictionaryValue* bindings = 811 m_state->getObject(V8RuntimeAgentImplState::bindings); 812 if (bindings) bindings->remove(name); 813 m_activeBindings.erase(name); 814 return Response::Success(); 815} 816 817Response V8RuntimeAgentImpl::getExceptionDetails( 818 const String16& errorObjectId, 819 Maybe<protocol::Runtime::ExceptionDetails>* out_exceptionDetails) { 820 InjectedScript::ObjectScope scope(m_session, errorObjectId); 821 Response response = scope.initialize(); 822 if (!response.IsSuccess()) return response; 823 824 const v8::Local<v8::Value> error = scope.object(); 825 if (!error->IsNativeError()) 826 return Response::ServerError("errorObjectId is not a JS error object"); 827 828 const v8::Local<v8::Message> message = 829 v8::debug::CreateMessageFromException(m_inspector->isolate(), error); 830 831 response = scope.injectedScript()->createExceptionDetails( 832 message, error, scope.objectGroupName(), out_exceptionDetails); 833 if (!response.IsSuccess()) return response; 834 835 CHECK(out_exceptionDetails->isJust()); 836 837 // When an exception object is present, `createExceptionDetails` assumes 838 // the exception is uncaught and will overwrite the text field to "Uncaught". 839 // Lets use the normal message text instead. 840 out_exceptionDetails->fromJust()->setText( 841 toProtocolString(m_inspector->isolate(), message->Get())); 842 843 // Check if the exception has any metadata on the inspector and also attach 844 // it. 845 std::unique_ptr<protocol::DictionaryValue> data = 846 m_inspector->getAssociatedExceptionDataForProtocol(error); 847 if (data) 848 out_exceptionDetails->fromJust()->setExceptionMetaData(std::move(data)); 849 return Response::Success(); 850} 851 852void V8RuntimeAgentImpl::bindingCalled(const String16& name, 853 const String16& payload, 854 int executionContextId) { 855 if (!m_activeBindings.count(name)) return; 856 m_frontend.bindingCalled(name, payload, executionContextId); 857 m_frontend.flush(); 858} 859 860void V8RuntimeAgentImpl::addBindings(InspectedContext* context) { 861 const String16 contextName = context->humanReadableName(); 862 if (!m_enabled) return; 863 protocol::DictionaryValue* bindings = 864 m_state->getObject(V8RuntimeAgentImplState::bindings); 865 if (!bindings) return; 866 protocol::DictionaryValue* globalBindings = 867 bindings->getObject(V8RuntimeAgentImplState::globalBindingsKey); 868 if (globalBindings) { 869 for (size_t i = 0; i < globalBindings->size(); ++i) 870 addBinding(context, globalBindings->at(i).first); 871 } 872 protocol::DictionaryValue* contextBindings = 873 contextName.isEmpty() ? nullptr : bindings->getObject(contextName); 874 if (contextBindings) { 875 for (size_t i = 0; i < contextBindings->size(); ++i) 876 addBinding(context, contextBindings->at(i).first); 877 } 878} 879 880void V8RuntimeAgentImpl::restore() { 881 if (!m_state->booleanProperty(V8RuntimeAgentImplState::runtimeEnabled, false)) 882 return; 883 m_frontend.executionContextsCleared(); 884 enable(); 885 if (m_state->booleanProperty( 886 V8RuntimeAgentImplState::customObjectFormatterEnabled, false)) 887 m_session->setCustomObjectFormatterEnabled(true); 888 889 int size; 890 if (m_state->getInteger(V8RuntimeAgentImplState::maxCallStackSizeToCapture, 891 &size)) 892 m_inspector->debugger()->setMaxCallStackSizeToCapture(this, size); 893 894 m_inspector->forEachContext( 895 m_session->contextGroupId(), 896 [this](InspectedContext* context) { addBindings(context); }); 897} 898 899Response V8RuntimeAgentImpl::enable() { 900 if (m_enabled) return Response::Success(); 901 TRACE_EVENT_WITH_FLOW0(TRACE_DISABLED_BY_DEFAULT("v8.inspector"), 902 "V8RuntimeAgentImpl::enable", this, 903 TRACE_EVENT_FLAG_FLOW_OUT); 904 m_inspector->client()->beginEnsureAllContextsInGroup( 905 m_session->contextGroupId()); 906 m_enabled = true; 907 m_state->setBoolean(V8RuntimeAgentImplState::runtimeEnabled, true); 908 m_inspector->debugger()->setMaxCallStackSizeToCapture( 909 this, V8StackTraceImpl::kDefaultMaxCallStackSizeToCapture); 910 m_session->reportAllContexts(this); 911 V8ConsoleMessageStorage* storage = 912 m_inspector->ensureConsoleMessageStorage(m_session->contextGroupId()); 913 for (const auto& message : storage->messages()) { 914 if (!reportMessage(message.get(), false)) break; 915 } 916 return Response::Success(); 917} 918 919Response V8RuntimeAgentImpl::disable() { 920 if (!m_enabled) return Response::Success(); 921 TRACE_EVENT_WITH_FLOW0(TRACE_DISABLED_BY_DEFAULT("v8.inspector"), 922 "V8RuntimeAgentImpl::disable", this, 923 TRACE_EVENT_FLAG_FLOW_IN); 924 m_enabled = false; 925 m_state->setBoolean(V8RuntimeAgentImplState::runtimeEnabled, false); 926 m_state->remove(V8RuntimeAgentImplState::bindings); 927 m_inspector->debugger()->setMaxCallStackSizeToCapture(this, -1); 928 m_session->setCustomObjectFormatterEnabled(false); 929 reset(); 930 m_inspector->client()->endEnsureAllContextsInGroup( 931 m_session->contextGroupId()); 932 if (m_session->debuggerAgent() && !m_session->debuggerAgent()->enabled()) { 933 m_session->debuggerAgent()->setAsyncCallStackDepth(0); 934 } 935 return Response::Success(); 936} 937 938void V8RuntimeAgentImpl::reset() { 939 m_compiledScripts.clear(); 940 if (m_enabled) { 941 int sessionId = m_session->sessionId(); 942 m_inspector->forEachContext(m_session->contextGroupId(), 943 [&sessionId](InspectedContext* context) { 944 context->setReported(sessionId, false); 945 }); 946 m_frontend.executionContextsCleared(); 947 } 948} 949 950void V8RuntimeAgentImpl::reportExecutionContextCreated( 951 InspectedContext* context) { 952 if (!m_enabled) return; 953 context->setReported(m_session->sessionId(), true); 954 std::unique_ptr<protocol::Runtime::ExecutionContextDescription> description = 955 protocol::Runtime::ExecutionContextDescription::create() 956 .setId(context->contextId()) 957 .setName(context->humanReadableName()) 958 .setOrigin(context->origin()) 959 .setUniqueId(context->uniqueId().toString()) 960 .build(); 961 const String16& aux = context->auxData(); 962 if (!aux.isEmpty()) { 963 std::vector<uint8_t> cbor; 964 v8_crdtp::json::ConvertJSONToCBOR( 965 v8_crdtp::span<uint16_t>(aux.characters16(), aux.length()), &cbor); 966 description->setAuxData(protocol::DictionaryValue::cast( 967 protocol::Value::parseBinary(cbor.data(), cbor.size()))); 968 } 969 m_frontend.executionContextCreated(std::move(description)); 970} 971 972void V8RuntimeAgentImpl::reportExecutionContextDestroyed( 973 InspectedContext* context) { 974 if (m_enabled && context->isReported(m_session->sessionId())) { 975 context->setReported(m_session->sessionId(), false); 976 m_frontend.executionContextDestroyed(context->contextId()); 977 } 978} 979 980void V8RuntimeAgentImpl::inspect( 981 std::unique_ptr<protocol::Runtime::RemoteObject> objectToInspect, 982 std::unique_ptr<protocol::DictionaryValue> hints, int executionContextId) { 983 if (m_enabled) 984 m_frontend.inspectRequested(std::move(objectToInspect), std::move(hints), 985 executionContextId); 986} 987 988void V8RuntimeAgentImpl::messageAdded(V8ConsoleMessage* message) { 989 if (m_enabled) reportMessage(message, true); 990} 991 992bool V8RuntimeAgentImpl::reportMessage(V8ConsoleMessage* message, 993 bool generatePreview) { 994 message->reportToFrontend(&m_frontend, m_session, generatePreview); 995 m_frontend.flush(); 996 return m_inspector->hasConsoleMessageStorage(m_session->contextGroupId()); 997} 998} // namespace v8_inspector 999