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