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