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