1 /*
2 * Copyright (C) 2012 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/injected-script.h"
32
33 #include <cmath>
34 #include <unordered_set>
35
36 #include "../../third_party/inspector_protocol/crdtp/json.h"
37 #include "include/v8-container.h"
38 #include "include/v8-context.h"
39 #include "include/v8-function.h"
40 #include "include/v8-inspector.h"
41 #include "include/v8-microtask-queue.h"
42 #include "src/debug/debug-interface.h"
43 #include "src/inspector/custom-preview.h"
44 #include "src/inspector/inspected-context.h"
45 #include "src/inspector/protocol/Protocol.h"
46 #include "src/inspector/remote-object-id.h"
47 #include "src/inspector/string-util.h"
48 #include "src/inspector/v8-console.h"
49 #include "src/inspector/v8-inspector-impl.h"
50 #include "src/inspector/v8-inspector-session-impl.h"
51 #include "src/inspector/v8-stack-trace-impl.h"
52 #include "src/inspector/v8-value-utils.h"
53 #include "src/inspector/value-mirror.h"
54
55 namespace v8_inspector {
56
57 namespace {
58 const char kGlobalHandleLabel[] = "DevTools console";
isResolvableNumberLike(String16 query)59 bool isResolvableNumberLike(String16 query) {
60 return query == "Infinity" || query == "-Infinity" || query == "NaN";
61 }
62 } // namespace
63
64 using protocol::Array;
65 using protocol::Maybe;
66 using protocol::Runtime::InternalPropertyDescriptor;
67 using protocol::Runtime::PrivatePropertyDescriptor;
68 using protocol::Runtime::PropertyDescriptor;
69 using protocol::Runtime::RemoteObject;
70
71 class InjectedScript::ProtocolPromiseHandler {
72 public:
add(V8InspectorSessionImpl * session,v8::Local<v8::Context> context,v8::Local<v8::Value> value,int executionContextId,const String16 & objectGroup,WrapMode wrapMode,bool replMode,EvaluateCallback * callback)73 static bool add(V8InspectorSessionImpl* session,
74 v8::Local<v8::Context> context, v8::Local<v8::Value> value,
75 int executionContextId, const String16& objectGroup,
76 WrapMode wrapMode, bool replMode,
77 EvaluateCallback* callback) {
78 v8::Local<v8::Promise::Resolver> resolver;
79 if (!v8::Promise::Resolver::New(context).ToLocal(&resolver)) {
80 callback->sendFailure(Response::InternalError());
81 return false;
82 }
83 if (!resolver->Resolve(context, value).FromMaybe(false)) {
84 callback->sendFailure(Response::InternalError());
85 return false;
86 }
87
88 v8::MaybeLocal<v8::Promise> originalPromise =
89 value->IsPromise() ? value.As<v8::Promise>()
90 : v8::MaybeLocal<v8::Promise>();
91 V8InspectorImpl* inspector = session->inspector();
92 ProtocolPromiseHandler* handler = new ProtocolPromiseHandler(
93 session, executionContextId, objectGroup, wrapMode, replMode, callback,
94 originalPromise);
95 v8::Local<v8::Value> wrapper = handler->m_wrapper.Get(inspector->isolate());
96 v8::Local<v8::Function> thenCallbackFunction =
97 v8::Function::New(context, thenCallback, wrapper, 0,
98 v8::ConstructorBehavior::kThrow)
99 .ToLocalChecked();
100 v8::Local<v8::Function> catchCallbackFunction =
101 v8::Function::New(context, catchCallback, wrapper, 0,
102 v8::ConstructorBehavior::kThrow)
103 .ToLocalChecked();
104 v8::Local<v8::Promise> promise = resolver->GetPromise();
105 if (promise->Then(context, thenCallbackFunction, catchCallbackFunction)
106 .IsEmpty()) {
107 callback->sendFailure(Response::InternalError());
108 return false;
109 }
110 return true;
111 }
112
113 private:
GetDotReplResultString(v8::Isolate * isolate)114 static v8::Local<v8::String> GetDotReplResultString(v8::Isolate* isolate) {
115 // TODO(szuend): Cache the string in a v8::Persistent handle.
116 return v8::String::NewFromOneByte(
117 isolate, reinterpret_cast<const uint8_t*>(".repl_result"))
118 .ToLocalChecked();
119 }
120
thenCallback(const v8::FunctionCallbackInfo<v8::Value> & info)121 static void thenCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
122 ProtocolPromiseHandler* handler = static_cast<ProtocolPromiseHandler*>(
123 info.Data().As<v8::External>()->Value());
124 DCHECK(handler);
125 v8::Local<v8::Value> value =
126 info.Length() > 0 ? info[0]
127 : v8::Undefined(info.GetIsolate()).As<v8::Value>();
128 handler->thenCallback(value);
129 delete handler;
130 }
131
catchCallback(const v8::FunctionCallbackInfo<v8::Value> & info)132 static void catchCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
133 ProtocolPromiseHandler* handler = static_cast<ProtocolPromiseHandler*>(
134 info.Data().As<v8::External>()->Value());
135 DCHECK(handler);
136 v8::Local<v8::Value> value =
137 info.Length() > 0 ? info[0]
138 : v8::Undefined(info.GetIsolate()).As<v8::Value>();
139 handler->catchCallback(value);
140 delete handler;
141 }
142
ProtocolPromiseHandler(V8InspectorSessionImpl * session,int executionContextId,const String16 & objectGroup,WrapMode wrapMode,bool replMode,EvaluateCallback * callback,v8::MaybeLocal<v8::Promise> maybeEvaluationResult)143 ProtocolPromiseHandler(V8InspectorSessionImpl* session,
144 int executionContextId, const String16& objectGroup,
145 WrapMode wrapMode, bool replMode,
146 EvaluateCallback* callback,
147 v8::MaybeLocal<v8::Promise> maybeEvaluationResult)
148 : m_inspector(session->inspector()),
149 m_sessionId(session->sessionId()),
150 m_contextGroupId(session->contextGroupId()),
151 m_executionContextId(executionContextId),
152 m_objectGroup(objectGroup),
153 m_wrapMode(wrapMode),
154 m_replMode(replMode),
155 m_callback(std::move(callback)),
156 m_wrapper(m_inspector->isolate(),
157 v8::External::New(m_inspector->isolate(), this)) {
158 m_wrapper.SetWeak(this, cleanup, v8::WeakCallbackType::kParameter);
159 v8::Local<v8::Promise> promise;
160 if (maybeEvaluationResult.ToLocal(&promise)) {
161 m_evaluationResult =
162 v8::Global<v8::Promise>(m_inspector->isolate(), promise);
163 }
164 }
165
cleanup(const v8::WeakCallbackInfo<ProtocolPromiseHandler> & data)166 static void cleanup(
167 const v8::WeakCallbackInfo<ProtocolPromiseHandler>& data) {
168 if (!data.GetParameter()->m_wrapper.IsEmpty()) {
169 data.GetParameter()->m_wrapper.Reset();
170 data.GetParameter()->m_evaluationResult.Reset();
171 data.SetSecondPassCallback(cleanup);
172 } else {
173 data.GetParameter()->sendPromiseCollected();
174 delete data.GetParameter();
175 }
176 }
177
thenCallback(v8::Local<v8::Value> value)178 void thenCallback(v8::Local<v8::Value> value) {
179 V8InspectorSessionImpl* session =
180 m_inspector->sessionById(m_contextGroupId, m_sessionId);
181 if (!session) return;
182 InjectedScript::ContextScope scope(session, m_executionContextId);
183 Response response = scope.initialize();
184 if (!response.IsSuccess()) return;
185
186 std::unique_ptr<EvaluateCallback> callback =
187 scope.injectedScript()->takeEvaluateCallback(m_callback);
188 if (!callback) return;
189
190 // In REPL mode the result is additionally wrapped in an object.
191 // The evaluation result can be found at ".repl_result".
192 v8::Local<v8::Value> result = value;
193 if (m_replMode) {
194 v8::Local<v8::Object> object;
195 if (!result->ToObject(scope.context()).ToLocal(&object)) {
196 callback->sendFailure(response);
197 return;
198 }
199
200 v8::Local<v8::String> name =
201 GetDotReplResultString(m_inspector->isolate());
202 if (!object->Get(scope.context(), name).ToLocal(&result)) {
203 callback->sendFailure(response);
204 return;
205 }
206 }
207
208 if (m_objectGroup == "console") {
209 scope.injectedScript()->setLastEvaluationResult(result);
210 }
211
212 std::unique_ptr<protocol::Runtime::RemoteObject> wrappedValue;
213 response = scope.injectedScript()->wrapObject(result, m_objectGroup,
214 m_wrapMode, &wrappedValue);
215 if (!response.IsSuccess()) {
216 callback->sendFailure(response);
217 return;
218 }
219 callback->sendSuccess(std::move(wrappedValue),
220 Maybe<protocol::Runtime::ExceptionDetails>());
221 }
222
catchCallback(v8::Local<v8::Value> result)223 void catchCallback(v8::Local<v8::Value> result) {
224 V8InspectorSessionImpl* session =
225 m_inspector->sessionById(m_contextGroupId, m_sessionId);
226 if (!session) return;
227 InjectedScript::ContextScope scope(session, m_executionContextId);
228 Response response = scope.initialize();
229 if (!response.IsSuccess()) return;
230 std::unique_ptr<EvaluateCallback> callback =
231 scope.injectedScript()->takeEvaluateCallback(m_callback);
232 if (!callback) return;
233 std::unique_ptr<protocol::Runtime::RemoteObject> wrappedValue;
234 response = scope.injectedScript()->wrapObject(result, m_objectGroup,
235 m_wrapMode, &wrappedValue);
236 if (!response.IsSuccess()) {
237 callback->sendFailure(response);
238 return;
239 }
240 v8::Isolate* isolate = session->inspector()->isolate();
241
242 v8::MaybeLocal<v8::Message> maybeMessage =
243 m_evaluationResult.IsEmpty()
244 ? v8::MaybeLocal<v8::Message>()
245 : v8::debug::GetMessageFromPromise(m_evaluationResult.Get(isolate));
246 v8::Local<v8::Message> message;
247 // In case a MessageObject was attached to the rejected promise, we
248 // construct the exception details from the message object. Otherwise
249 // we try to capture a fresh stack trace.
250 if (maybeMessage.ToLocal(&message)) {
251 v8::Local<v8::Value> exception = result;
252 session->inspector()->client()->dispatchError(scope.context(), message,
253 exception);
254 protocol::PtrMaybe<protocol::Runtime::ExceptionDetails> exceptionDetails;
255 response = scope.injectedScript()->createExceptionDetails(
256 message, exception, m_objectGroup, &exceptionDetails);
257 if (!response.IsSuccess()) {
258 callback->sendFailure(response);
259 return;
260 }
261
262 callback->sendSuccess(std::move(wrappedValue),
263 std::move(exceptionDetails));
264 return;
265 }
266
267 String16 messageString;
268 std::unique_ptr<V8StackTraceImpl> stack;
269 if (result->IsNativeError()) {
270 messageString =
271 " " +
272 toProtocolString(isolate,
273 result->ToDetailString(isolate->GetCurrentContext())
274 .ToLocalChecked());
275 v8::Local<v8::StackTrace> stackTrace =
276 v8::Exception::GetStackTrace(result);
277 if (!stackTrace.IsEmpty()) {
278 stack = m_inspector->debugger()->createStackTrace(stackTrace);
279 }
280 }
281 if (!stack) {
282 stack = m_inspector->debugger()->captureStackTrace(true);
283 }
284
285 // REPL mode implicitly handles the script like an async function.
286 // Do not prepend the '(in promise)' prefix for these exceptions since that
287 // would be confusing for the user. The stringified error is part of the
288 // exception and does not need to be added in REPL mode, otherwise it would
289 // be printed twice.
290 String16 exceptionDetailsText =
291 m_replMode ? "Uncaught" : "Uncaught (in promise)" + messageString;
292 std::unique_ptr<protocol::Runtime::ExceptionDetails> exceptionDetails =
293 protocol::Runtime::ExceptionDetails::create()
294 .setExceptionId(m_inspector->nextExceptionId())
295 .setText(exceptionDetailsText)
296 .setLineNumber(stack && !stack->isEmpty() ? stack->topLineNumber()
297 : 0)
298 .setColumnNumber(
299 stack && !stack->isEmpty() ? stack->topColumnNumber() : 0)
300 .build();
301 response = scope.injectedScript()->addExceptionToDetails(
302 result, exceptionDetails.get(), m_objectGroup);
303 if (!response.IsSuccess()) {
304 callback->sendFailure(response);
305 return;
306 }
307 if (stack)
308 exceptionDetails->setStackTrace(
309 stack->buildInspectorObjectImpl(m_inspector->debugger()));
310 if (stack && !stack->isEmpty())
311 exceptionDetails->setScriptId(
312 String16::fromInteger(stack->topScriptId()));
313 callback->sendSuccess(std::move(wrappedValue), std::move(exceptionDetails));
314 }
315
sendPromiseCollected()316 void sendPromiseCollected() {
317 V8InspectorSessionImpl* session =
318 m_inspector->sessionById(m_contextGroupId, m_sessionId);
319 if (!session) return;
320 InjectedScript::ContextScope scope(session, m_executionContextId);
321 Response response = scope.initialize();
322 if (!response.IsSuccess()) return;
323 std::unique_ptr<EvaluateCallback> callback =
324 scope.injectedScript()->takeEvaluateCallback(m_callback);
325 if (!callback) return;
326 callback->sendFailure(Response::ServerError("Promise was collected"));
327 }
328
329 V8InspectorImpl* m_inspector;
330 int m_sessionId;
331 int m_contextGroupId;
332 int m_executionContextId;
333 String16 m_objectGroup;
334 WrapMode m_wrapMode;
335 bool m_replMode;
336 EvaluateCallback* m_callback;
337 v8::Global<v8::External> m_wrapper;
338 v8::Global<v8::Promise> m_evaluationResult;
339 };
340
InjectedScript(InspectedContext * context,int sessionId)341 InjectedScript::InjectedScript(InspectedContext* context, int sessionId)
342 : m_context(context), m_sessionId(sessionId) {}
343
~InjectedScript()344 InjectedScript::~InjectedScript() { discardEvaluateCallbacks(); }
345
346 namespace {
347 class PropertyAccumulator : public ValueMirror::PropertyAccumulator {
348 public:
PropertyAccumulator(std::vector<PropertyMirror> * mirrors)349 explicit PropertyAccumulator(std::vector<PropertyMirror>* mirrors)
350 : m_mirrors(mirrors) {}
Add(PropertyMirror mirror)351 bool Add(PropertyMirror mirror) override {
352 m_mirrors->push_back(std::move(mirror));
353 return true;
354 }
355
356 private:
357 std::vector<PropertyMirror>* m_mirrors;
358 };
359 } // anonymous namespace
360
getProperties(v8::Local<v8::Object> object,const String16 & groupName,bool ownProperties,bool accessorPropertiesOnly,bool nonIndexedPropertiesOnly,WrapMode wrapMode,std::unique_ptr<Array<PropertyDescriptor>> * properties,Maybe<protocol::Runtime::ExceptionDetails> * exceptionDetails)361 Response InjectedScript::getProperties(
362 v8::Local<v8::Object> object, const String16& groupName, bool ownProperties,
363 bool accessorPropertiesOnly, bool nonIndexedPropertiesOnly,
364 WrapMode wrapMode, std::unique_ptr<Array<PropertyDescriptor>>* properties,
365 Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
366 v8::HandleScope handles(m_context->isolate());
367 v8::Local<v8::Context> context = m_context->context();
368 v8::Isolate* isolate = m_context->isolate();
369 int sessionId = m_sessionId;
370 v8::TryCatch tryCatch(isolate);
371
372 *properties = std::make_unique<Array<PropertyDescriptor>>();
373 std::vector<PropertyMirror> mirrors;
374 PropertyAccumulator accumulator(&mirrors);
375 if (!ValueMirror::getProperties(context, object, ownProperties,
376 accessorPropertiesOnly,
377 nonIndexedPropertiesOnly, &accumulator)) {
378 return createExceptionDetails(tryCatch, groupName, exceptionDetails);
379 }
380 for (const PropertyMirror& mirror : mirrors) {
381 std::unique_ptr<PropertyDescriptor> descriptor =
382 PropertyDescriptor::create()
383 .setName(mirror.name)
384 .setConfigurable(mirror.configurable)
385 .setEnumerable(mirror.enumerable)
386 .setIsOwn(mirror.isOwn)
387 .build();
388 std::unique_ptr<RemoteObject> remoteObject;
389 if (mirror.value) {
390 Response response = wrapObjectMirror(
391 *mirror.value, groupName, wrapMode, v8::MaybeLocal<v8::Value>(),
392 kMaxCustomPreviewDepth, &remoteObject);
393 if (!response.IsSuccess()) return response;
394 descriptor->setValue(std::move(remoteObject));
395 descriptor->setWritable(mirror.writable);
396 }
397 if (mirror.getter) {
398 Response response =
399 mirror.getter->buildRemoteObject(context, wrapMode, &remoteObject);
400 if (!response.IsSuccess()) return response;
401 response =
402 bindRemoteObjectIfNeeded(sessionId, context, mirror.getter->v8Value(),
403 groupName, remoteObject.get());
404 if (!response.IsSuccess()) return response;
405 descriptor->setGet(std::move(remoteObject));
406 }
407 if (mirror.setter) {
408 Response response =
409 mirror.setter->buildRemoteObject(context, wrapMode, &remoteObject);
410 if (!response.IsSuccess()) return response;
411 response =
412 bindRemoteObjectIfNeeded(sessionId, context, mirror.setter->v8Value(),
413 groupName, remoteObject.get());
414 if (!response.IsSuccess()) return response;
415 descriptor->setSet(std::move(remoteObject));
416 }
417 if (mirror.symbol) {
418 Response response =
419 mirror.symbol->buildRemoteObject(context, wrapMode, &remoteObject);
420 if (!response.IsSuccess()) return response;
421 response =
422 bindRemoteObjectIfNeeded(sessionId, context, mirror.symbol->v8Value(),
423 groupName, remoteObject.get());
424 if (!response.IsSuccess()) return response;
425 descriptor->setSymbol(std::move(remoteObject));
426 }
427 if (mirror.exception) {
428 Response response =
429 mirror.exception->buildRemoteObject(context, wrapMode, &remoteObject);
430 if (!response.IsSuccess()) return response;
431 response = bindRemoteObjectIfNeeded(sessionId, context,
432 mirror.exception->v8Value(),
433 groupName, remoteObject.get());
434 if (!response.IsSuccess()) return response;
435 descriptor->setValue(std::move(remoteObject));
436 descriptor->setWasThrown(true);
437 }
438 (*properties)->emplace_back(std::move(descriptor));
439 }
440 return Response::Success();
441 }
442
getInternalAndPrivateProperties(v8::Local<v8::Value> value,const String16 & groupName,bool accessorPropertiesOnly,std::unique_ptr<protocol::Array<InternalPropertyDescriptor>> * internalProperties,std::unique_ptr<protocol::Array<PrivatePropertyDescriptor>> * privateProperties)443 Response InjectedScript::getInternalAndPrivateProperties(
444 v8::Local<v8::Value> value, const String16& groupName,
445 bool accessorPropertiesOnly,
446 std::unique_ptr<protocol::Array<InternalPropertyDescriptor>>*
447 internalProperties,
448 std::unique_ptr<protocol::Array<PrivatePropertyDescriptor>>*
449 privateProperties) {
450 *internalProperties = std::make_unique<Array<InternalPropertyDescriptor>>();
451 *privateProperties = std::make_unique<Array<PrivatePropertyDescriptor>>();
452
453 if (!value->IsObject()) return Response::Success();
454
455 v8::Local<v8::Object> value_obj = value.As<v8::Object>();
456
457 v8::Local<v8::Context> context = m_context->context();
458 int sessionId = m_sessionId;
459
460 if (!accessorPropertiesOnly) {
461 std::vector<InternalPropertyMirror> internalPropertiesWrappers;
462 ValueMirror::getInternalProperties(m_context->context(), value_obj,
463 &internalPropertiesWrappers);
464 for (const auto& internalProperty : internalPropertiesWrappers) {
465 std::unique_ptr<RemoteObject> remoteObject;
466 Response response = internalProperty.value->buildRemoteObject(
467 m_context->context(), WrapMode::kNoPreview, &remoteObject);
468 if (!response.IsSuccess()) return response;
469 response = bindRemoteObjectIfNeeded(sessionId, context,
470 internalProperty.value->v8Value(),
471 groupName, remoteObject.get());
472 if (!response.IsSuccess()) return response;
473 (*internalProperties)
474 ->emplace_back(InternalPropertyDescriptor::create()
475 .setName(internalProperty.name)
476 .setValue(std::move(remoteObject))
477 .build());
478 }
479 }
480
481 std::vector<PrivatePropertyMirror> privatePropertyWrappers =
482 ValueMirror::getPrivateProperties(context, value_obj,
483 accessorPropertiesOnly);
484 for (const auto& privateProperty : privatePropertyWrappers) {
485 std::unique_ptr<PrivatePropertyDescriptor> descriptor =
486 PrivatePropertyDescriptor::create()
487 .setName(privateProperty.name)
488 .build();
489
490 std::unique_ptr<RemoteObject> remoteObject;
491 DCHECK((privateProperty.getter || privateProperty.setter) ^
492 (!!privateProperty.value));
493 if (privateProperty.value) {
494 Response response = privateProperty.value->buildRemoteObject(
495 context, WrapMode::kNoPreview, &remoteObject);
496 if (!response.IsSuccess()) return response;
497 response = bindRemoteObjectIfNeeded(sessionId, context,
498 privateProperty.value->v8Value(),
499 groupName, remoteObject.get());
500 if (!response.IsSuccess()) return response;
501 descriptor->setValue(std::move(remoteObject));
502 }
503
504 if (privateProperty.getter) {
505 Response response = privateProperty.getter->buildRemoteObject(
506 context, WrapMode::kNoPreview, &remoteObject);
507 if (!response.IsSuccess()) return response;
508 response = bindRemoteObjectIfNeeded(sessionId, context,
509 privateProperty.getter->v8Value(),
510 groupName, remoteObject.get());
511 if (!response.IsSuccess()) return response;
512 descriptor->setGet(std::move(remoteObject));
513 }
514
515 if (privateProperty.setter) {
516 Response response = privateProperty.setter->buildRemoteObject(
517 context, WrapMode::kNoPreview, &remoteObject);
518 if (!response.IsSuccess()) return response;
519 response = bindRemoteObjectIfNeeded(sessionId, context,
520 privateProperty.setter->v8Value(),
521 groupName, remoteObject.get());
522 if (!response.IsSuccess()) return response;
523 descriptor->setSet(std::move(remoteObject));
524 }
525
526 (*privateProperties)->emplace_back(std::move(descriptor));
527 }
528 return Response::Success();
529 }
530
releaseObject(const String16 & objectId)531 void InjectedScript::releaseObject(const String16& objectId) {
532 std::unique_ptr<RemoteObjectId> remoteId;
533 Response response = RemoteObjectId::parse(objectId, &remoteId);
534 if (response.IsSuccess()) unbindObject(remoteId->id());
535 }
536
wrapObject(v8::Local<v8::Value> value,const String16 & groupName,WrapMode wrapMode,std::unique_ptr<protocol::Runtime::RemoteObject> * result)537 Response InjectedScript::wrapObject(
538 v8::Local<v8::Value> value, const String16& groupName, WrapMode wrapMode,
539 std::unique_ptr<protocol::Runtime::RemoteObject>* result) {
540 return wrapObject(value, groupName, wrapMode, v8::MaybeLocal<v8::Value>(),
541 kMaxCustomPreviewDepth, result);
542 }
543
wrapObject(v8::Local<v8::Value> value,const String16 & groupName,WrapMode wrapMode,v8::MaybeLocal<v8::Value> customPreviewConfig,int maxCustomPreviewDepth,std::unique_ptr<protocol::Runtime::RemoteObject> * result)544 Response InjectedScript::wrapObject(
545 v8::Local<v8::Value> value, const String16& groupName, WrapMode wrapMode,
546 v8::MaybeLocal<v8::Value> customPreviewConfig, int maxCustomPreviewDepth,
547 std::unique_ptr<protocol::Runtime::RemoteObject>* result) {
548 v8::Local<v8::Context> context = m_context->context();
549 v8::Context::Scope contextScope(context);
550 std::unique_ptr<ValueMirror> mirror = ValueMirror::create(context, value);
551 if (!mirror) return Response::InternalError();
552 return wrapObjectMirror(*mirror, groupName, wrapMode, customPreviewConfig,
553 maxCustomPreviewDepth, result);
554 }
555
wrapObjectMirror(const ValueMirror & mirror,const String16 & groupName,WrapMode wrapMode,v8::MaybeLocal<v8::Value> customPreviewConfig,int maxCustomPreviewDepth,std::unique_ptr<protocol::Runtime::RemoteObject> * result)556 Response InjectedScript::wrapObjectMirror(
557 const ValueMirror& mirror, const String16& groupName, WrapMode wrapMode,
558 v8::MaybeLocal<v8::Value> customPreviewConfig, int maxCustomPreviewDepth,
559 std::unique_ptr<protocol::Runtime::RemoteObject>* result) {
560 int customPreviewEnabled = m_customPreviewEnabled;
561 int sessionId = m_sessionId;
562 v8::Local<v8::Context> context = m_context->context();
563 v8::Context::Scope contextScope(context);
564 Response response = mirror.buildRemoteObject(context, wrapMode, result);
565 if (!response.IsSuccess()) return response;
566 v8::Local<v8::Value> value = mirror.v8Value();
567 response = bindRemoteObjectIfNeeded(sessionId, context, value, groupName,
568 result->get());
569 if (!response.IsSuccess()) return response;
570 if (customPreviewEnabled && value->IsObject()) {
571 std::unique_ptr<protocol::Runtime::CustomPreview> customPreview;
572 generateCustomPreview(sessionId, groupName, value.As<v8::Object>(),
573 customPreviewConfig, maxCustomPreviewDepth,
574 &customPreview);
575 if (customPreview) (*result)->setCustomPreview(std::move(customPreview));
576 }
577 if (wrapMode == WrapMode::kGenerateWebDriverValue) {
578 int maxDepth = 1;
579 std::unique_ptr<protocol::Runtime::WebDriverValue> webDriverValue;
580 response = mirror.buildWebDriverValue(context, maxDepth, &webDriverValue);
581 if (!response.IsSuccess()) return response;
582 (*result)->setWebDriverValue(std::move(webDriverValue));
583 }
584
585 return Response::Success();
586 }
587
wrapTable(v8::Local<v8::Object> table,v8::MaybeLocal<v8::Array> maybeColumns)588 std::unique_ptr<protocol::Runtime::RemoteObject> InjectedScript::wrapTable(
589 v8::Local<v8::Object> table, v8::MaybeLocal<v8::Array> maybeColumns) {
590 using protocol::Runtime::RemoteObject;
591 using protocol::Runtime::ObjectPreview;
592 using protocol::Runtime::PropertyPreview;
593 using protocol::Array;
594
595 v8::Isolate* isolate = m_context->isolate();
596 v8::HandleScope handles(isolate);
597 v8::Local<v8::Context> context = m_context->context();
598
599 std::unique_ptr<RemoteObject> remoteObject;
600 Response response =
601 wrapObject(table, "console", WrapMode::kNoPreview, &remoteObject);
602 if (!remoteObject || !response.IsSuccess()) return nullptr;
603
604 auto mirror = ValueMirror::create(context, table);
605 std::unique_ptr<ObjectPreview> preview;
606 int limit = 1000;
607 mirror->buildObjectPreview(context, true /* generatePreviewForTable */,
608 &limit, &limit, &preview);
609 if (!preview) return nullptr;
610
611 std::vector<String16> selectedColumns;
612 std::unordered_set<String16> columnSet;
613 v8::Local<v8::Array> v8Columns;
614 if (maybeColumns.ToLocal(&v8Columns)) {
615 for (uint32_t i = 0; i < v8Columns->Length(); ++i) {
616 v8::Local<v8::Value> column;
617 if (v8Columns->Get(context, i).ToLocal(&column) && column->IsString()) {
618 String16 name = toProtocolString(isolate, column.As<v8::String>());
619 if (columnSet.find(name) == columnSet.end()) {
620 columnSet.insert(name);
621 selectedColumns.push_back(name);
622 }
623 }
624 }
625 }
626 if (!selectedColumns.empty()) {
627 for (const std::unique_ptr<PropertyPreview>& prop :
628 *preview->getProperties()) {
629 ObjectPreview* columnPreview = prop->getValuePreview(nullptr);
630 if (!columnPreview) continue;
631 // Use raw pointer here since the lifetime of each PropertyPreview is
632 // ensured by columnPreview. This saves an additional clone.
633 std::unordered_map<String16, PropertyPreview*> columnMap;
634 for (const std::unique_ptr<PropertyPreview>& property :
635 *columnPreview->getProperties()) {
636 if (columnSet.find(property->getName()) == columnSet.end()) continue;
637 columnMap[property->getName()] = property.get();
638 }
639 auto filtered = std::make_unique<Array<PropertyPreview>>();
640 for (const String16& column : selectedColumns) {
641 if (columnMap.find(column) == columnMap.end()) continue;
642 filtered->push_back(columnMap[column]->clone());
643 }
644 columnPreview->setProperties(std::move(filtered));
645 }
646 }
647 remoteObject->setPreview(std::move(preview));
648 return remoteObject;
649 }
650
addPromiseCallback(V8InspectorSessionImpl * session,v8::MaybeLocal<v8::Value> value,const String16 & objectGroup,WrapMode wrapMode,bool replMode,std::unique_ptr<EvaluateCallback> callback)651 void InjectedScript::addPromiseCallback(
652 V8InspectorSessionImpl* session, v8::MaybeLocal<v8::Value> value,
653 const String16& objectGroup, WrapMode wrapMode, bool replMode,
654 std::unique_ptr<EvaluateCallback> callback) {
655 if (value.IsEmpty()) {
656 callback->sendFailure(Response::InternalError());
657 return;
658 }
659 v8::MicrotasksScope microtasksScope(m_context->isolate(),
660 v8::MicrotasksScope::kRunMicrotasks);
661 if (ProtocolPromiseHandler::add(session, m_context->context(),
662 value.ToLocalChecked(),
663 m_context->contextId(), objectGroup, wrapMode,
664 replMode, callback.get())) {
665 m_evaluateCallbacks.insert(callback.release());
666 }
667 }
668
discardEvaluateCallbacks()669 void InjectedScript::discardEvaluateCallbacks() {
670 for (auto& callback : m_evaluateCallbacks) {
671 callback->sendFailure(
672 Response::ServerError("Execution context was destroyed."));
673 delete callback;
674 }
675 m_evaluateCallbacks.clear();
676 }
677
takeEvaluateCallback(EvaluateCallback * callback)678 std::unique_ptr<EvaluateCallback> InjectedScript::takeEvaluateCallback(
679 EvaluateCallback* callback) {
680 auto it = m_evaluateCallbacks.find(callback);
681 if (it == m_evaluateCallbacks.end()) return nullptr;
682 std::unique_ptr<EvaluateCallback> value(*it);
683 m_evaluateCallbacks.erase(it);
684 return value;
685 }
686
findObject(const RemoteObjectId & objectId,v8::Local<v8::Value> * outObject) const687 Response InjectedScript::findObject(const RemoteObjectId& objectId,
688 v8::Local<v8::Value>* outObject) const {
689 auto it = m_idToWrappedObject.find(objectId.id());
690 if (it == m_idToWrappedObject.end())
691 return Response::ServerError("Could not find object with given id");
692 *outObject = it->second.Get(m_context->isolate());
693 return Response::Success();
694 }
695
objectGroupName(const RemoteObjectId & objectId) const696 String16 InjectedScript::objectGroupName(const RemoteObjectId& objectId) const {
697 if (objectId.id() <= 0) return String16();
698 auto it = m_idToObjectGroupName.find(objectId.id());
699 return it != m_idToObjectGroupName.end() ? it->second : String16();
700 }
701
releaseObjectGroup(const String16 & objectGroup)702 void InjectedScript::releaseObjectGroup(const String16& objectGroup) {
703 if (objectGroup == "console") m_lastEvaluationResult.Reset();
704 if (objectGroup.isEmpty()) return;
705 auto it = m_nameToObjectGroup.find(objectGroup);
706 if (it == m_nameToObjectGroup.end()) return;
707 for (int id : it->second) unbindObject(id);
708 m_nameToObjectGroup.erase(it);
709 }
710
setCustomObjectFormatterEnabled(bool enabled)711 void InjectedScript::setCustomObjectFormatterEnabled(bool enabled) {
712 m_customPreviewEnabled = enabled;
713 }
714
lastEvaluationResult() const715 v8::Local<v8::Value> InjectedScript::lastEvaluationResult() const {
716 if (m_lastEvaluationResult.IsEmpty())
717 return v8::Undefined(m_context->isolate());
718 return m_lastEvaluationResult.Get(m_context->isolate());
719 }
720
setLastEvaluationResult(v8::Local<v8::Value> result)721 void InjectedScript::setLastEvaluationResult(v8::Local<v8::Value> result) {
722 m_lastEvaluationResult.Reset(m_context->isolate(), result);
723 m_lastEvaluationResult.AnnotateStrongRetainer(kGlobalHandleLabel);
724 }
725
resolveCallArgument(protocol::Runtime::CallArgument * callArgument,v8::Local<v8::Value> * result)726 Response InjectedScript::resolveCallArgument(
727 protocol::Runtime::CallArgument* callArgument,
728 v8::Local<v8::Value>* result) {
729 if (callArgument->hasObjectId()) {
730 std::unique_ptr<RemoteObjectId> remoteObjectId;
731 Response response =
732 RemoteObjectId::parse(callArgument->getObjectId(""), &remoteObjectId);
733 if (!response.IsSuccess()) return response;
734 if (remoteObjectId->contextId() != m_context->contextId() ||
735 remoteObjectId->isolateId() != m_context->inspector()->isolateId()) {
736 return Response::ServerError(
737 "Argument should belong to the same JavaScript world as target "
738 "object");
739 }
740 return findObject(*remoteObjectId, result);
741 }
742 if (callArgument->hasValue() || callArgument->hasUnserializableValue()) {
743 String16 value;
744 if (callArgument->hasValue()) {
745 std::vector<uint8_t> json;
746 v8_crdtp::json::ConvertCBORToJSON(
747 v8_crdtp::SpanFrom(callArgument->getValue(nullptr)->Serialize()),
748 &json);
749 value =
750 "(" +
751 String16(reinterpret_cast<const char*>(json.data()), json.size()) +
752 ")";
753 } else {
754 String16 unserializableValue = callArgument->getUnserializableValue("");
755 // Protect against potential identifier resolution for NaN and Infinity.
756 if (isResolvableNumberLike(unserializableValue))
757 value = "Number(\"" + unserializableValue + "\")";
758 else
759 value = unserializableValue;
760 }
761 if (!m_context->inspector()
762 ->compileAndRunInternalScript(
763 m_context->context(), toV8String(m_context->isolate(), value))
764 .ToLocal(result)) {
765 return Response::ServerError(
766 "Couldn't parse value object in call argument");
767 }
768 return Response::Success();
769 }
770 *result = v8::Undefined(m_context->isolate());
771 return Response::Success();
772 }
773
addExceptionToDetails(v8::Local<v8::Value> exception,protocol::Runtime::ExceptionDetails * exceptionDetails,const String16 & objectGroup)774 Response InjectedScript::addExceptionToDetails(
775 v8::Local<v8::Value> exception,
776 protocol::Runtime::ExceptionDetails* exceptionDetails,
777 const String16& objectGroup) {
778 if (exception.IsEmpty()) return Response::Success();
779 std::unique_ptr<protocol::Runtime::RemoteObject> wrapped;
780 Response response =
781 wrapObject(exception, objectGroup,
782 exception->IsNativeError() ? WrapMode::kNoPreview
783 : WrapMode::kWithPreview,
784 &wrapped);
785 if (!response.IsSuccess()) return response;
786 exceptionDetails->setException(std::move(wrapped));
787 return Response::Success();
788 }
789
createExceptionDetails(const v8::TryCatch & tryCatch,const String16 & objectGroup,Maybe<protocol::Runtime::ExceptionDetails> * result)790 Response InjectedScript::createExceptionDetails(
791 const v8::TryCatch& tryCatch, const String16& objectGroup,
792 Maybe<protocol::Runtime::ExceptionDetails>* result) {
793 if (!tryCatch.HasCaught()) return Response::InternalError();
794 v8::Local<v8::Message> message = tryCatch.Message();
795 v8::Local<v8::Value> exception = tryCatch.Exception();
796 return createExceptionDetails(message, exception, objectGroup, result);
797 }
798
createExceptionDetails(v8::Local<v8::Message> message,v8::Local<v8::Value> exception,const String16 & objectGroup,Maybe<protocol::Runtime::ExceptionDetails> * result)799 Response InjectedScript::createExceptionDetails(
800 v8::Local<v8::Message> message, v8::Local<v8::Value> exception,
801 const String16& objectGroup,
802 Maybe<protocol::Runtime::ExceptionDetails>* result) {
803 String16 messageText =
804 message.IsEmpty()
805 ? String16()
806 : toProtocolString(m_context->isolate(), message->Get());
807 std::unique_ptr<protocol::Runtime::ExceptionDetails> exceptionDetails =
808 protocol::Runtime::ExceptionDetails::create()
809 .setExceptionId(m_context->inspector()->nextExceptionId())
810 .setText(exception.IsEmpty() ? messageText : String16("Uncaught"))
811 .setLineNumber(
812 message.IsEmpty()
813 ? 0
814 : message->GetLineNumber(m_context->context()).FromMaybe(1) -
815 1)
816 .setColumnNumber(
817 message.IsEmpty()
818 ? 0
819 : message->GetStartColumn(m_context->context()).FromMaybe(0))
820 .build();
821 if (!message.IsEmpty()) {
822 exceptionDetails->setScriptId(
823 String16::fromInteger(message->GetScriptOrigin().ScriptId()));
824 v8::Local<v8::StackTrace> stackTrace = message->GetStackTrace();
825 if (!stackTrace.IsEmpty() && stackTrace->GetFrameCount() > 0) {
826 std::unique_ptr<V8StackTraceImpl> v8StackTrace =
827 m_context->inspector()->debugger()->createStackTrace(stackTrace);
828 if (v8StackTrace) {
829 exceptionDetails->setStackTrace(v8StackTrace->buildInspectorObjectImpl(
830 m_context->inspector()->debugger()));
831 }
832 }
833 }
834 Response response =
835 addExceptionToDetails(exception, exceptionDetails.get(), objectGroup);
836 if (!response.IsSuccess()) return response;
837 *result = std::move(exceptionDetails);
838 return Response::Success();
839 }
840
wrapEvaluateResult(v8::MaybeLocal<v8::Value> maybeResultValue,const v8::TryCatch & tryCatch,const String16 & objectGroup,WrapMode wrapMode,std::unique_ptr<protocol::Runtime::RemoteObject> * result,Maybe<protocol::Runtime::ExceptionDetails> * exceptionDetails)841 Response InjectedScript::wrapEvaluateResult(
842 v8::MaybeLocal<v8::Value> maybeResultValue, const v8::TryCatch& tryCatch,
843 const String16& objectGroup, WrapMode wrapMode,
844 std::unique_ptr<protocol::Runtime::RemoteObject>* result,
845 Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
846 v8::Local<v8::Value> resultValue;
847 if (!tryCatch.HasCaught()) {
848 if (!maybeResultValue.ToLocal(&resultValue))
849 return Response::InternalError();
850 Response response = wrapObject(resultValue, objectGroup, wrapMode, result);
851 if (!response.IsSuccess()) return response;
852 if (objectGroup == "console") {
853 m_lastEvaluationResult.Reset(m_context->isolate(), resultValue);
854 m_lastEvaluationResult.AnnotateStrongRetainer(kGlobalHandleLabel);
855 }
856 } else {
857 if (tryCatch.HasTerminated() || !tryCatch.CanContinue()) {
858 return Response::ServerError("Execution was terminated");
859 }
860 v8::Local<v8::Value> exception = tryCatch.Exception();
861 m_context->inspector()->client()->dispatchError(
862 m_context->context(), tryCatch.Message(), exception);
863 Response response =
864 wrapObject(exception, objectGroup,
865 exception->IsNativeError() ? WrapMode::kNoPreview
866 : WrapMode::kWithPreview,
867 result);
868 if (!response.IsSuccess()) return response;
869 // We send exception in result for compatibility reasons, even though it's
870 // accessible through exceptionDetails.exception.
871 response = createExceptionDetails(tryCatch, objectGroup, exceptionDetails);
872 if (!response.IsSuccess()) return response;
873 }
874 return Response::Success();
875 }
876
commandLineAPI()877 v8::Local<v8::Object> InjectedScript::commandLineAPI() {
878 if (m_commandLineAPI.IsEmpty()) {
879 v8::debug::DisableBreakScope disable_break(m_context->isolate());
880 m_commandLineAPI.Reset(
881 m_context->isolate(),
882 m_context->inspector()->console()->createCommandLineAPI(
883 m_context->context(), m_sessionId));
884 m_commandLineAPI.AnnotateStrongRetainer(kGlobalHandleLabel);
885 }
886 return m_commandLineAPI.Get(m_context->isolate());
887 }
888
Scope(V8InspectorSessionImpl * session)889 InjectedScript::Scope::Scope(V8InspectorSessionImpl* session)
890 : m_inspector(session->inspector()),
891 m_injectedScript(nullptr),
892 m_handleScope(m_inspector->isolate()),
893 m_tryCatch(m_inspector->isolate()),
894 m_ignoreExceptionsAndMuteConsole(false),
895 m_previousPauseOnExceptionsState(v8::debug::NoBreakOnException),
896 m_userGesture(false),
897 m_allowEval(false),
898 m_contextGroupId(session->contextGroupId()),
899 m_sessionId(session->sessionId()) {}
900
initialize()901 Response InjectedScript::Scope::initialize() {
902 cleanup();
903 V8InspectorSessionImpl* session =
904 m_inspector->sessionById(m_contextGroupId, m_sessionId);
905 if (!session) return Response::InternalError();
906 Response response = findInjectedScript(session);
907 if (!response.IsSuccess()) return response;
908 m_context = m_injectedScript->context()->context();
909 m_context->Enter();
910 if (m_allowEval) m_context->AllowCodeGenerationFromStrings(true);
911 return Response::Success();
912 }
913
installCommandLineAPI()914 void InjectedScript::Scope::installCommandLineAPI() {
915 DCHECK(m_injectedScript && !m_context.IsEmpty() &&
916 !m_commandLineAPIScope.get());
917 m_commandLineAPIScope.reset(new V8Console::CommandLineAPIScope(
918 m_context, m_injectedScript->commandLineAPI(), m_context->Global()));
919 }
920
ignoreExceptionsAndMuteConsole()921 void InjectedScript::Scope::ignoreExceptionsAndMuteConsole() {
922 DCHECK(!m_ignoreExceptionsAndMuteConsole);
923 m_ignoreExceptionsAndMuteConsole = true;
924 m_inspector->client()->muteMetrics(m_contextGroupId);
925 m_inspector->muteExceptions(m_contextGroupId);
926 m_previousPauseOnExceptionsState =
927 setPauseOnExceptionsState(v8::debug::NoBreakOnException);
928 }
929
setPauseOnExceptionsState(v8::debug::ExceptionBreakState newState)930 v8::debug::ExceptionBreakState InjectedScript::Scope::setPauseOnExceptionsState(
931 v8::debug::ExceptionBreakState newState) {
932 if (!m_inspector->debugger()->enabled()) return newState;
933 v8::debug::ExceptionBreakState presentState =
934 m_inspector->debugger()->getPauseOnExceptionsState();
935 if (presentState != newState)
936 m_inspector->debugger()->setPauseOnExceptionsState(newState);
937 return presentState;
938 }
939
pretendUserGesture()940 void InjectedScript::Scope::pretendUserGesture() {
941 DCHECK(!m_userGesture);
942 m_userGesture = true;
943 m_inspector->client()->beginUserGesture();
944 }
945
allowCodeGenerationFromStrings()946 void InjectedScript::Scope::allowCodeGenerationFromStrings() {
947 DCHECK(!m_allowEval);
948 if (m_context->IsCodeGenerationFromStringsAllowed()) return;
949 m_allowEval = true;
950 m_context->AllowCodeGenerationFromStrings(true);
951 }
952
cleanup()953 void InjectedScript::Scope::cleanup() {
954 m_commandLineAPIScope.reset();
955 if (!m_context.IsEmpty()) {
956 if (m_allowEval) m_context->AllowCodeGenerationFromStrings(false);
957 m_context->Exit();
958 m_context.Clear();
959 }
960 }
961
~Scope()962 InjectedScript::Scope::~Scope() {
963 if (m_ignoreExceptionsAndMuteConsole) {
964 setPauseOnExceptionsState(m_previousPauseOnExceptionsState);
965 m_inspector->client()->unmuteMetrics(m_contextGroupId);
966 m_inspector->unmuteExceptions(m_contextGroupId);
967 }
968 if (m_userGesture) m_inspector->client()->endUserGesture();
969 cleanup();
970 }
971
ContextScope(V8InspectorSessionImpl * session,int executionContextId)972 InjectedScript::ContextScope::ContextScope(V8InspectorSessionImpl* session,
973 int executionContextId)
974 : InjectedScript::Scope(session),
975 m_executionContextId(executionContextId) {}
976
977 InjectedScript::ContextScope::~ContextScope() = default;
978
findInjectedScript(V8InspectorSessionImpl * session)979 Response InjectedScript::ContextScope::findInjectedScript(
980 V8InspectorSessionImpl* session) {
981 return session->findInjectedScript(m_executionContextId, m_injectedScript);
982 }
983
ObjectScope(V8InspectorSessionImpl * session,const String16 & remoteObjectId)984 InjectedScript::ObjectScope::ObjectScope(V8InspectorSessionImpl* session,
985 const String16& remoteObjectId)
986 : InjectedScript::Scope(session), m_remoteObjectId(remoteObjectId) {}
987
988 InjectedScript::ObjectScope::~ObjectScope() = default;
989
findInjectedScript(V8InspectorSessionImpl * session)990 Response InjectedScript::ObjectScope::findInjectedScript(
991 V8InspectorSessionImpl* session) {
992 std::unique_ptr<RemoteObjectId> remoteId;
993 Response response = RemoteObjectId::parse(m_remoteObjectId, &remoteId);
994 if (!response.IsSuccess()) return response;
995 InjectedScript* injectedScript = nullptr;
996 response = session->findInjectedScript(remoteId.get(), injectedScript);
997 if (!response.IsSuccess()) return response;
998 m_objectGroupName = injectedScript->objectGroupName(*remoteId);
999 response = injectedScript->findObject(*remoteId, &m_object);
1000 if (!response.IsSuccess()) return response;
1001 m_injectedScript = injectedScript;
1002 return Response::Success();
1003 }
1004
CallFrameScope(V8InspectorSessionImpl * session,const String16 & remoteObjectId)1005 InjectedScript::CallFrameScope::CallFrameScope(V8InspectorSessionImpl* session,
1006 const String16& remoteObjectId)
1007 : InjectedScript::Scope(session), m_remoteCallFrameId(remoteObjectId) {}
1008
1009 InjectedScript::CallFrameScope::~CallFrameScope() = default;
1010
findInjectedScript(V8InspectorSessionImpl * session)1011 Response InjectedScript::CallFrameScope::findInjectedScript(
1012 V8InspectorSessionImpl* session) {
1013 std::unique_ptr<RemoteCallFrameId> remoteId;
1014 Response response = RemoteCallFrameId::parse(m_remoteCallFrameId, &remoteId);
1015 if (!response.IsSuccess()) return response;
1016 m_frameOrdinal = static_cast<size_t>(remoteId->frameOrdinal());
1017 return session->findInjectedScript(remoteId.get(), m_injectedScript);
1018 }
1019
bindObject(v8::Local<v8::Value> value,const String16 & groupName)1020 String16 InjectedScript::bindObject(v8::Local<v8::Value> value,
1021 const String16& groupName) {
1022 if (m_lastBoundObjectId <= 0) m_lastBoundObjectId = 1;
1023 int id = m_lastBoundObjectId++;
1024 m_idToWrappedObject[id].Reset(m_context->isolate(), value);
1025 m_idToWrappedObject[id].AnnotateStrongRetainer(kGlobalHandleLabel);
1026 if (!groupName.isEmpty() && id > 0) {
1027 m_idToObjectGroupName[id] = groupName;
1028 m_nameToObjectGroup[groupName].push_back(id);
1029 }
1030 return RemoteObjectId::serialize(m_context->inspector()->isolateId(),
1031 m_context->contextId(), id);
1032 }
1033
1034 // static
bindRemoteObjectIfNeeded(int sessionId,v8::Local<v8::Context> context,v8::Local<v8::Value> value,const String16 & groupName,protocol::Runtime::RemoteObject * remoteObject)1035 Response InjectedScript::bindRemoteObjectIfNeeded(
1036 int sessionId, v8::Local<v8::Context> context, v8::Local<v8::Value> value,
1037 const String16& groupName, protocol::Runtime::RemoteObject* remoteObject) {
1038 if (!remoteObject) return Response::Success();
1039 if (remoteObject->hasValue()) return Response::Success();
1040 if (remoteObject->hasUnserializableValue()) return Response::Success();
1041 if (remoteObject->getType() != RemoteObject::TypeEnum::Undefined) {
1042 v8::Isolate* isolate = context->GetIsolate();
1043 V8InspectorImpl* inspector =
1044 static_cast<V8InspectorImpl*>(v8::debug::GetInspector(isolate));
1045 InspectedContext* inspectedContext =
1046 inspector->getContext(InspectedContext::contextId(context));
1047 InjectedScript* injectedScript =
1048 inspectedContext ? inspectedContext->getInjectedScript(sessionId)
1049 : nullptr;
1050 if (!injectedScript) {
1051 return Response::ServerError("Cannot find context with specified id");
1052 }
1053 remoteObject->setObjectId(injectedScript->bindObject(value, groupName));
1054 }
1055 return Response::Success();
1056 }
1057
unbindObject(int id)1058 void InjectedScript::unbindObject(int id) {
1059 m_idToWrappedObject.erase(id);
1060 m_idToObjectGroupName.erase(id);
1061 }
1062
1063 } // namespace v8_inspector
1064