Line data Source code
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 "src/inspector/injected-script-source.h"
34 : #include "src/inspector/inspected-context.h"
35 : #include "src/inspector/protocol/Protocol.h"
36 : #include "src/inspector/remote-object-id.h"
37 : #include "src/inspector/string-util.h"
38 : #include "src/inspector/v8-console.h"
39 : #include "src/inspector/v8-function-call.h"
40 : #include "src/inspector/v8-injected-script-host.h"
41 : #include "src/inspector/v8-inspector-impl.h"
42 : #include "src/inspector/v8-inspector-session-impl.h"
43 : #include "src/inspector/v8-stack-trace-impl.h"
44 : #include "src/inspector/v8-value-utils.h"
45 :
46 : #include "include/v8-inspector.h"
47 :
48 : namespace v8_inspector {
49 :
50 : namespace {
51 : static const char privateKeyName[] = "v8-inspector#injectedScript";
52 : } // namespace
53 :
54 : using protocol::Array;
55 : using protocol::Runtime::PropertyDescriptor;
56 : using protocol::Runtime::InternalPropertyDescriptor;
57 : using protocol::Runtime::RemoteObject;
58 : using protocol::Maybe;
59 :
60 1040 : class InjectedScript::ProtocolPromiseHandler {
61 : public:
62 1040 : static bool add(V8InspectorSessionImpl* session,
63 : v8::Local<v8::Context> context, v8::Local<v8::Value> value,
64 : int executionContextId, const String16& objectGroup,
65 : bool returnByValue, bool generatePreview,
66 : EvaluateCallback* callback) {
67 : v8::Local<v8::Promise::Resolver> resolver;
68 1040 : if (!v8::Promise::Resolver::New(context).ToLocal(&resolver)) {
69 0 : callback->sendFailure(Response::InternalError());
70 0 : return false;
71 : }
72 1040 : if (!resolver->Resolve(context, value).FromMaybe(false)) {
73 0 : callback->sendFailure(Response::InternalError());
74 0 : return false;
75 : }
76 :
77 520 : v8::Local<v8::Promise> promise = resolver->GetPromise();
78 520 : V8InspectorImpl* inspector = session->inspector();
79 : ProtocolPromiseHandler* handler =
80 : new ProtocolPromiseHandler(session, executionContextId, objectGroup,
81 520 : returnByValue, generatePreview, callback);
82 : v8::Local<v8::Value> wrapper = handler->m_wrapper.Get(inspector->isolate());
83 : v8::Local<v8::Function> thenCallbackFunction =
84 : v8::Function::New(context, thenCallback, wrapper, 0,
85 : v8::ConstructorBehavior::kThrow)
86 1040 : .ToLocalChecked();
87 1040 : if (promise->Then(context, thenCallbackFunction).IsEmpty()) {
88 0 : callback->sendFailure(Response::InternalError());
89 0 : return false;
90 : }
91 : v8::Local<v8::Function> catchCallbackFunction =
92 : v8::Function::New(context, catchCallback, wrapper, 0,
93 : v8::ConstructorBehavior::kThrow)
94 1040 : .ToLocalChecked();
95 1040 : if (promise->Catch(context, catchCallbackFunction).IsEmpty()) {
96 0 : callback->sendFailure(Response::InternalError());
97 0 : return false;
98 : }
99 : return true;
100 : }
101 :
102 : private:
103 940 : static void thenCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
104 : ProtocolPromiseHandler* handler = static_cast<ProtocolPromiseHandler*>(
105 470 : info.Data().As<v8::External>()->Value());
106 : DCHECK(handler);
107 : v8::Local<v8::Value> value =
108 : info.Length() > 0
109 : ? info[0]
110 940 : : v8::Local<v8::Value>::Cast(v8::Undefined(info.GetIsolate()));
111 470 : handler->thenCallback(value);
112 470 : delete handler;
113 470 : }
114 :
115 80 : static void catchCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
116 : ProtocolPromiseHandler* handler = static_cast<ProtocolPromiseHandler*>(
117 40 : info.Data().As<v8::External>()->Value());
118 : DCHECK(handler);
119 : v8::Local<v8::Value> value =
120 : info.Length() > 0
121 : ? info[0]
122 80 : : v8::Local<v8::Value>::Cast(v8::Undefined(info.GetIsolate()));
123 40 : handler->catchCallback(value);
124 40 : delete handler;
125 40 : }
126 :
127 1560 : ProtocolPromiseHandler(V8InspectorSessionImpl* session,
128 : int executionContextId, const String16& objectGroup,
129 : bool returnByValue, bool generatePreview,
130 : EvaluateCallback* callback)
131 : : m_inspector(session->inspector()),
132 : m_sessionId(session->sessionId()),
133 : m_contextGroupId(session->contextGroupId()),
134 : m_executionContextId(executionContextId),
135 : m_objectGroup(objectGroup),
136 : m_returnByValue(returnByValue),
137 : m_generatePreview(generatePreview),
138 : m_callback(std::move(callback)),
139 : m_wrapper(m_inspector->isolate(),
140 1560 : v8::External::New(m_inspector->isolate(), this)) {
141 : m_wrapper.SetWeak(this, cleanup, v8::WeakCallbackType::kParameter);
142 520 : }
143 :
144 20 : static void cleanup(
145 40 : const v8::WeakCallbackInfo<ProtocolPromiseHandler>& data) {
146 20 : if (!data.GetParameter()->m_wrapper.IsEmpty()) {
147 : data.GetParameter()->m_wrapper.Reset();
148 : data.SetSecondPassCallback(cleanup);
149 : } else {
150 10 : data.GetParameter()->sendPromiseCollected();
151 10 : delete data.GetParameter();
152 : }
153 20 : }
154 :
155 470 : void thenCallback(v8::Local<v8::Value> result) {
156 : V8InspectorSessionImpl* session =
157 470 : m_inspector->sessionById(m_contextGroupId, m_sessionId);
158 475 : if (!session) return;
159 470 : InjectedScript::ContextScope scope(session, m_executionContextId);
160 470 : Response response = scope.initialize();
161 470 : if (!response.isSuccess()) return;
162 940 : if (m_objectGroup == "console") {
163 5 : scope.injectedScript()->setLastEvaluationResult(result);
164 : }
165 : std::unique_ptr<EvaluateCallback> callback =
166 470 : scope.injectedScript()->takeEvaluateCallback(m_callback);
167 470 : if (!callback) return;
168 470 : std::unique_ptr<protocol::Runtime::RemoteObject> wrappedValue;
169 940 : response = scope.injectedScript()->wrapObject(
170 : result, m_objectGroup, m_returnByValue, m_generatePreview,
171 : &wrappedValue);
172 470 : if (!response.isSuccess()) {
173 5 : callback->sendFailure(response);
174 : return;
175 : }
176 : callback->sendSuccess(std::move(wrappedValue),
177 1860 : Maybe<protocol::Runtime::ExceptionDetails>());
178 : }
179 :
180 40 : void catchCallback(v8::Local<v8::Value> result) {
181 40 : V8InspectorSessionImpl* session =
182 40 : m_inspector->sessionById(m_contextGroupId, m_sessionId);
183 40 : if (!session) return;
184 40 : InjectedScript::ContextScope scope(session, m_executionContextId);
185 40 : Response response = scope.initialize();
186 40 : if (!response.isSuccess()) return;
187 : std::unique_ptr<EvaluateCallback> callback =
188 40 : scope.injectedScript()->takeEvaluateCallback(m_callback);
189 40 : if (!callback) return;
190 40 : std::unique_ptr<protocol::Runtime::RemoteObject> wrappedValue;
191 80 : response = scope.injectedScript()->wrapObject(
192 : result, m_objectGroup, m_returnByValue, m_generatePreview,
193 : &wrappedValue);
194 40 : if (!response.isSuccess()) {
195 0 : callback->sendFailure(response);
196 : return;
197 : }
198 40 : String16 message;
199 : std::unique_ptr<V8StackTraceImpl> stack;
200 40 : v8::Isolate* isolate = session->inspector()->isolate();
201 40 : if (result->IsNativeError()) {
202 60 : message = " " + toProtocolString(
203 15 : result->ToDetailString(isolate->GetCurrentContext())
204 15 : .ToLocalChecked());
205 : v8::Local<v8::StackTrace> stackTrace = v8::debug::GetDetailedStackTrace(
206 15 : isolate, v8::Local<v8::Object>::Cast(result));
207 15 : if (!stackTrace.IsEmpty()) {
208 30 : stack = m_inspector->debugger()->createStackTrace(stackTrace);
209 : }
210 : }
211 40 : if (!stack) {
212 90 : stack = m_inspector->debugger()->captureStackTrace(true);
213 : }
214 : std::unique_ptr<protocol::Runtime::ExceptionDetails> exceptionDetails =
215 : protocol::Runtime::ExceptionDetails::create()
216 120 : .setExceptionId(m_inspector->nextExceptionId())
217 80 : .setText("Uncaught (in promise)" + message)
218 25 : .setLineNumber(stack && !stack->isEmpty() ? stack->topLineNumber()
219 50 : : 0)
220 : .setColumnNumber(
221 40 : stack && !stack->isEmpty() ? stack->topColumnNumber() : 0)
222 80 : .setException(wrappedValue->clone())
223 : .build();
224 40 : if (stack)
225 50 : exceptionDetails->setStackTrace(stack->buildInspectorObjectImpl());
226 40 : if (stack && !stack->isEmpty())
227 20 : exceptionDetails->setScriptId(toString16(stack->topScriptId()));
228 160 : callback->sendSuccess(std::move(wrappedValue), std::move(exceptionDetails));
229 : }
230 :
231 10 : void sendPromiseCollected() {
232 : V8InspectorSessionImpl* session =
233 10 : m_inspector->sessionById(m_contextGroupId, m_sessionId);
234 15 : if (!session) return;
235 10 : InjectedScript::ContextScope scope(session, m_executionContextId);
236 10 : Response response = scope.initialize();
237 10 : if (!response.isSuccess()) return;
238 : std::unique_ptr<EvaluateCallback> callback =
239 5 : scope.injectedScript()->takeEvaluateCallback(m_callback);
240 5 : if (!callback) return;
241 15 : callback->sendFailure(Response::Error("Promise was collected"));
242 : }
243 :
244 : V8InspectorImpl* m_inspector;
245 : int m_sessionId;
246 : int m_contextGroupId;
247 : int m_executionContextId;
248 : String16 m_objectGroup;
249 : bool m_returnByValue;
250 : bool m_generatePreview;
251 : EvaluateCallback* m_callback;
252 : v8::Global<v8::External> m_wrapper;
253 : };
254 :
255 2944 : std::unique_ptr<InjectedScript> InjectedScript::create(
256 11776 : InspectedContext* inspectedContext, int sessionId) {
257 2944 : v8::Isolate* isolate = inspectedContext->isolate();
258 2944 : v8::HandleScope handles(isolate);
259 5888 : v8::TryCatch tryCatch(isolate);
260 2944 : v8::Local<v8::Context> context = inspectedContext->context();
261 : v8::Context::Scope scope(context);
262 : v8::MicrotasksScope microtasksScope(isolate,
263 5888 : v8::MicrotasksScope::kDoNotRunMicrotasks);
264 :
265 : // Inject javascript into the context. The compiled script is supposed to
266 : // evaluate into
267 : // a single anonymous function(it's anonymous to avoid cluttering the global
268 : // object with
269 : // inspector's stuff) the function is called a few lines below with
270 : // InjectedScriptHost wrapper,
271 : // injected script id and explicit reference to the inspected global object.
272 : // The function is expected
273 : // to create and configure InjectedScript instance that is going to be used by
274 : // the inspector.
275 : StringView injectedScriptSource(
276 : reinterpret_cast<const uint8_t*>(InjectedScriptSource_js),
277 : sizeof(InjectedScriptSource_js));
278 : v8::Local<v8::Value> value;
279 2944 : if (!inspectedContext->inspector()
280 : ->compileAndRunInternalScript(
281 2944 : context, toV8String(isolate, injectedScriptSource))
282 2944 : .ToLocal(&value))
283 : return nullptr;
284 : DCHECK(value->IsFunction());
285 : v8::Local<v8::Object> scriptHostWrapper =
286 2944 : V8InjectedScriptHost::create(context, inspectedContext->inspector());
287 : v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(value);
288 2944 : v8::Local<v8::Object> windowGlobal = context->Global();
289 : v8::Local<v8::Value> info[] = {
290 : scriptHostWrapper, windowGlobal,
291 2944 : v8::Number::New(isolate, inspectedContext->contextId())};
292 :
293 : int contextGroupId = inspectedContext->contextGroupId();
294 : int contextId = inspectedContext->contextId();
295 : V8InspectorImpl* inspector = inspectedContext->inspector();
296 : v8::Local<v8::Value> injectedScriptValue;
297 2944 : if (!function->Call(context, windowGlobal, arraysize(info), info)
298 2944 : .ToLocal(&injectedScriptValue))
299 : return nullptr;
300 2944 : if (inspector->getContext(contextGroupId, contextId) != inspectedContext)
301 : return nullptr;
302 2944 : if (!injectedScriptValue->IsObject()) return nullptr;
303 :
304 : std::unique_ptr<InjectedScript> injectedScript(new InjectedScript(
305 2944 : inspectedContext, injectedScriptValue.As<v8::Object>(), sessionId));
306 : v8::Local<v8::Private> privateKey = v8::Private::ForApi(
307 : isolate, v8::String::NewFromUtf8(isolate, privateKeyName,
308 : v8::NewStringType::kInternalized)
309 5888 : .ToLocalChecked());
310 : scriptHostWrapper->SetPrivate(
311 5888 : context, privateKey, v8::External::New(isolate, injectedScript.get()));
312 2944 : return injectedScript;
313 : }
314 :
315 2944 : InjectedScript::InjectedScript(InspectedContext* context,
316 : v8::Local<v8::Object> object, int sessionId)
317 : : m_context(context),
318 : m_value(context->isolate(), object),
319 20608 : m_sessionId(sessionId) {}
320 :
321 5888 : InjectedScript::~InjectedScript() { discardEvaluateCallbacks(); }
322 :
323 67096 : Response InjectedScript::getProperties(
324 : v8::Local<v8::Object> object, const String16& groupName, bool ownProperties,
325 : bool accessorPropertiesOnly, bool generatePreview,
326 : std::unique_ptr<Array<PropertyDescriptor>>* properties,
327 : Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
328 134192 : v8::HandleScope handles(m_context->isolate());
329 67096 : v8::Local<v8::Context> context = m_context->context();
330 : V8FunctionCall function(m_context->inspector(), m_context->context(),
331 201288 : v8Value(), "getProperties");
332 67096 : function.appendArgument(object);
333 67096 : function.appendArgument(groupName);
334 67096 : function.appendArgument(ownProperties);
335 67096 : function.appendArgument(accessorPropertiesOnly);
336 67096 : function.appendArgument(generatePreview);
337 :
338 134192 : v8::TryCatch tryCatch(m_context->isolate());
339 67096 : v8::Local<v8::Value> resultValue = function.callWithoutExceptionHandling();
340 67096 : if (tryCatch.HasCaught()) {
341 : Response response = createExceptionDetails(
342 0 : tryCatch, groupName, generatePreview, exceptionDetails);
343 0 : if (!response.isSuccess()) return response;
344 : // FIXME: make properties optional
345 : *properties = Array<PropertyDescriptor>::create();
346 0 : return Response::OK();
347 : }
348 67096 : if (resultValue.IsEmpty()) return Response::InternalError();
349 67096 : std::unique_ptr<protocol::Value> protocolValue;
350 67096 : Response response = toProtocolValue(context, resultValue, &protocolValue);
351 67096 : if (!response.isSuccess()) return response;
352 134192 : protocol::ErrorSupport errors;
353 : std::unique_ptr<Array<PropertyDescriptor>> result =
354 67096 : Array<PropertyDescriptor>::fromValue(protocolValue.get(), &errors);
355 67096 : if (errors.hasErrors()) return Response::Error(errors.errors());
356 : *properties = std::move(result);
357 134192 : return Response::OK();
358 : }
359 :
360 5 : void InjectedScript::releaseObject(const String16& objectId) {
361 : std::unique_ptr<protocol::Value> parsedObjectId =
362 5 : protocol::StringUtil::parseJSON(objectId);
363 5 : if (!parsedObjectId) return;
364 : protocol::DictionaryValue* object =
365 : protocol::DictionaryValue::cast(parsedObjectId.get());
366 5 : if (!object) return;
367 5 : int boundId = 0;
368 10 : if (!object->getInteger("id", &boundId)) return;
369 5 : unbindObject(boundId);
370 : }
371 :
372 482802 : Response InjectedScript::wrapObject(
373 : v8::Local<v8::Value> value, const String16& groupName, bool forceValueType,
374 : bool generatePreview,
375 : std::unique_ptr<protocol::Runtime::RemoteObject>* result) const {
376 482802 : v8::HandleScope handles(m_context->isolate());
377 : v8::Local<v8::Value> wrappedObject;
378 482802 : v8::Local<v8::Context> context = m_context->context();
379 : Response response = wrapValue(value, groupName, forceValueType,
380 482802 : generatePreview, &wrappedObject);
381 482802 : if (!response.isSuccess()) return response;
382 965408 : protocol::ErrorSupport errors;
383 482704 : std::unique_ptr<protocol::Value> protocolValue;
384 965408 : response = toProtocolValue(context, wrappedObject, &protocolValue);
385 482704 : if (!response.isSuccess()) return response;
386 :
387 965398 : *result =
388 : protocol::Runtime::RemoteObject::fromValue(protocolValue.get(), &errors);
389 482699 : if (!result->get()) return Response::Error(errors.errors());
390 965501 : return Response::OK();
391 : }
392 :
393 0 : Response InjectedScript::wrapObjectProperty(v8::Local<v8::Object> object,
394 : v8::Local<v8::Name> key,
395 : const String16& groupName,
396 : bool forceValueType,
397 : bool generatePreview) const {
398 : v8::Local<v8::Value> property;
399 0 : v8::Local<v8::Context> context = m_context->context();
400 0 : if (!object->Get(context, key).ToLocal(&property))
401 0 : return Response::InternalError();
402 : v8::Local<v8::Value> wrappedProperty;
403 : Response response = wrapValue(property, groupName, forceValueType,
404 0 : generatePreview, &wrappedProperty);
405 0 : if (!response.isSuccess()) return response;
406 : v8::Maybe<bool> success =
407 0 : createDataProperty(context, object, key, wrappedProperty);
408 0 : if (success.IsNothing() || !success.FromJust())
409 0 : return Response::InternalError();
410 0 : return Response::OK();
411 : }
412 :
413 0 : Response InjectedScript::wrapPropertyInArray(v8::Local<v8::Array> array,
414 : v8::Local<v8::String> property,
415 : const String16& groupName,
416 : bool forceValueType,
417 : bool generatePreview) const {
418 : V8FunctionCall function(m_context->inspector(), m_context->context(),
419 0 : v8Value(), "wrapPropertyInArray");
420 0 : function.appendArgument(array);
421 0 : function.appendArgument(property);
422 0 : function.appendArgument(groupName);
423 0 : function.appendArgument(forceValueType);
424 0 : function.appendArgument(generatePreview);
425 0 : bool hadException = false;
426 0 : function.call(hadException);
427 0 : return hadException ? Response::InternalError() : Response::OK();
428 : }
429 :
430 482802 : Response InjectedScript::wrapValue(v8::Local<v8::Value> value,
431 : const String16& groupName,
432 : bool forceValueType, bool generatePreview,
433 : v8::Local<v8::Value>* result) const {
434 : V8FunctionCall function(m_context->inspector(), m_context->context(),
435 965604 : v8Value(), "wrapObject");
436 482802 : function.appendArgument(value);
437 482802 : function.appendArgument(groupName);
438 482802 : function.appendArgument(forceValueType);
439 482802 : function.appendArgument(generatePreview);
440 482802 : bool hadException = false;
441 482802 : *result = function.call(hadException);
442 965506 : if (hadException || result->IsEmpty()) return Response::InternalError();
443 482704 : return Response::OK();
444 : }
445 :
446 105 : std::unique_ptr<protocol::Runtime::RemoteObject> InjectedScript::wrapTable(
447 : v8::Local<v8::Value> table, v8::Local<v8::Value> columns) const {
448 210 : v8::HandleScope handles(m_context->isolate());
449 105 : v8::Local<v8::Context> context = m_context->context();
450 : V8FunctionCall function(m_context->inspector(), context, v8Value(),
451 315 : "wrapTable");
452 105 : function.appendArgument(table);
453 105 : if (columns.IsEmpty())
454 100 : function.appendArgument(false);
455 : else
456 5 : function.appendArgument(columns);
457 105 : bool hadException = false;
458 105 : v8::Local<v8::Value> r = function.call(hadException);
459 210 : if (hadException || r.IsEmpty()) return nullptr;
460 105 : std::unique_ptr<protocol::Value> protocolValue;
461 105 : Response response = toProtocolValue(context, r, &protocolValue);
462 105 : if (!response.isSuccess()) return nullptr;
463 210 : protocol::ErrorSupport errors;
464 : return protocol::Runtime::RemoteObject::fromValue(protocolValue.get(),
465 210 : &errors);
466 : }
467 :
468 520 : void InjectedScript::addPromiseCallback(
469 : V8InspectorSessionImpl* session, v8::MaybeLocal<v8::Value> value,
470 : const String16& objectGroup, bool returnByValue, bool generatePreview,
471 : std::unique_ptr<EvaluateCallback> callback) {
472 520 : if (value.IsEmpty()) {
473 0 : callback->sendFailure(Response::InternalError());
474 520 : return;
475 : }
476 : v8::MicrotasksScope microtasksScope(m_context->isolate(),
477 1040 : v8::MicrotasksScope::kRunMicrotasks);
478 520 : if (ProtocolPromiseHandler::add(
479 : session, m_context->context(), value.ToLocalChecked(),
480 : m_context->contextId(), objectGroup, returnByValue, generatePreview,
481 1040 : callback.get())) {
482 1040 : m_evaluateCallbacks.insert(callback.release());
483 520 : }
484 : }
485 :
486 2944 : void InjectedScript::discardEvaluateCallbacks() {
487 5893 : for (auto& callback : m_evaluateCallbacks) {
488 15 : callback->sendFailure(Response::Error("Execution context was destroyed."));
489 5 : delete callback;
490 : }
491 : m_evaluateCallbacks.clear();
492 2944 : }
493 :
494 515 : std::unique_ptr<EvaluateCallback> InjectedScript::takeEvaluateCallback(
495 : EvaluateCallback* callback) {
496 : auto it = m_evaluateCallbacks.find(callback);
497 515 : if (it == m_evaluateCallbacks.end()) return nullptr;
498 515 : std::unique_ptr<EvaluateCallback> value(*it);
499 : m_evaluateCallbacks.erase(it);
500 : return value;
501 : }
502 :
503 68696 : Response InjectedScript::findObject(const RemoteObjectId& objectId,
504 : v8::Local<v8::Value>* outObject) const {
505 137392 : auto it = m_idToWrappedObject.find(objectId.id());
506 68696 : if (it == m_idToWrappedObject.end())
507 0 : return Response::Error("Could not find object with given id");
508 137392 : *outObject = it->second.Get(m_context->isolate());
509 68696 : return Response::OK();
510 : }
511 :
512 68656 : String16 InjectedScript::objectGroupName(const RemoteObjectId& objectId) const {
513 68656 : if (objectId.id() <= 0) return String16();
514 137312 : auto it = m_idToObjectGroupName.find(objectId.id());
515 68656 : return it != m_idToObjectGroupName.end() ? it->second : String16();
516 : }
517 :
518 41296 : void InjectedScript::releaseObjectGroup(const String16& objectGroup) {
519 82592 : if (objectGroup == "console") m_lastEvaluationResult.Reset();
520 41296 : if (objectGroup.isEmpty()) return;
521 : auto it = m_nameToObjectGroup.find(objectGroup);
522 41296 : if (it == m_nameToObjectGroup.end()) return;
523 2024919 : for (int id : it->second) unbindObject(id);
524 : m_nameToObjectGroup.erase(it);
525 : }
526 :
527 345 : void InjectedScript::setCustomObjectFormatterEnabled(bool enabled) {
528 690 : v8::HandleScope handles(m_context->isolate());
529 : V8FunctionCall function(m_context->inspector(), m_context->context(),
530 1035 : v8Value(), "setCustomObjectFormatterEnabled");
531 345 : function.appendArgument(enabled);
532 345 : bool hadException = false;
533 345 : function.call(hadException);
534 345 : DCHECK(!hadException);
535 345 : }
536 :
537 550348 : v8::Local<v8::Value> InjectedScript::v8Value() const {
538 1100696 : return m_value.Get(m_context->isolate());
539 : }
540 :
541 85 : v8::Local<v8::Value> InjectedScript::lastEvaluationResult() const {
542 85 : if (m_lastEvaluationResult.IsEmpty())
543 20 : return v8::Undefined(m_context->isolate());
544 75 : return m_lastEvaluationResult.Get(m_context->isolate());
545 : }
546 :
547 5 : void InjectedScript::setLastEvaluationResult(v8::Local<v8::Value> result) {
548 5 : m_lastEvaluationResult.Reset(m_context->isolate(), result);
549 5 : }
550 :
551 276 : Response InjectedScript::resolveCallArgument(
552 : protocol::Runtime::CallArgument* callArgument,
553 : v8::Local<v8::Value>* result) {
554 276 : if (callArgument->hasObjectId()) {
555 40 : std::unique_ptr<RemoteObjectId> remoteObjectId;
556 : Response response =
557 120 : RemoteObjectId::parse(callArgument->getObjectId(""), &remoteObjectId);
558 40 : if (!response.isSuccess()) return response;
559 271 : if (remoteObjectId->contextId() != m_context->contextId())
560 : return Response::Error(
561 : "Argument should belong to the same JavaScript world as target "
562 0 : "object");
563 40 : return findObject(*remoteObjectId, result);
564 : }
565 246 : if (callArgument->hasValue() || callArgument->hasUnserializableValue()) {
566 : String16 value =
567 : callArgument->hasValue()
568 909 : ? "(" + callArgument->getValue(nullptr)->serialize() + ")"
569 934 : : "Number(\"" + callArgument->getUnserializableValue("") + "\")";
570 231 : if (!m_context->inspector()
571 : ->compileAndRunInternalScript(
572 231 : m_context->context(), toV8String(m_context->isolate(), value))
573 231 : .ToLocal(result)) {
574 0 : return Response::Error("Couldn't parse value object in call argument");
575 : }
576 231 : return Response::OK();
577 : }
578 10 : *result = v8::Undefined(m_context->isolate());
579 5 : return Response::OK();
580 : }
581 :
582 1027 : Response InjectedScript::createExceptionDetails(
583 : const v8::TryCatch& tryCatch, const String16& objectGroup,
584 : bool generatePreview, Maybe<protocol::Runtime::ExceptionDetails>* result) {
585 1027 : if (!tryCatch.HasCaught()) return Response::InternalError();
586 1027 : v8::Local<v8::Message> message = tryCatch.Message();
587 1027 : v8::Local<v8::Value> exception = tryCatch.Exception();
588 : String16 messageText =
589 1027 : message.IsEmpty() ? String16() : toProtocolString(message->Get());
590 : std::unique_ptr<protocol::Runtime::ExceptionDetails> exceptionDetails =
591 : protocol::Runtime::ExceptionDetails::create()
592 3081 : .setExceptionId(m_context->inspector()->nextExceptionId())
593 2054 : .setText(exception.IsEmpty() ? messageText : String16("Uncaught"))
594 : .setLineNumber(
595 : message.IsEmpty()
596 : ? 0
597 2054 : : message->GetLineNumber(m_context->context()).FromMaybe(1) -
598 2054 : 1)
599 : .setColumnNumber(
600 : message.IsEmpty()
601 : ? 0
602 3131 : : message->GetStartColumn(m_context->context()).FromMaybe(0))
603 : .build();
604 1027 : if (!message.IsEmpty()) {
605 : exceptionDetails->setScriptId(String16::fromInteger(
606 2054 : static_cast<int>(message->GetScriptOrigin().ScriptID()->Value())));
607 1027 : v8::Local<v8::StackTrace> stackTrace = message->GetStackTrace();
608 1027 : if (!stackTrace.IsEmpty() && stackTrace->GetFrameCount() > 0)
609 : exceptionDetails->setStackTrace(m_context->inspector()
610 : ->debugger()
611 : ->createStackTrace(stackTrace)
612 250 : ->buildInspectorObjectImpl());
613 : }
614 1027 : if (!exception.IsEmpty()) {
615 1027 : std::unique_ptr<protocol::Runtime::RemoteObject> wrapped;
616 : Response response =
617 : wrapObject(exception, objectGroup, false /* forceValueType */,
618 1027 : generatePreview && !exception->IsNativeError(), &wrapped);
619 1027 : if (!response.isSuccess()) return response;
620 : exceptionDetails->setException(std::move(wrapped));
621 : }
622 : *result = std::move(exceptionDetails);
623 1027 : return Response::OK();
624 : }
625 :
626 15152 : Response InjectedScript::wrapEvaluateResult(
627 : v8::MaybeLocal<v8::Value> maybeResultValue, const v8::TryCatch& tryCatch,
628 : const String16& objectGroup, bool returnByValue, bool generatePreview,
629 : std::unique_ptr<protocol::Runtime::RemoteObject>* result,
630 : Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
631 : v8::Local<v8::Value> resultValue;
632 15152 : if (!tryCatch.HasCaught()) {
633 14145 : if (!maybeResultValue.ToLocal(&resultValue))
634 0 : return Response::InternalError();
635 : Response response = wrapObject(resultValue, objectGroup, returnByValue,
636 14145 : generatePreview, result);
637 14145 : if (!response.isSuccess()) return response;
638 28290 : if (objectGroup == "console")
639 175 : m_lastEvaluationResult.Reset(m_context->isolate(), resultValue);
640 : } else {
641 1007 : v8::Local<v8::Value> exception = tryCatch.Exception();
642 : Response response =
643 : wrapObject(exception, objectGroup, false,
644 1007 : generatePreview && !exception->IsNativeError(), result);
645 1007 : if (!response.isSuccess()) return response;
646 : // We send exception in result for compatibility reasons, even though it's
647 : // accessible through exceptionDetails.exception.
648 2004 : response = createExceptionDetails(tryCatch, objectGroup, generatePreview,
649 : exceptionDetails);
650 1002 : if (!response.isSuccess()) return response;
651 : }
652 15147 : return Response::OK();
653 : }
654 :
655 400 : v8::Local<v8::Object> InjectedScript::commandLineAPI() {
656 400 : if (m_commandLineAPI.IsEmpty()) {
657 : m_commandLineAPI.Reset(
658 : m_context->isolate(),
659 : m_context->inspector()->console()->createCommandLineAPI(
660 60 : m_context->context(), m_sessionId));
661 : }
662 800 : return m_commandLineAPI.Get(m_context->isolate());
663 : }
664 :
665 335972 : InjectedScript::Scope::Scope(V8InspectorSessionImpl* session)
666 : : m_inspector(session->inspector()),
667 : m_injectedScript(nullptr),
668 : m_handleScope(m_inspector->isolate()),
669 : m_tryCatch(m_inspector->isolate()),
670 : m_ignoreExceptionsAndMuteConsole(false),
671 : m_previousPauseOnExceptionsState(v8::debug::NoBreakOnException),
672 : m_userGesture(false),
673 : m_contextGroupId(session->contextGroupId()),
674 419965 : m_sessionId(session->sessionId()) {}
675 :
676 100130 : Response InjectedScript::Scope::initialize() {
677 100130 : cleanup();
678 : V8InspectorSessionImpl* session =
679 100130 : m_inspector->sessionById(m_contextGroupId, m_sessionId);
680 100130 : if (!session) return Response::InternalError();
681 100125 : Response response = findInjectedScript(session);
682 100125 : if (!response.isSuccess()) return response;
683 100100 : m_context = m_injectedScript->context()->context();
684 100100 : m_context->Enter();
685 100100 : return Response::OK();
686 : }
687 :
688 400 : void InjectedScript::Scope::installCommandLineAPI() {
689 : DCHECK(m_injectedScript && !m_context.IsEmpty() &&
690 : !m_commandLineAPIScope.get());
691 : m_commandLineAPIScope.reset(new V8Console::CommandLineAPIScope(
692 1200 : m_context, m_injectedScript->commandLineAPI(), m_context->Global()));
693 400 : }
694 :
695 67101 : void InjectedScript::Scope::ignoreExceptionsAndMuteConsole() {
696 : DCHECK(!m_ignoreExceptionsAndMuteConsole);
697 67101 : m_ignoreExceptionsAndMuteConsole = true;
698 67101 : m_inspector->client()->muteMetrics(m_contextGroupId);
699 67101 : m_inspector->muteExceptions(m_contextGroupId);
700 : m_previousPauseOnExceptionsState =
701 67101 : setPauseOnExceptionsState(v8::debug::NoBreakOnException);
702 67101 : }
703 :
704 134202 : v8::debug::ExceptionBreakState InjectedScript::Scope::setPauseOnExceptionsState(
705 : v8::debug::ExceptionBreakState newState) {
706 268404 : if (!m_inspector->debugger()->enabled()) return newState;
707 : v8::debug::ExceptionBreakState presentState =
708 267124 : m_inspector->debugger()->getPauseOnExceptionsState();
709 133562 : if (presentState != newState)
710 560 : m_inspector->debugger()->setPauseOnExceptionsState(newState);
711 133562 : return presentState;
712 : }
713 :
714 0 : void InjectedScript::Scope::pretendUserGesture() {
715 : DCHECK(!m_userGesture);
716 0 : m_userGesture = true;
717 0 : m_inspector->client()->beginUserGesture();
718 0 : }
719 :
720 184123 : void InjectedScript::Scope::cleanup() {
721 : m_commandLineAPIScope.reset();
722 184123 : if (!m_context.IsEmpty()) {
723 100100 : m_context->Exit();
724 : m_context.Clear();
725 : }
726 184123 : }
727 :
728 167986 : InjectedScript::Scope::~Scope() {
729 83993 : if (m_ignoreExceptionsAndMuteConsole) {
730 67101 : setPauseOnExceptionsState(m_previousPauseOnExceptionsState);
731 67101 : m_inspector->client()->unmuteMetrics(m_contextGroupId);
732 67101 : m_inspector->unmuteExceptions(m_contextGroupId);
733 : }
734 83993 : if (m_userGesture) m_inspector->client()->endUserGesture();
735 83993 : cleanup();
736 83993 : }
737 :
738 5914 : InjectedScript::ContextScope::ContextScope(V8InspectorSessionImpl* session,
739 : int executionContextId)
740 : : InjectedScript::Scope(session),
741 6434 : m_executionContextId(executionContextId) {}
742 :
743 6434 : InjectedScript::ContextScope::~ContextScope() {}
744 :
745 12048 : Response InjectedScript::ContextScope::findInjectedScript(
746 : V8InspectorSessionImpl* session) {
747 12048 : return session->findInjectedScript(m_executionContextId, m_injectedScript);
748 : }
749 :
750 67691 : InjectedScript::ObjectScope::ObjectScope(V8InspectorSessionImpl* session,
751 : const String16& remoteObjectId)
752 135382 : : InjectedScript::Scope(session), m_remoteObjectId(remoteObjectId) {}
753 :
754 135382 : InjectedScript::ObjectScope::~ObjectScope() {}
755 :
756 68656 : Response InjectedScript::ObjectScope::findInjectedScript(
757 : V8InspectorSessionImpl* session) {
758 68656 : std::unique_ptr<RemoteObjectId> remoteId;
759 68656 : Response response = RemoteObjectId::parse(m_remoteObjectId, &remoteId);
760 68656 : if (!response.isSuccess()) return response;
761 68656 : InjectedScript* injectedScript = nullptr;
762 137312 : response = session->findInjectedScript(remoteId.get(), injectedScript);
763 68656 : if (!response.isSuccess()) return response;
764 137312 : m_objectGroupName = injectedScript->objectGroupName(*remoteId);
765 205968 : response = injectedScript->findObject(*remoteId, &m_object);
766 68656 : if (!response.isSuccess()) return response;
767 68656 : m_injectedScript = injectedScript;
768 68656 : return Response::OK();
769 : }
770 :
771 9868 : InjectedScript::CallFrameScope::CallFrameScope(V8InspectorSessionImpl* session,
772 : const String16& remoteObjectId)
773 9868 : : InjectedScript::Scope(session), m_remoteCallFrameId(remoteObjectId) {}
774 :
775 19736 : InjectedScript::CallFrameScope::~CallFrameScope() {}
776 :
777 19421 : Response InjectedScript::CallFrameScope::findInjectedScript(
778 : V8InspectorSessionImpl* session) {
779 19421 : std::unique_ptr<RemoteCallFrameId> remoteId;
780 19421 : Response response = RemoteCallFrameId::parse(m_remoteCallFrameId, &remoteId);
781 19421 : if (!response.isSuccess()) return response;
782 19421 : m_frameOrdinal = static_cast<size_t>(remoteId->frameOrdinal());
783 19421 : return session->findInjectedScript(remoteId.get(), m_injectedScript);
784 : }
785 :
786 2540233 : InjectedScript* InjectedScript::fromInjectedScriptHost(
787 : v8::Isolate* isolate, v8::Local<v8::Object> injectedScriptObject) {
788 2540233 : v8::HandleScope handleScope(isolate);
789 2540233 : v8::Local<v8::Context> context = isolate->GetCurrentContext();
790 : v8::Local<v8::Private> privateKey = v8::Private::ForApi(
791 : isolate, v8::String::NewFromUtf8(isolate, privateKeyName,
792 : v8::NewStringType::kInternalized)
793 5080466 : .ToLocalChecked());
794 : v8::Local<v8::Value> value =
795 2540233 : injectedScriptObject->GetPrivate(context, privateKey).ToLocalChecked();
796 : DCHECK(value->IsExternal());
797 : v8::Local<v8::External> external = value.As<v8::External>();
798 2540233 : return static_cast<InjectedScript*>(external->Value());
799 : }
800 :
801 2540233 : int InjectedScript::bindObject(v8::Local<v8::Value> value,
802 : const String16& groupName) {
803 2540233 : if (m_lastBoundObjectId <= 0) m_lastBoundObjectId = 1;
804 2540233 : int id = m_lastBoundObjectId++;
805 2540233 : m_idToWrappedObject[id].Reset(m_context->isolate(), value);
806 :
807 2540233 : if (!groupName.isEmpty() && id > 0) {
808 2531245 : m_idToObjectGroupName[id] = groupName;
809 2531245 : m_nameToObjectGroup[groupName].push_back(id);
810 : }
811 2540233 : return id;
812 : }
813 :
814 0 : void InjectedScript::unbindObject(int id) {
815 : m_idToWrappedObject.erase(id);
816 : m_idToObjectGroupName.erase(id);
817 0 : }
818 :
819 : } // namespace v8_inspector
|