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 "src/debug/debug-interface.h" 36 #include "src/inspector/injected-script.h" 37 #include "src/inspector/inspected-context.h" 38 #include "src/inspector/protocol/Protocol.h" 39 #include "src/inspector/remote-object-id.h" 40 #include "src/inspector/v8-console-message.h" 41 #include "src/inspector/v8-debugger-agent-impl.h" 42 #include "src/inspector/v8-debugger.h" 43 #include "src/inspector/v8-inspector-impl.h" 44 #include "src/inspector/v8-inspector-session-impl.h" 45 #include "src/inspector/v8-stack-trace-impl.h" 46 #include "src/inspector/v8-value-utils.h" 47 #include "src/tracing/trace-event.h" 48 49 #include "include/v8-inspector.h" 50 51 namespace v8_inspector { 52 53 namespace V8RuntimeAgentImplState { 54 static const char customObjectFormatterEnabled[] = 55 "customObjectFormatterEnabled"; 56 static const char runtimeEnabled[] = "runtimeEnabled"; 57 static const char bindings[] = "bindings"; 58 }; 59 60 using protocol::Runtime::RemoteObject; 61 62 namespace { 63 64 template <typename ProtocolCallback> 65 class EvaluateCallbackWrapper : public EvaluateCallback { 66 public: 67 static std::unique_ptr<EvaluateCallback> wrap( 68 std::unique_ptr<ProtocolCallback> callback) { 69 return std::unique_ptr<EvaluateCallback>( 70 new EvaluateCallbackWrapper(std::move(callback))); 71 } 72 void sendSuccess(std::unique_ptr<protocol::Runtime::RemoteObject> result, 73 protocol::Maybe<protocol::Runtime::ExceptionDetails> 74 exceptionDetails) override { 75 return m_callback->sendSuccess(std::move(result), 76 std::move(exceptionDetails)); 77 } 78 void sendFailure(const protocol::DispatchResponse& response) override { 79 return m_callback->sendFailure(response); 80 } 81 82 private: 83 explicit EvaluateCallbackWrapper(std::unique_ptr<ProtocolCallback> callback) 84 : m_callback(std::move(callback)) {} 85 86 std::unique_ptr<ProtocolCallback> m_callback; 87 }; 88 89 template <typename ProtocolCallback> 90 bool wrapEvaluateResultAsync(InjectedScript* injectedScript, 91 v8::MaybeLocal<v8::Value> maybeResultValue, 92 const v8::TryCatch& tryCatch, 93 const String16& objectGroup, bool returnByValue, 94 bool generatePreview, ProtocolCallback* callback) { 95 std::unique_ptr<RemoteObject> result; 96 Maybe<protocol::Runtime::ExceptionDetails> exceptionDetails; 97 98 Response response = injectedScript->wrapEvaluateResult( 99 maybeResultValue, tryCatch, objectGroup, returnByValue, generatePreview, 100 &result, &exceptionDetails); 101 if (response.isSuccess()) { 102 callback->sendSuccess(std::move(result), std::move(exceptionDetails)); 103 return true; 104 } 105 callback->sendFailure(response); 106 return false; 107 } 108 109 void innerCallFunctionOn( 110 V8InspectorSessionImpl* session, InjectedScript::Scope& scope, 111 v8::Local<v8::Value> recv, const String16& expression, 112 Maybe<protocol::Array<protocol::Runtime::CallArgument>> optionalArguments, 113 bool silent, bool returnByValue, bool generatePreview, bool userGesture, 114 bool awaitPromise, const String16& objectGroup, 115 std::unique_ptr<V8RuntimeAgentImpl::CallFunctionOnCallback> callback) { 116 V8InspectorImpl* inspector = session->inspector(); 117 118 std::unique_ptr<v8::Local<v8::Value>[]> argv = nullptr; 119 int argc = 0; 120 if (optionalArguments.isJust()) { 121 protocol::Array<protocol::Runtime::CallArgument>* arguments = 122 optionalArguments.fromJust(); 123 argc = static_cast<int>(arguments->length()); 124 argv.reset(new v8::Local<v8::Value>[argc]); 125 for (int i = 0; i < argc; ++i) { 126 v8::Local<v8::Value> argumentValue; 127 Response response = scope.injectedScript()->resolveCallArgument( 128 arguments->get(i), &argumentValue); 129 if (!response.isSuccess()) { 130 callback->sendFailure(response); 131 return; 132 } 133 argv[i] = argumentValue; 134 } 135 } 136 137 if (silent) scope.ignoreExceptionsAndMuteConsole(); 138 if (userGesture) scope.pretendUserGesture(); 139 140 // Temporarily enable allow evals for inspector. 141 scope.allowCodeGenerationFromStrings(); 142 143 v8::MaybeLocal<v8::Value> maybeFunctionValue; 144 v8::Local<v8::Script> functionScript; 145 if (inspector 146 ->compileScript(scope.context(), "(" + expression + ")", String16()) 147 .ToLocal(&functionScript)) { 148 v8::MicrotasksScope microtasksScope(inspector->isolate(), 149 v8::MicrotasksScope::kRunMicrotasks); 150 maybeFunctionValue = functionScript->Run(scope.context()); 151 } 152 // Re-initialize after running client's code, as it could have destroyed 153 // context or session. 154 Response response = scope.initialize(); 155 if (!response.isSuccess()) { 156 callback->sendFailure(response); 157 return; 158 } 159 160 if (scope.tryCatch().HasCaught()) { 161 wrapEvaluateResultAsync(scope.injectedScript(), maybeFunctionValue, 162 scope.tryCatch(), objectGroup, false, false, 163 callback.get()); 164 return; 165 } 166 167 v8::Local<v8::Value> functionValue; 168 if (!maybeFunctionValue.ToLocal(&functionValue) || 169 !functionValue->IsFunction()) { 170 callback->sendFailure( 171 Response::Error("Given expression does not evaluate to a function")); 172 return; 173 } 174 175 v8::MaybeLocal<v8::Value> maybeResultValue; 176 { 177 v8::MicrotasksScope microtasksScope(inspector->isolate(), 178 v8::MicrotasksScope::kRunMicrotasks); 179 maybeResultValue = functionValue.As<v8::Function>()->Call( 180 scope.context(), recv, argc, argv.get()); 181 } 182 // Re-initialize after running client's code, as it could have destroyed 183 // context or session. 184 response = scope.initialize(); 185 if (!response.isSuccess()) { 186 callback->sendFailure(response); 187 return; 188 } 189 190 if (!awaitPromise || scope.tryCatch().HasCaught()) { 191 wrapEvaluateResultAsync(scope.injectedScript(), maybeResultValue, 192 scope.tryCatch(), objectGroup, returnByValue, 193 generatePreview, callback.get()); 194 return; 195 } 196 197 scope.injectedScript()->addPromiseCallback( 198 session, maybeResultValue, objectGroup, returnByValue, generatePreview, 199 EvaluateCallbackWrapper<V8RuntimeAgentImpl::CallFunctionOnCallback>::wrap( 200 std::move(callback))); 201 } 202 203 Response ensureContext(V8InspectorImpl* inspector, int contextGroupId, 204 Maybe<int> executionContextId, int* contextId) { 205 if (executionContextId.isJust()) { 206 *contextId = executionContextId.fromJust(); 207 } else { 208 v8::HandleScope handles(inspector->isolate()); 209 v8::Local<v8::Context> defaultContext = 210 inspector->client()->ensureDefaultContextInGroup(contextGroupId); 211 if (defaultContext.IsEmpty()) 212 return Response::Error("Cannot find default execution context"); 213 *contextId = InspectedContext::contextId(defaultContext); 214 } 215 return Response::OK(); 216 } 217 218 } // namespace 219 220 V8RuntimeAgentImpl::V8RuntimeAgentImpl( 221 V8InspectorSessionImpl* session, protocol::FrontendChannel* FrontendChannel, 222 protocol::DictionaryValue* state) 223 : m_session(session), 224 m_state(state), 225 m_frontend(FrontendChannel), 226 m_inspector(session->inspector()), 227 m_enabled(false) {} 228 229 V8RuntimeAgentImpl::~V8RuntimeAgentImpl() {} 230 231 void V8RuntimeAgentImpl::evaluate( 232 const String16& expression, Maybe<String16> objectGroup, 233 Maybe<bool> includeCommandLineAPI, Maybe<bool> silent, 234 Maybe<int> executionContextId, Maybe<bool> returnByValue, 235 Maybe<bool> generatePreview, Maybe<bool> userGesture, 236 Maybe<bool> awaitPromise, Maybe<bool> throwOnSideEffect, 237 Maybe<double> timeout, std::unique_ptr<EvaluateCallback> callback) { 238 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), 239 "EvaluateScript"); 240 int contextId = 0; 241 Response response = ensureContext(m_inspector, m_session->contextGroupId(), 242 std::move(executionContextId), &contextId); 243 if (!response.isSuccess()) { 244 callback->sendFailure(response); 245 return; 246 } 247 248 InjectedScript::ContextScope scope(m_session, contextId); 249 response = scope.initialize(); 250 if (!response.isSuccess()) { 251 callback->sendFailure(response); 252 return; 253 } 254 255 if (silent.fromMaybe(false)) scope.ignoreExceptionsAndMuteConsole(); 256 if (userGesture.fromMaybe(false)) scope.pretendUserGesture(); 257 258 if (includeCommandLineAPI.fromMaybe(false)) scope.installCommandLineAPI(); 259 260 // Temporarily enable allow evals for inspector. 261 scope.allowCodeGenerationFromStrings(); 262 v8::MaybeLocal<v8::Value> maybeResultValue; 263 { 264 V8InspectorImpl::EvaluateScope evaluateScope(m_inspector->isolate()); 265 if (timeout.isJust()) { 266 response = evaluateScope.setTimeout(timeout.fromJust() / 1000.0); 267 if (!response.isSuccess()) { 268 callback->sendFailure(response); 269 return; 270 } 271 } 272 v8::MicrotasksScope microtasksScope(m_inspector->isolate(), 273 v8::MicrotasksScope::kRunMicrotasks); 274 maybeResultValue = v8::debug::EvaluateGlobal( 275 m_inspector->isolate(), toV8String(m_inspector->isolate(), expression), 276 throwOnSideEffect.fromMaybe(false)); 277 } // Run microtasks before returning result. 278 279 // Re-initialize after running client's code, as it could have destroyed 280 // context or session. 281 response = scope.initialize(); 282 if (!response.isSuccess()) { 283 callback->sendFailure(response); 284 return; 285 } 286 287 if (!awaitPromise.fromMaybe(false) || scope.tryCatch().HasCaught()) { 288 wrapEvaluateResultAsync(scope.injectedScript(), maybeResultValue, 289 scope.tryCatch(), objectGroup.fromMaybe(""), 290 returnByValue.fromMaybe(false), 291 generatePreview.fromMaybe(false), callback.get()); 292 return; 293 } 294 scope.injectedScript()->addPromiseCallback( 295 m_session, maybeResultValue, objectGroup.fromMaybe(""), 296 returnByValue.fromMaybe(false), generatePreview.fromMaybe(false), 297 EvaluateCallbackWrapper<EvaluateCallback>::wrap(std::move(callback))); 298 } 299 300 void V8RuntimeAgentImpl::awaitPromise( 301 const String16& promiseObjectId, Maybe<bool> returnByValue, 302 Maybe<bool> generatePreview, 303 std::unique_ptr<AwaitPromiseCallback> callback) { 304 InjectedScript::ObjectScope scope(m_session, promiseObjectId); 305 Response response = scope.initialize(); 306 if (!response.isSuccess()) { 307 callback->sendFailure(response); 308 return; 309 } 310 if (!scope.object()->IsPromise()) { 311 callback->sendFailure( 312 Response::Error("Could not find promise with given id")); 313 return; 314 } 315 scope.injectedScript()->addPromiseCallback( 316 m_session, scope.object(), scope.objectGroupName(), 317 returnByValue.fromMaybe(false), generatePreview.fromMaybe(false), 318 EvaluateCallbackWrapper<AwaitPromiseCallback>::wrap(std::move(callback))); 319 } 320 321 void V8RuntimeAgentImpl::callFunctionOn( 322 const String16& expression, Maybe<String16> objectId, 323 Maybe<protocol::Array<protocol::Runtime::CallArgument>> optionalArguments, 324 Maybe<bool> silent, Maybe<bool> returnByValue, Maybe<bool> generatePreview, 325 Maybe<bool> userGesture, Maybe<bool> awaitPromise, 326 Maybe<int> executionContextId, Maybe<String16> objectGroup, 327 std::unique_ptr<CallFunctionOnCallback> callback) { 328 if (objectId.isJust() && executionContextId.isJust()) { 329 callback->sendFailure(Response::Error( 330 "ObjectId must not be specified together with executionContextId")); 331 return; 332 } 333 if (!objectId.isJust() && !executionContextId.isJust()) { 334 callback->sendFailure(Response::Error( 335 "Either ObjectId or executionContextId must be specified")); 336 return; 337 } 338 if (objectId.isJust()) { 339 InjectedScript::ObjectScope scope(m_session, objectId.fromJust()); 340 Response response = scope.initialize(); 341 if (!response.isSuccess()) { 342 callback->sendFailure(response); 343 return; 344 } 345 innerCallFunctionOn( 346 m_session, scope, scope.object(), expression, 347 std::move(optionalArguments), silent.fromMaybe(false), 348 returnByValue.fromMaybe(false), generatePreview.fromMaybe(false), 349 userGesture.fromMaybe(false), awaitPromise.fromMaybe(false), 350 objectGroup.isJust() ? objectGroup.fromMaybe(String16()) 351 : scope.objectGroupName(), 352 std::move(callback)); 353 } else { 354 int contextId = 0; 355 Response response = 356 ensureContext(m_inspector, m_session->contextGroupId(), 357 std::move(executionContextId.fromJust()), &contextId); 358 if (!response.isSuccess()) { 359 callback->sendFailure(response); 360 return; 361 } 362 InjectedScript::ContextScope scope(m_session, contextId); 363 response = scope.initialize(); 364 if (!response.isSuccess()) { 365 callback->sendFailure(response); 366 return; 367 } 368 innerCallFunctionOn( 369 m_session, scope, scope.context()->Global(), expression, 370 std::move(optionalArguments), silent.fromMaybe(false), 371 returnByValue.fromMaybe(false), generatePreview.fromMaybe(false), 372 userGesture.fromMaybe(false), awaitPromise.fromMaybe(false), 373 objectGroup.fromMaybe(""), std::move(callback)); 374 } 375 } 376 377 Response V8RuntimeAgentImpl::getProperties( 378 const String16& objectId, Maybe<bool> ownProperties, 379 Maybe<bool> accessorPropertiesOnly, Maybe<bool> generatePreview, 380 std::unique_ptr<protocol::Array<protocol::Runtime::PropertyDescriptor>>* 381 result, 382 Maybe<protocol::Array<protocol::Runtime::InternalPropertyDescriptor>>* 383 internalProperties, 384 Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) { 385 using protocol::Runtime::InternalPropertyDescriptor; 386 387 InjectedScript::ObjectScope scope(m_session, objectId); 388 Response response = scope.initialize(); 389 if (!response.isSuccess()) return response; 390 391 scope.ignoreExceptionsAndMuteConsole(); 392 v8::MicrotasksScope microtasks_scope(m_inspector->isolate(), 393 v8::MicrotasksScope::kRunMicrotasks); 394 if (!scope.object()->IsObject()) 395 return Response::Error("Value with given id is not an object"); 396 397 v8::Local<v8::Object> object = scope.object().As<v8::Object>(); 398 response = scope.injectedScript()->getProperties( 399 object, scope.objectGroupName(), ownProperties.fromMaybe(false), 400 accessorPropertiesOnly.fromMaybe(false), generatePreview.fromMaybe(false), 401 result, exceptionDetails); 402 if (!response.isSuccess()) return response; 403 if (exceptionDetails->isJust() || accessorPropertiesOnly.fromMaybe(false)) 404 return Response::OK(); 405 v8::Local<v8::Array> propertiesArray; 406 if (!m_inspector->debugger() 407 ->internalProperties(scope.context(), scope.object()) 408 .ToLocal(&propertiesArray)) { 409 return Response::InternalError(); 410 } 411 std::unique_ptr<protocol::Array<InternalPropertyDescriptor>> 412 propertiesProtocolArray = 413 protocol::Array<InternalPropertyDescriptor>::create(); 414 for (uint32_t i = 0; i < propertiesArray->Length(); i += 2) { 415 v8::Local<v8::Value> name; 416 if (!propertiesArray->Get(scope.context(), i).ToLocal(&name) || 417 !name->IsString()) { 418 return Response::InternalError(); 419 } 420 v8::Local<v8::Value> value; 421 if (!propertiesArray->Get(scope.context(), i + 1).ToLocal(&value)) 422 return Response::InternalError(); 423 std::unique_ptr<RemoteObject> wrappedValue; 424 protocol::Response response = scope.injectedScript()->wrapObject( 425 value, scope.objectGroupName(), false, false, &wrappedValue); 426 if (!response.isSuccess()) return response; 427 propertiesProtocolArray->addItem( 428 InternalPropertyDescriptor::create() 429 .setName( 430 toProtocolString(m_inspector->isolate(), name.As<v8::String>())) 431 .setValue(std::move(wrappedValue)) 432 .build()); 433 } 434 if (propertiesProtocolArray->length()) 435 *internalProperties = std::move(propertiesProtocolArray); 436 return Response::OK(); 437 } 438 439 Response V8RuntimeAgentImpl::releaseObject(const String16& objectId) { 440 InjectedScript::ObjectScope scope(m_session, objectId); 441 Response response = scope.initialize(); 442 if (!response.isSuccess()) return response; 443 scope.injectedScript()->releaseObject(objectId); 444 return Response::OK(); 445 } 446 447 Response V8RuntimeAgentImpl::releaseObjectGroup(const String16& objectGroup) { 448 m_session->releaseObjectGroup(objectGroup); 449 return Response::OK(); 450 } 451 452 Response V8RuntimeAgentImpl::runIfWaitingForDebugger() { 453 m_inspector->client()->runIfWaitingForDebugger(m_session->contextGroupId()); 454 return Response::OK(); 455 } 456 457 Response V8RuntimeAgentImpl::setCustomObjectFormatterEnabled(bool enabled) { 458 m_state->setBoolean(V8RuntimeAgentImplState::customObjectFormatterEnabled, 459 enabled); 460 if (!m_enabled) return Response::Error("Runtime agent is not enabled"); 461 m_session->setCustomObjectFormatterEnabled(enabled); 462 return Response::OK(); 463 } 464 465 Response V8RuntimeAgentImpl::setMaxCallStackSizeToCapture(int size) { 466 if (size < 0) { 467 return Response::Error("maxCallStackSizeToCapture should be non-negative"); 468 } 469 V8StackTraceImpl::maxCallStackSizeToCapture = size; 470 return Response::OK(); 471 } 472 473 Response V8RuntimeAgentImpl::discardConsoleEntries() { 474 V8ConsoleMessageStorage* storage = 475 m_inspector->ensureConsoleMessageStorage(m_session->contextGroupId()); 476 storage->clear(); 477 return Response::OK(); 478 } 479 480 Response V8RuntimeAgentImpl::compileScript( 481 const String16& expression, const String16& sourceURL, bool persistScript, 482 Maybe<int> executionContextId, Maybe<String16>* scriptId, 483 Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) { 484 if (!m_enabled) return Response::Error("Runtime agent is not enabled"); 485 486 int contextId = 0; 487 Response response = ensureContext(m_inspector, m_session->contextGroupId(), 488 std::move(executionContextId), &contextId); 489 if (!response.isSuccess()) return response; 490 InjectedScript::ContextScope scope(m_session, contextId); 491 response = scope.initialize(); 492 if (!response.isSuccess()) return response; 493 494 if (!persistScript) m_inspector->debugger()->muteScriptParsedEvents(); 495 v8::Local<v8::Script> script; 496 bool isOk = m_inspector->compileScript(scope.context(), expression, sourceURL) 497 .ToLocal(&script); 498 if (!persistScript) m_inspector->debugger()->unmuteScriptParsedEvents(); 499 if (!isOk) { 500 if (scope.tryCatch().HasCaught()) { 501 response = scope.injectedScript()->createExceptionDetails( 502 scope.tryCatch(), String16(), false, exceptionDetails); 503 if (!response.isSuccess()) return response; 504 return Response::OK(); 505 } else { 506 return Response::Error("Script compilation failed"); 507 } 508 } 509 510 if (!persistScript) return Response::OK(); 511 512 String16 scriptValueId = 513 String16::fromInteger(script->GetUnboundScript()->GetId()); 514 std::unique_ptr<v8::Global<v8::Script>> global( 515 new v8::Global<v8::Script>(m_inspector->isolate(), script)); 516 m_compiledScripts[scriptValueId] = std::move(global); 517 *scriptId = scriptValueId; 518 return Response::OK(); 519 } 520 521 void V8RuntimeAgentImpl::runScript( 522 const String16& scriptId, Maybe<int> executionContextId, 523 Maybe<String16> objectGroup, Maybe<bool> silent, 524 Maybe<bool> includeCommandLineAPI, Maybe<bool> returnByValue, 525 Maybe<bool> generatePreview, Maybe<bool> awaitPromise, 526 std::unique_ptr<RunScriptCallback> callback) { 527 if (!m_enabled) { 528 callback->sendFailure(Response::Error("Runtime agent is not enabled")); 529 return; 530 } 531 532 auto it = m_compiledScripts.find(scriptId); 533 if (it == m_compiledScripts.end()) { 534 callback->sendFailure(Response::Error("No script with given id")); 535 return; 536 } 537 538 int contextId = 0; 539 Response response = ensureContext(m_inspector, m_session->contextGroupId(), 540 std::move(executionContextId), &contextId); 541 if (!response.isSuccess()) { 542 callback->sendFailure(response); 543 return; 544 } 545 546 InjectedScript::ContextScope scope(m_session, contextId); 547 response = scope.initialize(); 548 if (!response.isSuccess()) { 549 callback->sendFailure(response); 550 return; 551 } 552 553 if (silent.fromMaybe(false)) scope.ignoreExceptionsAndMuteConsole(); 554 555 std::unique_ptr<v8::Global<v8::Script>> scriptWrapper = std::move(it->second); 556 m_compiledScripts.erase(it); 557 v8::Local<v8::Script> script = scriptWrapper->Get(m_inspector->isolate()); 558 if (script.IsEmpty()) { 559 callback->sendFailure(Response::Error("Script execution failed")); 560 return; 561 } 562 563 if (includeCommandLineAPI.fromMaybe(false)) scope.installCommandLineAPI(); 564 565 v8::MaybeLocal<v8::Value> maybeResultValue; 566 { 567 v8::MicrotasksScope microtasksScope(m_inspector->isolate(), 568 v8::MicrotasksScope::kRunMicrotasks); 569 maybeResultValue = script->Run(scope.context()); 570 } 571 572 // Re-initialize after running client's code, as it could have destroyed 573 // context or session. 574 response = scope.initialize(); 575 if (!response.isSuccess()) { 576 callback->sendFailure(response); 577 return; 578 } 579 580 if (!awaitPromise.fromMaybe(false) || scope.tryCatch().HasCaught()) { 581 wrapEvaluateResultAsync(scope.injectedScript(), maybeResultValue, 582 scope.tryCatch(), objectGroup.fromMaybe(""), 583 returnByValue.fromMaybe(false), 584 generatePreview.fromMaybe(false), callback.get()); 585 return; 586 } 587 scope.injectedScript()->addPromiseCallback( 588 m_session, maybeResultValue.ToLocalChecked(), 589 objectGroup.fromMaybe(""), returnByValue.fromMaybe(false), 590 generatePreview.fromMaybe(false), 591 EvaluateCallbackWrapper<RunScriptCallback>::wrap(std::move(callback))); 592 } 593 594 Response V8RuntimeAgentImpl::queryObjects( 595 const String16& prototypeObjectId, Maybe<String16> objectGroup, 596 std::unique_ptr<protocol::Runtime::RemoteObject>* objects) { 597 InjectedScript::ObjectScope scope(m_session, prototypeObjectId); 598 Response response = scope.initialize(); 599 if (!response.isSuccess()) return response; 600 if (!scope.object()->IsObject()) { 601 return Response::Error("Prototype should be instance of Object"); 602 } 603 v8::Local<v8::Array> resultArray = m_inspector->debugger()->queryObjects( 604 scope.context(), v8::Local<v8::Object>::Cast(scope.object())); 605 return scope.injectedScript()->wrapObject( 606 resultArray, objectGroup.fromMaybe(scope.objectGroupName()), false, false, 607 objects); 608 } 609 610 Response V8RuntimeAgentImpl::globalLexicalScopeNames( 611 Maybe<int> executionContextId, 612 std::unique_ptr<protocol::Array<String16>>* outNames) { 613 int contextId = 0; 614 Response response = ensureContext(m_inspector, m_session->contextGroupId(), 615 std::move(executionContextId), &contextId); 616 if (!response.isSuccess()) return response; 617 618 InjectedScript::ContextScope scope(m_session, contextId); 619 response = scope.initialize(); 620 if (!response.isSuccess()) return response; 621 622 v8::PersistentValueVector<v8::String> names(m_inspector->isolate()); 623 v8::debug::GlobalLexicalScopeNames(scope.context(), &names); 624 *outNames = protocol::Array<String16>::create(); 625 for (size_t i = 0; i < names.Size(); ++i) { 626 (*outNames)->addItem( 627 toProtocolString(m_inspector->isolate(), names.Get(i))); 628 } 629 return Response::OK(); 630 } 631 632 Response V8RuntimeAgentImpl::getIsolateId(String16* outIsolateId) { 633 char buf[40]; 634 std::snprintf(buf, sizeof(buf), "%" PRIx64, m_inspector->isolateId()); 635 *outIsolateId = buf; 636 return Response::OK(); 637 } 638 639 Response V8RuntimeAgentImpl::getHeapUsage(double* out_usedSize, 640 double* out_totalSize) { 641 v8::HeapStatistics stats; 642 m_inspector->isolate()->GetHeapStatistics(&stats); 643 *out_usedSize = stats.used_heap_size(); 644 *out_totalSize = stats.total_heap_size(); 645 return Response::OK(); 646 } 647 648 void V8RuntimeAgentImpl::terminateExecution( 649 std::unique_ptr<TerminateExecutionCallback> callback) { 650 m_inspector->debugger()->terminateExecution(std::move(callback)); 651 } 652 653 Response V8RuntimeAgentImpl::addBinding(const String16& name, 654 Maybe<int> executionContextId) { 655 if (!m_state->getObject(V8RuntimeAgentImplState::bindings)) { 656 m_state->setObject(V8RuntimeAgentImplState::bindings, 657 protocol::DictionaryValue::create()); 658 } 659 protocol::DictionaryValue* bindings = 660 m_state->getObject(V8RuntimeAgentImplState::bindings); 661 if (bindings->booleanProperty(name, false)) return Response::OK(); 662 if (executionContextId.isJust()) { 663 int contextId = executionContextId.fromJust(); 664 InspectedContext* context = 665 m_inspector->getContext(m_session->contextGroupId(), contextId); 666 if (!context) { 667 return Response::Error( 668 "Cannot find execution context with given executionContextId"); 669 } 670 addBinding(context, name); 671 // false means that we should not add this binding later. 672 bindings->setBoolean(name, false); 673 return Response::OK(); 674 } 675 bindings->setBoolean(name, true); 676 m_inspector->forEachContext( 677 m_session->contextGroupId(), 678 [&name, this](InspectedContext* context) { addBinding(context, name); }); 679 return Response::OK(); 680 } 681 682 void V8RuntimeAgentImpl::bindingCallback( 683 const v8::FunctionCallbackInfo<v8::Value>& info) { 684 v8::Isolate* isolate = info.GetIsolate(); 685 if (info.Length() != 1 || !info[0]->IsString()) { 686 info.GetIsolate()->ThrowException(toV8String( 687 isolate, "Invalid arguments: should be exactly one string.")); 688 return; 689 } 690 V8InspectorImpl* inspector = 691 static_cast<V8InspectorImpl*>(v8::debug::GetInspector(isolate)); 692 int contextId = InspectedContext::contextId(isolate->GetCurrentContext()); 693 int contextGroupId = inspector->contextGroupId(contextId); 694 695 String16 name = 696 toProtocolString(isolate, v8::Local<v8::String>::Cast(info.Data())); 697 String16 payload = 698 toProtocolString(isolate, v8::Local<v8::String>::Cast(info[0])); 699 700 inspector->forEachSession( 701 contextGroupId, 702 [&name, &payload, &contextId](V8InspectorSessionImpl* session) { 703 session->runtimeAgent()->bindingCalled(name, payload, contextId); 704 }); 705 } 706 707 void V8RuntimeAgentImpl::addBinding(InspectedContext* context, 708 const String16& name) { 709 v8::HandleScope handles(m_inspector->isolate()); 710 v8::Local<v8::Context> localContext = context->context(); 711 v8::Local<v8::Object> global = localContext->Global(); 712 v8::Local<v8::String> v8Name = toV8String(m_inspector->isolate(), name); 713 v8::Local<v8::Value> functionValue; 714 v8::MicrotasksScope microtasks(m_inspector->isolate(), 715 v8::MicrotasksScope::kDoNotRunMicrotasks); 716 if (v8::Function::New(localContext, bindingCallback, v8Name) 717 .ToLocal(&functionValue)) { 718 v8::Maybe<bool> success = global->Set(localContext, v8Name, functionValue); 719 USE(success); 720 } 721 } 722 723 Response V8RuntimeAgentImpl::removeBinding(const String16& name) { 724 protocol::DictionaryValue* bindings = 725 m_state->getObject(V8RuntimeAgentImplState::bindings); 726 if (!bindings) return Response::OK(); 727 bindings->remove(name); 728 return Response::OK(); 729 } 730 731 void V8RuntimeAgentImpl::bindingCalled(const String16& name, 732 const String16& payload, 733 int executionContextId) { 734 protocol::DictionaryValue* bindings = 735 m_state->getObject(V8RuntimeAgentImplState::bindings); 736 if (!bindings || !bindings->get(name)) return; 737 m_frontend.bindingCalled(name, payload, executionContextId); 738 } 739 740 void V8RuntimeAgentImpl::addBindings(InspectedContext* context) { 741 if (!m_enabled) return; 742 protocol::DictionaryValue* bindings = 743 m_state->getObject(V8RuntimeAgentImplState::bindings); 744 if (!bindings) return; 745 for (size_t i = 0; i < bindings->size(); ++i) { 746 if (!bindings->at(i).second) continue; 747 addBinding(context, bindings->at(i).first); 748 } 749 } 750 751 void V8RuntimeAgentImpl::restore() { 752 if (!m_state->booleanProperty(V8RuntimeAgentImplState::runtimeEnabled, false)) 753 return; 754 m_frontend.executionContextsCleared(); 755 enable(); 756 if (m_state->booleanProperty( 757 V8RuntimeAgentImplState::customObjectFormatterEnabled, false)) 758 m_session->setCustomObjectFormatterEnabled(true); 759 760 m_inspector->forEachContext( 761 m_session->contextGroupId(), 762 [this](InspectedContext* context) { addBindings(context); }); 763 } 764 765 Response V8RuntimeAgentImpl::enable() { 766 if (m_enabled) return Response::OK(); 767 m_inspector->client()->beginEnsureAllContextsInGroup( 768 m_session->contextGroupId()); 769 m_enabled = true; 770 m_state->setBoolean(V8RuntimeAgentImplState::runtimeEnabled, true); 771 m_inspector->enableStackCapturingIfNeeded(); 772 m_session->reportAllContexts(this); 773 V8ConsoleMessageStorage* storage = 774 m_inspector->ensureConsoleMessageStorage(m_session->contextGroupId()); 775 for (const auto& message : storage->messages()) { 776 if (!reportMessage(message.get(), false)) break; 777 } 778 return Response::OK(); 779 } 780 781 Response V8RuntimeAgentImpl::disable() { 782 if (!m_enabled) return Response::OK(); 783 m_enabled = false; 784 m_state->setBoolean(V8RuntimeAgentImplState::runtimeEnabled, false); 785 m_state->remove(V8RuntimeAgentImplState::bindings); 786 m_inspector->disableStackCapturingIfNeeded(); 787 m_session->setCustomObjectFormatterEnabled(false); 788 reset(); 789 m_inspector->client()->endEnsureAllContextsInGroup( 790 m_session->contextGroupId()); 791 if (m_session->debuggerAgent() && !m_session->debuggerAgent()->enabled()) { 792 m_session->debuggerAgent()->setAsyncCallStackDepth(0); 793 } 794 return Response::OK(); 795 } 796 797 void V8RuntimeAgentImpl::reset() { 798 m_compiledScripts.clear(); 799 if (m_enabled) { 800 int sessionId = m_session->sessionId(); 801 m_inspector->forEachContext(m_session->contextGroupId(), 802 [&sessionId](InspectedContext* context) { 803 context->setReported(sessionId, false); 804 }); 805 m_frontend.executionContextsCleared(); 806 } 807 } 808 809 void V8RuntimeAgentImpl::reportExecutionContextCreated( 810 InspectedContext* context) { 811 if (!m_enabled) return; 812 context->setReported(m_session->sessionId(), true); 813 std::unique_ptr<protocol::Runtime::ExecutionContextDescription> description = 814 protocol::Runtime::ExecutionContextDescription::create() 815 .setId(context->contextId()) 816 .setName(context->humanReadableName()) 817 .setOrigin(context->origin()) 818 .build(); 819 if (!context->auxData().isEmpty()) 820 description->setAuxData(protocol::DictionaryValue::cast( 821 protocol::StringUtil::parseJSON(context->auxData()))); 822 m_frontend.executionContextCreated(std::move(description)); 823 } 824 825 void V8RuntimeAgentImpl::reportExecutionContextDestroyed( 826 InspectedContext* context) { 827 if (m_enabled && context->isReported(m_session->sessionId())) { 828 context->setReported(m_session->sessionId(), false); 829 m_frontend.executionContextDestroyed(context->contextId()); 830 } 831 } 832 833 void V8RuntimeAgentImpl::inspect( 834 std::unique_ptr<protocol::Runtime::RemoteObject> objectToInspect, 835 std::unique_ptr<protocol::DictionaryValue> hints) { 836 if (m_enabled) 837 m_frontend.inspectRequested(std::move(objectToInspect), std::move(hints)); 838 } 839 840 void V8RuntimeAgentImpl::messageAdded(V8ConsoleMessage* message) { 841 if (m_enabled) reportMessage(message, true); 842 } 843 844 bool V8RuntimeAgentImpl::reportMessage(V8ConsoleMessage* message, 845 bool generatePreview) { 846 message->reportToFrontend(&m_frontend, m_session, generatePreview); 847 m_frontend.flush(); 848 return m_inspector->hasConsoleMessageStorage(m_session->contextGroupId()); 849 } 850 } // namespace v8_inspector 851