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 "src/debug/debug-interface.h" 34 #include "src/inspector/injected-script.h" 35 #include "src/inspector/inspected-context.h" 36 #include "src/inspector/protocol/Protocol.h" 37 #include "src/inspector/remote-object-id.h" 38 #include "src/inspector/string-util.h" 39 #include "src/inspector/v8-console-message.h" 40 #include "src/inspector/v8-debugger-agent-impl.h" 41 #include "src/inspector/v8-debugger.h" 42 #include "src/inspector/v8-inspector-impl.h" 43 #include "src/inspector/v8-inspector-session-impl.h" 44 #include "src/inspector/v8-stack-trace-impl.h" 45 #include "src/tracing/trace-event.h" 46 47 #include "include/v8-inspector.h" 48 49 namespace v8_inspector { 50 51 namespace V8RuntimeAgentImplState { 52 static const char customObjectFormatterEnabled[] = 53 "customObjectFormatterEnabled"; 54 static const char runtimeEnabled[] = "runtimeEnabled"; 55 }; 56 57 using protocol::Runtime::RemoteObject; 58 59 namespace { 60 61 template <typename Callback> 62 class ProtocolPromiseHandler { 63 public: 64 static void add(V8InspectorImpl* inspector, v8::Local<v8::Context> context, 65 v8::MaybeLocal<v8::Value> value, 66 const String16& notPromiseError, int contextGroupId, 67 int executionContextId, const String16& objectGroup, 68 bool returnByValue, bool generatePreview, 69 std::unique_ptr<Callback> callback) { 70 if (value.IsEmpty()) { 71 callback->sendFailure(Response::InternalError()); 72 return; 73 } 74 if (!value.ToLocalChecked()->IsPromise()) { 75 callback->sendFailure(Response::Error(notPromiseError)); 76 return; 77 } 78 v8::MicrotasksScope microtasks_scope(inspector->isolate(), 79 v8::MicrotasksScope::kRunMicrotasks); 80 v8::Local<v8::Promise> promise = 81 v8::Local<v8::Promise>::Cast(value.ToLocalChecked()); 82 Callback* rawCallback = callback.get(); 83 ProtocolPromiseHandler<Callback>* handler = new ProtocolPromiseHandler( 84 inspector, contextGroupId, executionContextId, objectGroup, 85 returnByValue, generatePreview, std::move(callback)); 86 v8::Local<v8::Value> wrapper = handler->m_wrapper.Get(inspector->isolate()); 87 88 v8::Local<v8::Function> thenCallbackFunction = 89 v8::Function::New(context, thenCallback, wrapper, 0, 90 v8::ConstructorBehavior::kThrow) 91 .ToLocalChecked(); 92 if (promise->Then(context, thenCallbackFunction).IsEmpty()) { 93 rawCallback->sendFailure(Response::InternalError()); 94 return; 95 } 96 v8::Local<v8::Function> catchCallbackFunction = 97 v8::Function::New(context, catchCallback, wrapper, 0, 98 v8::ConstructorBehavior::kThrow) 99 .ToLocalChecked(); 100 if (promise->Catch(context, catchCallbackFunction).IsEmpty()) { 101 rawCallback->sendFailure(Response::InternalError()); 102 return; 103 } 104 } 105 106 private: 107 static void thenCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { 108 ProtocolPromiseHandler<Callback>* handler = 109 static_cast<ProtocolPromiseHandler<Callback>*>( 110 info.Data().As<v8::External>()->Value()); 111 DCHECK(handler); 112 v8::Local<v8::Value> value = 113 info.Length() > 0 114 ? info[0] 115 : v8::Local<v8::Value>::Cast(v8::Undefined(info.GetIsolate())); 116 std::unique_ptr<protocol::Runtime::RemoteObject> wrappedValue( 117 handler->wrapObject(value)); 118 if (!wrappedValue) return; 119 handler->m_callback->sendSuccess( 120 std::move(wrappedValue), Maybe<protocol::Runtime::ExceptionDetails>()); 121 } 122 123 static void catchCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { 124 ProtocolPromiseHandler<Callback>* handler = 125 static_cast<ProtocolPromiseHandler<Callback>*>( 126 info.Data().As<v8::External>()->Value()); 127 DCHECK(handler); 128 v8::Local<v8::Value> value = 129 info.Length() > 0 130 ? info[0] 131 : v8::Local<v8::Value>::Cast(v8::Undefined(info.GetIsolate())); 132 133 std::unique_ptr<protocol::Runtime::RemoteObject> wrappedValue( 134 handler->wrapObject(value)); 135 if (!wrappedValue) return; 136 137 std::unique_ptr<V8StackTraceImpl> stack = 138 handler->m_inspector->debugger()->captureStackTrace(true); 139 std::unique_ptr<protocol::Runtime::ExceptionDetails> exceptionDetails = 140 protocol::Runtime::ExceptionDetails::create() 141 .setExceptionId(handler->m_inspector->nextExceptionId()) 142 .setText("Uncaught (in promise)") 143 .setLineNumber(stack && !stack->isEmpty() ? stack->topLineNumber() 144 : 0) 145 .setColumnNumber( 146 stack && !stack->isEmpty() ? stack->topColumnNumber() : 0) 147 .setException(wrappedValue->clone()) 148 .build(); 149 if (stack) 150 exceptionDetails->setStackTrace(stack->buildInspectorObjectImpl()); 151 if (stack && !stack->isEmpty()) 152 exceptionDetails->setScriptId(toString16(stack->topScriptId())); 153 handler->m_callback->sendSuccess(std::move(wrappedValue), 154 std::move(exceptionDetails)); 155 } 156 157 ProtocolPromiseHandler(V8InspectorImpl* inspector, int contextGroupId, 158 int executionContextId, const String16& objectGroup, 159 bool returnByValue, bool generatePreview, 160 std::unique_ptr<Callback> callback) 161 : m_inspector(inspector), 162 m_contextGroupId(contextGroupId), 163 m_executionContextId(executionContextId), 164 m_objectGroup(objectGroup), 165 m_returnByValue(returnByValue), 166 m_generatePreview(generatePreview), 167 m_callback(std::move(callback)), 168 m_wrapper(inspector->isolate(), 169 v8::External::New(inspector->isolate(), this)) { 170 m_wrapper.SetWeak(this, cleanup, v8::WeakCallbackType::kParameter); 171 } 172 173 static void cleanup( 174 const v8::WeakCallbackInfo<ProtocolPromiseHandler<Callback>>& data) { 175 if (!data.GetParameter()->m_wrapper.IsEmpty()) { 176 data.GetParameter()->m_wrapper.Reset(); 177 data.SetSecondPassCallback(cleanup); 178 } else { 179 data.GetParameter()->m_callback->sendFailure( 180 Response::Error("Promise was collected")); 181 delete data.GetParameter(); 182 } 183 } 184 185 std::unique_ptr<protocol::Runtime::RemoteObject> wrapObject( 186 v8::Local<v8::Value> value) { 187 InjectedScript::ContextScope scope(m_inspector, m_contextGroupId, 188 m_executionContextId); 189 Response response = scope.initialize(); 190 if (!response.isSuccess()) { 191 m_callback->sendFailure(response); 192 return nullptr; 193 } 194 std::unique_ptr<protocol::Runtime::RemoteObject> wrappedValue; 195 response = scope.injectedScript()->wrapObject( 196 value, m_objectGroup, m_returnByValue, m_generatePreview, 197 &wrappedValue); 198 if (!response.isSuccess()) { 199 m_callback->sendFailure(response); 200 return nullptr; 201 } 202 return wrappedValue; 203 } 204 205 V8InspectorImpl* m_inspector; 206 int m_contextGroupId; 207 int m_executionContextId; 208 String16 m_objectGroup; 209 bool m_returnByValue; 210 bool m_generatePreview; 211 std::unique_ptr<Callback> m_callback; 212 v8::Global<v8::External> m_wrapper; 213 }; 214 215 template <typename Callback> 216 bool wrapEvaluateResultAsync(InjectedScript* injectedScript, 217 v8::MaybeLocal<v8::Value> maybeResultValue, 218 const v8::TryCatch& tryCatch, 219 const String16& objectGroup, bool returnByValue, 220 bool generatePreview, Callback* callback) { 221 std::unique_ptr<RemoteObject> result; 222 Maybe<protocol::Runtime::ExceptionDetails> exceptionDetails; 223 224 Response response = injectedScript->wrapEvaluateResult( 225 maybeResultValue, tryCatch, objectGroup, returnByValue, generatePreview, 226 &result, &exceptionDetails); 227 if (response.isSuccess()) { 228 callback->sendSuccess(std::move(result), std::move(exceptionDetails)); 229 return true; 230 } 231 callback->sendFailure(response); 232 return false; 233 } 234 235 Response ensureContext(V8InspectorImpl* inspector, int contextGroupId, 236 Maybe<int> executionContextId, int* contextId) { 237 if (executionContextId.isJust()) { 238 *contextId = executionContextId.fromJust(); 239 } else { 240 v8::HandleScope handles(inspector->isolate()); 241 v8::Local<v8::Context> defaultContext = 242 inspector->client()->ensureDefaultContextInGroup(contextGroupId); 243 if (defaultContext.IsEmpty()) 244 return Response::Error("Cannot find default execution context"); 245 *contextId = InspectedContext::contextId(defaultContext); 246 } 247 return Response::OK(); 248 } 249 250 } // namespace 251 252 V8RuntimeAgentImpl::V8RuntimeAgentImpl( 253 V8InspectorSessionImpl* session, protocol::FrontendChannel* FrontendChannel, 254 protocol::DictionaryValue* state) 255 : m_session(session), 256 m_state(state), 257 m_frontend(FrontendChannel), 258 m_inspector(session->inspector()), 259 m_enabled(false) {} 260 261 V8RuntimeAgentImpl::~V8RuntimeAgentImpl() {} 262 263 void V8RuntimeAgentImpl::evaluate( 264 const String16& expression, Maybe<String16> objectGroup, 265 Maybe<bool> includeCommandLineAPI, Maybe<bool> silent, 266 Maybe<int> executionContextId, Maybe<bool> returnByValue, 267 Maybe<bool> generatePreview, Maybe<bool> userGesture, 268 Maybe<bool> awaitPromise, std::unique_ptr<EvaluateCallback> callback) { 269 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), 270 "EvaluateScript"); 271 int contextId = 0; 272 Response response = ensureContext(m_inspector, m_session->contextGroupId(), 273 std::move(executionContextId), &contextId); 274 if (!response.isSuccess()) { 275 callback->sendFailure(response); 276 return; 277 } 278 279 InjectedScript::ContextScope scope(m_inspector, m_session->contextGroupId(), 280 contextId); 281 response = scope.initialize(); 282 if (!response.isSuccess()) { 283 callback->sendFailure(response); 284 return; 285 } 286 287 if (silent.fromMaybe(false)) scope.ignoreExceptionsAndMuteConsole(); 288 if (userGesture.fromMaybe(false)) scope.pretendUserGesture(); 289 290 if (includeCommandLineAPI.fromMaybe(false)) scope.installCommandLineAPI(); 291 292 bool evalIsDisabled = !scope.context()->IsCodeGenerationFromStringsAllowed(); 293 // Temporarily enable allow evals for inspector. 294 if (evalIsDisabled) scope.context()->AllowCodeGenerationFromStrings(true); 295 296 v8::MaybeLocal<v8::Value> maybeResultValue; 297 v8::Local<v8::Script> script; 298 if (m_inspector->compileScript(scope.context(), expression, String16()) 299 .ToLocal(&script)) { 300 maybeResultValue = m_inspector->runCompiledScript(scope.context(), script); 301 } 302 303 if (evalIsDisabled) scope.context()->AllowCodeGenerationFromStrings(false); 304 305 // Re-initialize after running client's code, as it could have destroyed 306 // context or session. 307 response = scope.initialize(); 308 if (!response.isSuccess()) { 309 callback->sendFailure(response); 310 return; 311 } 312 313 if (!awaitPromise.fromMaybe(false) || scope.tryCatch().HasCaught()) { 314 wrapEvaluateResultAsync(scope.injectedScript(), maybeResultValue, 315 scope.tryCatch(), objectGroup.fromMaybe(""), 316 returnByValue.fromMaybe(false), 317 generatePreview.fromMaybe(false), callback.get()); 318 return; 319 } 320 ProtocolPromiseHandler<EvaluateCallback>::add( 321 m_inspector, scope.context(), maybeResultValue, 322 "Result of the evaluation is not a promise", m_session->contextGroupId(), 323 scope.injectedScript()->context()->contextId(), objectGroup.fromMaybe(""), 324 returnByValue.fromMaybe(false), generatePreview.fromMaybe(false), 325 std::move(callback)); 326 } 327 328 void V8RuntimeAgentImpl::awaitPromise( 329 const String16& promiseObjectId, Maybe<bool> returnByValue, 330 Maybe<bool> generatePreview, 331 std::unique_ptr<AwaitPromiseCallback> callback) { 332 InjectedScript::ObjectScope scope(m_inspector, m_session->contextGroupId(), 333 promiseObjectId); 334 Response response = scope.initialize(); 335 if (!response.isSuccess()) { 336 callback->sendFailure(response); 337 return; 338 } 339 ProtocolPromiseHandler<AwaitPromiseCallback>::add( 340 m_inspector, scope.context(), scope.object(), 341 "Could not find promise with given id", m_session->contextGroupId(), 342 scope.injectedScript()->context()->contextId(), scope.objectGroupName(), 343 returnByValue.fromMaybe(false), generatePreview.fromMaybe(false), 344 std::move(callback)); 345 } 346 347 void V8RuntimeAgentImpl::callFunctionOn( 348 const String16& objectId, const String16& expression, 349 Maybe<protocol::Array<protocol::Runtime::CallArgument>> optionalArguments, 350 Maybe<bool> silent, Maybe<bool> returnByValue, Maybe<bool> generatePreview, 351 Maybe<bool> userGesture, Maybe<bool> awaitPromise, 352 std::unique_ptr<CallFunctionOnCallback> callback) { 353 InjectedScript::ObjectScope scope(m_inspector, m_session->contextGroupId(), 354 objectId); 355 Response response = scope.initialize(); 356 if (!response.isSuccess()) { 357 callback->sendFailure(response); 358 return; 359 } 360 361 std::unique_ptr<v8::Local<v8::Value>[]> argv = nullptr; 362 int argc = 0; 363 if (optionalArguments.isJust()) { 364 protocol::Array<protocol::Runtime::CallArgument>* arguments = 365 optionalArguments.fromJust(); 366 argc = static_cast<int>(arguments->length()); 367 argv.reset(new v8::Local<v8::Value>[argc]); 368 for (int i = 0; i < argc; ++i) { 369 v8::Local<v8::Value> argumentValue; 370 response = scope.injectedScript()->resolveCallArgument(arguments->get(i), 371 &argumentValue); 372 if (!response.isSuccess()) { 373 callback->sendFailure(response); 374 return; 375 } 376 argv[i] = argumentValue; 377 } 378 } 379 380 if (silent.fromMaybe(false)) scope.ignoreExceptionsAndMuteConsole(); 381 if (userGesture.fromMaybe(false)) scope.pretendUserGesture(); 382 383 v8::MaybeLocal<v8::Value> maybeFunctionValue; 384 v8::Local<v8::Script> functionScript; 385 if (m_inspector 386 ->compileScript(scope.context(), "(" + expression + ")", String16()) 387 .ToLocal(&functionScript)) { 388 maybeFunctionValue = 389 m_inspector->runCompiledScript(scope.context(), functionScript); 390 } 391 // Re-initialize after running client's code, as it could have destroyed 392 // context or session. 393 response = scope.initialize(); 394 if (!response.isSuccess()) { 395 callback->sendFailure(response); 396 return; 397 } 398 399 if (scope.tryCatch().HasCaught()) { 400 wrapEvaluateResultAsync(scope.injectedScript(), maybeFunctionValue, 401 scope.tryCatch(), scope.objectGroupName(), false, 402 false, callback.get()); 403 return; 404 } 405 406 v8::Local<v8::Value> functionValue; 407 if (!maybeFunctionValue.ToLocal(&functionValue) || 408 !functionValue->IsFunction()) { 409 callback->sendFailure( 410 Response::Error("Given expression does not evaluate to a function")); 411 return; 412 } 413 414 v8::MaybeLocal<v8::Value> maybeResultValue = m_inspector->callFunction( 415 functionValue.As<v8::Function>(), scope.context(), scope.object(), argc, 416 argv.get()); 417 // Re-initialize after running client's code, as it could have destroyed 418 // context or session. 419 response = scope.initialize(); 420 if (!response.isSuccess()) { 421 callback->sendFailure(response); 422 return; 423 } 424 425 if (!awaitPromise.fromMaybe(false) || scope.tryCatch().HasCaught()) { 426 wrapEvaluateResultAsync(scope.injectedScript(), maybeResultValue, 427 scope.tryCatch(), scope.objectGroupName(), 428 returnByValue.fromMaybe(false), 429 generatePreview.fromMaybe(false), callback.get()); 430 return; 431 } 432 433 ProtocolPromiseHandler<CallFunctionOnCallback>::add( 434 m_inspector, scope.context(), maybeResultValue, 435 "Result of the function call is not a promise", 436 m_session->contextGroupId(), 437 scope.injectedScript()->context()->contextId(), scope.objectGroupName(), 438 returnByValue.fromMaybe(false), generatePreview.fromMaybe(false), 439 std::move(callback)); 440 } 441 442 Response V8RuntimeAgentImpl::getProperties( 443 const String16& objectId, Maybe<bool> ownProperties, 444 Maybe<bool> accessorPropertiesOnly, Maybe<bool> generatePreview, 445 std::unique_ptr<protocol::Array<protocol::Runtime::PropertyDescriptor>>* 446 result, 447 Maybe<protocol::Array<protocol::Runtime::InternalPropertyDescriptor>>* 448 internalProperties, 449 Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) { 450 using protocol::Runtime::InternalPropertyDescriptor; 451 452 InjectedScript::ObjectScope scope(m_inspector, m_session->contextGroupId(), 453 objectId); 454 Response response = scope.initialize(); 455 if (!response.isSuccess()) return response; 456 457 scope.ignoreExceptionsAndMuteConsole(); 458 if (!scope.object()->IsObject()) 459 return Response::Error("Value with given id is not an object"); 460 461 v8::Local<v8::Object> object = scope.object().As<v8::Object>(); 462 response = scope.injectedScript()->getProperties( 463 object, scope.objectGroupName(), ownProperties.fromMaybe(false), 464 accessorPropertiesOnly.fromMaybe(false), generatePreview.fromMaybe(false), 465 result, exceptionDetails); 466 if (!response.isSuccess()) return response; 467 if (exceptionDetails->isJust() || accessorPropertiesOnly.fromMaybe(false)) 468 return Response::OK(); 469 v8::Local<v8::Array> propertiesArray; 470 if (!m_inspector->debugger() 471 ->internalProperties(scope.context(), scope.object()) 472 .ToLocal(&propertiesArray)) { 473 return Response::InternalError(); 474 } 475 std::unique_ptr<protocol::Array<InternalPropertyDescriptor>> 476 propertiesProtocolArray = 477 protocol::Array<InternalPropertyDescriptor>::create(); 478 for (uint32_t i = 0; i < propertiesArray->Length(); i += 2) { 479 v8::Local<v8::Value> name; 480 if (!propertiesArray->Get(scope.context(), i).ToLocal(&name) || 481 !name->IsString()) { 482 return Response::InternalError(); 483 } 484 v8::Local<v8::Value> value; 485 if (!propertiesArray->Get(scope.context(), i + 1).ToLocal(&value)) 486 return Response::InternalError(); 487 std::unique_ptr<RemoteObject> wrappedValue; 488 protocol::Response response = scope.injectedScript()->wrapObject( 489 value, scope.objectGroupName(), false, false, &wrappedValue); 490 if (!response.isSuccess()) return response; 491 propertiesProtocolArray->addItem( 492 InternalPropertyDescriptor::create() 493 .setName(toProtocolString(name.As<v8::String>())) 494 .setValue(std::move(wrappedValue)) 495 .build()); 496 } 497 if (propertiesProtocolArray->length()) 498 *internalProperties = std::move(propertiesProtocolArray); 499 return Response::OK(); 500 } 501 502 Response V8RuntimeAgentImpl::releaseObject(const String16& objectId) { 503 InjectedScript::ObjectScope scope(m_inspector, m_session->contextGroupId(), 504 objectId); 505 Response response = scope.initialize(); 506 if (!response.isSuccess()) return response; 507 scope.injectedScript()->releaseObject(objectId); 508 return Response::OK(); 509 } 510 511 Response V8RuntimeAgentImpl::releaseObjectGroup(const String16& objectGroup) { 512 m_session->releaseObjectGroup(objectGroup); 513 return Response::OK(); 514 } 515 516 Response V8RuntimeAgentImpl::runIfWaitingForDebugger() { 517 m_inspector->client()->runIfWaitingForDebugger(m_session->contextGroupId()); 518 return Response::OK(); 519 } 520 521 Response V8RuntimeAgentImpl::setCustomObjectFormatterEnabled(bool enabled) { 522 m_state->setBoolean(V8RuntimeAgentImplState::customObjectFormatterEnabled, 523 enabled); 524 m_session->setCustomObjectFormatterEnabled(enabled); 525 return Response::OK(); 526 } 527 528 Response V8RuntimeAgentImpl::discardConsoleEntries() { 529 V8ConsoleMessageStorage* storage = 530 m_inspector->ensureConsoleMessageStorage(m_session->contextGroupId()); 531 storage->clear(); 532 return Response::OK(); 533 } 534 535 Response V8RuntimeAgentImpl::compileScript( 536 const String16& expression, const String16& sourceURL, bool persistScript, 537 Maybe<int> executionContextId, Maybe<String16>* scriptId, 538 Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) { 539 if (!m_enabled) return Response::Error("Runtime agent is not enabled"); 540 541 int contextId = 0; 542 Response response = ensureContext(m_inspector, m_session->contextGroupId(), 543 std::move(executionContextId), &contextId); 544 if (!response.isSuccess()) return response; 545 InjectedScript::ContextScope scope(m_inspector, m_session->contextGroupId(), 546 contextId); 547 response = scope.initialize(); 548 if (!response.isSuccess()) return response; 549 550 if (!persistScript) m_inspector->debugger()->muteScriptParsedEvents(); 551 v8::Local<v8::Script> script; 552 bool isOk = m_inspector->compileScript(scope.context(), expression, sourceURL) 553 .ToLocal(&script); 554 if (!persistScript) m_inspector->debugger()->unmuteScriptParsedEvents(); 555 if (!isOk) { 556 if (scope.tryCatch().HasCaught()) { 557 response = scope.injectedScript()->createExceptionDetails( 558 scope.tryCatch(), String16(), false, exceptionDetails); 559 if (!response.isSuccess()) return response; 560 return Response::OK(); 561 } else { 562 return Response::Error("Script compilation failed"); 563 } 564 } 565 566 if (!persistScript) return Response::OK(); 567 568 String16 scriptValueId = 569 String16::fromInteger(script->GetUnboundScript()->GetId()); 570 std::unique_ptr<v8::Global<v8::Script>> global( 571 new v8::Global<v8::Script>(m_inspector->isolate(), script)); 572 m_compiledScripts[scriptValueId] = std::move(global); 573 *scriptId = scriptValueId; 574 return Response::OK(); 575 } 576 577 void V8RuntimeAgentImpl::runScript( 578 const String16& scriptId, Maybe<int> executionContextId, 579 Maybe<String16> objectGroup, Maybe<bool> silent, 580 Maybe<bool> includeCommandLineAPI, Maybe<bool> returnByValue, 581 Maybe<bool> generatePreview, Maybe<bool> awaitPromise, 582 std::unique_ptr<RunScriptCallback> callback) { 583 if (!m_enabled) { 584 callback->sendFailure(Response::Error("Runtime agent is not enabled")); 585 return; 586 } 587 588 auto it = m_compiledScripts.find(scriptId); 589 if (it == m_compiledScripts.end()) { 590 callback->sendFailure(Response::Error("No script with given id")); 591 return; 592 } 593 594 int contextId = 0; 595 Response response = ensureContext(m_inspector, m_session->contextGroupId(), 596 std::move(executionContextId), &contextId); 597 if (!response.isSuccess()) { 598 callback->sendFailure(response); 599 return; 600 } 601 602 InjectedScript::ContextScope scope(m_inspector, m_session->contextGroupId(), 603 contextId); 604 response = scope.initialize(); 605 if (!response.isSuccess()) { 606 callback->sendFailure(response); 607 return; 608 } 609 610 if (silent.fromMaybe(false)) scope.ignoreExceptionsAndMuteConsole(); 611 612 std::unique_ptr<v8::Global<v8::Script>> scriptWrapper = std::move(it->second); 613 m_compiledScripts.erase(it); 614 v8::Local<v8::Script> script = scriptWrapper->Get(m_inspector->isolate()); 615 if (script.IsEmpty()) { 616 callback->sendFailure(Response::Error("Script execution failed")); 617 return; 618 } 619 620 if (includeCommandLineAPI.fromMaybe(false)) scope.installCommandLineAPI(); 621 622 v8::MaybeLocal<v8::Value> maybeResultValue = 623 m_inspector->runCompiledScript(scope.context(), script); 624 625 // Re-initialize after running client's code, as it could have destroyed 626 // context or session. 627 response = scope.initialize(); 628 if (!response.isSuccess()) { 629 callback->sendFailure(response); 630 return; 631 } 632 633 if (!awaitPromise.fromMaybe(false) || scope.tryCatch().HasCaught()) { 634 wrapEvaluateResultAsync(scope.injectedScript(), maybeResultValue, 635 scope.tryCatch(), objectGroup.fromMaybe(""), 636 returnByValue.fromMaybe(false), 637 generatePreview.fromMaybe(false), callback.get()); 638 return; 639 } 640 ProtocolPromiseHandler<RunScriptCallback>::add( 641 m_inspector, scope.context(), maybeResultValue.ToLocalChecked(), 642 "Result of the script execution is not a promise", 643 m_session->contextGroupId(), 644 scope.injectedScript()->context()->contextId(), objectGroup.fromMaybe(""), 645 returnByValue.fromMaybe(false), generatePreview.fromMaybe(false), 646 std::move(callback)); 647 } 648 649 void V8RuntimeAgentImpl::restore() { 650 if (!m_state->booleanProperty(V8RuntimeAgentImplState::runtimeEnabled, false)) 651 return; 652 m_frontend.executionContextsCleared(); 653 enable(); 654 if (m_state->booleanProperty( 655 V8RuntimeAgentImplState::customObjectFormatterEnabled, false)) 656 m_session->setCustomObjectFormatterEnabled(true); 657 } 658 659 Response V8RuntimeAgentImpl::enable() { 660 if (m_enabled) return Response::OK(); 661 m_inspector->client()->beginEnsureAllContextsInGroup( 662 m_session->contextGroupId()); 663 m_enabled = true; 664 m_state->setBoolean(V8RuntimeAgentImplState::runtimeEnabled, true); 665 m_inspector->enableStackCapturingIfNeeded(); 666 m_session->reportAllContexts(this); 667 V8ConsoleMessageStorage* storage = 668 m_inspector->ensureConsoleMessageStorage(m_session->contextGroupId()); 669 for (const auto& message : storage->messages()) { 670 if (!reportMessage(message.get(), false)) break; 671 } 672 return Response::OK(); 673 } 674 675 Response V8RuntimeAgentImpl::disable() { 676 if (!m_enabled) return Response::OK(); 677 m_enabled = false; 678 m_state->setBoolean(V8RuntimeAgentImplState::runtimeEnabled, false); 679 m_inspector->disableStackCapturingIfNeeded(); 680 m_session->discardInjectedScripts(); 681 reset(); 682 m_inspector->client()->endEnsureAllContextsInGroup( 683 m_session->contextGroupId()); 684 return Response::OK(); 685 } 686 687 void V8RuntimeAgentImpl::reset() { 688 m_compiledScripts.clear(); 689 if (m_enabled) { 690 if (const V8InspectorImpl::ContextByIdMap* contexts = 691 m_inspector->contextGroup(m_session->contextGroupId())) { 692 for (auto& idContext : *contexts) idContext.second->setReported(false); 693 } 694 m_frontend.executionContextsCleared(); 695 } 696 } 697 698 void V8RuntimeAgentImpl::reportExecutionContextCreated( 699 InspectedContext* context) { 700 if (!m_enabled) return; 701 context->setReported(true); 702 std::unique_ptr<protocol::Runtime::ExecutionContextDescription> description = 703 protocol::Runtime::ExecutionContextDescription::create() 704 .setId(context->contextId()) 705 .setName(context->humanReadableName()) 706 .setOrigin(context->origin()) 707 .build(); 708 if (!context->auxData().isEmpty()) 709 description->setAuxData(protocol::DictionaryValue::cast( 710 protocol::StringUtil::parseJSON(context->auxData()))); 711 m_frontend.executionContextCreated(std::move(description)); 712 } 713 714 void V8RuntimeAgentImpl::reportExecutionContextDestroyed( 715 InspectedContext* context) { 716 if (m_enabled && context->isReported()) { 717 context->setReported(false); 718 m_frontend.executionContextDestroyed(context->contextId()); 719 } 720 } 721 722 void V8RuntimeAgentImpl::inspect( 723 std::unique_ptr<protocol::Runtime::RemoteObject> objectToInspect, 724 std::unique_ptr<protocol::DictionaryValue> hints) { 725 if (m_enabled) 726 m_frontend.inspectRequested(std::move(objectToInspect), std::move(hints)); 727 } 728 729 void V8RuntimeAgentImpl::messageAdded(V8ConsoleMessage* message) { 730 if (m_enabled) reportMessage(message, true); 731 } 732 733 bool V8RuntimeAgentImpl::reportMessage(V8ConsoleMessage* message, 734 bool generatePreview) { 735 message->reportToFrontend(&m_frontend, m_session, generatePreview); 736 m_frontend.flush(); 737 return m_inspector->hasConsoleMessageStorage(m_session->contextGroupId()); 738 } 739 740 } // namespace v8_inspector 741