Commit b5e81c7c authored by dgozman's avatar dgozman Committed by Commit Bot

[inspector] Create InjectedScript per session in each context

This gives sessions separate remote objects space and also
makes command line api respect the session it was called from.

BUG=chromium:590878

Review-Url: https://codereview.chromium.org/2916803005
Cr-Commit-Position: refs/heads/master@{#45708}
parent 3965c548
......@@ -58,7 +58,7 @@ using protocol::Runtime::RemoteObject;
using protocol::Maybe;
std::unique_ptr<InjectedScript> InjectedScript::create(
InspectedContext* inspectedContext) {
InspectedContext* inspectedContext, int sessionId) {
v8::Isolate* isolate = inspectedContext->isolate();
v8::HandleScope handles(isolate);
v8::Local<v8::Context> context = inspectedContext->context();
......@@ -106,7 +106,7 @@ std::unique_ptr<InjectedScript> InjectedScript::create(
if (!injectedScriptValue->IsObject()) return nullptr;
std::unique_ptr<InjectedScript> injectedScript(new InjectedScript(
inspectedContext, injectedScriptValue.As<v8::Object>()));
inspectedContext, injectedScriptValue.As<v8::Object>(), sessionId));
v8::Local<v8::Private> privateKey = v8::Private::ForApi(
isolate, v8::String::NewFromUtf8(isolate, privateKeyName,
v8::NewStringType::kInternalized)
......@@ -117,8 +117,10 @@ std::unique_ptr<InjectedScript> InjectedScript::create(
}
InjectedScript::InjectedScript(InspectedContext* context,
v8::Local<v8::Object> object)
: m_context(context), m_value(context->isolate(), object) {}
v8::Local<v8::Object> object, int sessionId)
: m_context(context),
m_value(context->isolate(), object),
m_sessionId(sessionId) {}
InjectedScript::~InjectedScript() {}
......@@ -420,7 +422,7 @@ v8::Local<v8::Object> InjectedScript::commandLineAPI() {
m_commandLineAPI.Reset(
m_context->isolate(),
m_context->inspector()->console()->createCommandLineAPI(
m_context->context()));
m_context->context(), m_sessionId));
}
return m_commandLineAPI.Get(m_context->isolate());
}
......
......@@ -54,7 +54,8 @@ using protocol::Response;
class InjectedScript final {
public:
static std::unique_ptr<InjectedScript> create(InspectedContext*);
static std::unique_ptr<InjectedScript> create(InspectedContext*,
int sessionId);
~InjectedScript();
static InjectedScript* fromInjectedScriptHost(v8::Isolate* isolate,
v8::Local<v8::Object>);
......@@ -181,7 +182,7 @@ class InjectedScript final {
};
private:
InjectedScript(InspectedContext*, v8::Local<v8::Object>);
InjectedScript(InspectedContext*, v8::Local<v8::Object>, int sessionId);
v8::Local<v8::Value> v8Value() const;
Response wrapValue(v8::Local<v8::Value>, const String16& groupName,
bool forceValueType, bool generatePreview,
......@@ -191,6 +192,7 @@ class InjectedScript final {
InspectedContext* m_context;
v8::Global<v8::Value> m_value;
int m_sessionId;
v8::Global<v8::Value> m_lastEvaluationResult;
v8::Global<v8::Object> m_commandLineAPI;
int m_lastBoundObjectId = 1;
......
......@@ -64,15 +64,23 @@ void InspectedContext::setReported(int sessionId, bool reported) {
m_reportedSessionIds.erase(sessionId);
}
bool InspectedContext::createInjectedScript() {
DCHECK(!m_injectedScript);
std::unique_ptr<InjectedScript> injectedScript = InjectedScript::create(this);
InjectedScript* InspectedContext::getInjectedScript(int sessionId) {
auto it = m_injectedScripts.find(sessionId);
return it == m_injectedScripts.end() ? nullptr : it->second.get();
}
bool InspectedContext::createInjectedScript(int sessionId) {
DCHECK(m_injectedScripts.find(sessionId) == m_injectedScripts.end());
std::unique_ptr<InjectedScript> injectedScript =
InjectedScript::create(this, sessionId);
// InjectedScript::create can destroy |this|.
if (!injectedScript) return false;
m_injectedScript = std::move(injectedScript);
m_injectedScripts[sessionId] = std::move(injectedScript);
return true;
}
void InspectedContext::discardInjectedScript() { m_injectedScript.reset(); }
void InspectedContext::discardInjectedScript(int sessionId) {
m_injectedScripts.erase(sessionId);
}
} // namespace v8_inspector
......@@ -5,7 +5,7 @@
#ifndef V8_INSPECTOR_INSPECTEDCONTEXT_H_
#define V8_INSPECTOR_INSPECTEDCONTEXT_H_
#include <map>
#include <unordered_map>
#include <unordered_set>
#include "src/base/macros.h"
......@@ -39,9 +39,9 @@ class InspectedContext {
v8::Isolate* isolate() const;
V8InspectorImpl* inspector() const { return m_inspector; }
InjectedScript* getInjectedScript() { return m_injectedScript.get(); }
bool createInjectedScript();
void discardInjectedScript();
InjectedScript* getInjectedScript(int sessionId);
bool createInjectedScript(int sessionId);
void discardInjectedScript(int sessionId);
private:
friend class V8InspectorImpl;
......@@ -55,7 +55,7 @@ class InspectedContext {
const String16 m_humanReadableName;
const String16 m_auxData;
std::unordered_set<int> m_reportedSessionIds;
std::unique_ptr<InjectedScript> m_injectedScript;
std::unordered_map<int, std::unique_ptr<InjectedScript>> m_injectedScripts;
DISALLOW_COPY_AND_ASSIGN(InspectedContext);
};
......
......@@ -37,10 +37,14 @@ class ConsoleHelper {
int contextId() const { return m_contextId; }
int groupId() const { return m_groupId; }
InjectedScript* injectedScript() {
InjectedScript* injectedScript(int sessionId) {
InspectedContext* context = m_inspector->getContext(m_groupId, m_contextId);
if (!context) return nullptr;
return context->getInjectedScript();
return context->getInjectedScript(sessionId);
}
V8InspectorSessionImpl* session(int sessionId) {
return m_inspector->sessionById(m_groupId, sessionId);
}
V8ConsoleMessageStorage* consoleMessageStorage() {
......@@ -272,6 +276,7 @@ void V8Console::Assert(const v8::debug::ConsoleCallArguments& info) {
toV8String(m_inspector->isolate(), String16("console.assert")));
helper.reportCall(ConsoleAPIType::kAssert, arguments);
// TODO(dgozman): only break once, not per each session.
helper.forEachSession([](V8InspectorSessionImpl* session) {
if (session->debuggerAgent()->enabled()) {
session->debuggerAgent()->breakProgramOnException(
......@@ -376,7 +381,8 @@ void V8Console::memorySetterCallback(
// setter just ignores the passed value. http://crbug.com/468611
}
void V8Console::keysCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
void V8Console::keysCallback(const v8::FunctionCallbackInfo<v8::Value>& info,
int sessionId) {
v8::Isolate* isolate = info.GetIsolate();
info.GetReturnValue().Set(v8::Array::New(isolate));
......@@ -390,8 +396,8 @@ void V8Console::keysCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
info.GetReturnValue().Set(names);
}
void V8Console::valuesCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
void V8Console::valuesCallback(const v8::FunctionCallbackInfo<v8::Value>& info,
int sessionId) {
v8::Isolate* isolate = info.GetIsolate();
info.GetReturnValue().Set(v8::Array::New(isolate));
......@@ -413,7 +419,7 @@ void V8Console::valuesCallback(
info.GetReturnValue().Set(values);
}
static void setFunctionBreakpoint(ConsoleHelper& helper,
static void setFunctionBreakpoint(ConsoleHelper& helper, int sessionId,
v8::Local<v8::Function> function,
V8DebuggerAgentImpl::BreakpointSource source,
const String16& condition, bool enable) {
......@@ -424,8 +430,7 @@ static void setFunctionBreakpoint(ConsoleHelper& helper,
columnNumber == v8::Function::kLineOffsetNotFound)
return;
helper.forEachSession([&enable, &scriptId, &lineNumber, &columnNumber,
&source, &condition](V8InspectorSessionImpl* session) {
if (V8InspectorSessionImpl* session = helper.session(sessionId)) {
if (!session->debuggerAgent()->enabled()) return;
if (enable) {
session->debuggerAgent()->setBreakpointAt(
......@@ -434,33 +439,33 @@ static void setFunctionBreakpoint(ConsoleHelper& helper,
session->debuggerAgent()->removeBreakpointAt(scriptId, lineNumber,
columnNumber, source);
}
});
}
}
void V8Console::debugFunctionCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
const v8::FunctionCallbackInfo<v8::Value>& info, int sessionId) {
v8::debug::ConsoleCallArguments args(info);
ConsoleHelper helper(args, m_inspector);
v8::Local<v8::Function> function;
if (!helper.firstArgAsFunction().ToLocal(&function)) return;
setFunctionBreakpoint(helper, function,
setFunctionBreakpoint(helper, sessionId, function,
V8DebuggerAgentImpl::DebugCommandBreakpointSource,
String16(), true);
}
void V8Console::undebugFunctionCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
const v8::FunctionCallbackInfo<v8::Value>& info, int sessionId) {
v8::debug::ConsoleCallArguments args(info);
ConsoleHelper helper(args, m_inspector);
v8::Local<v8::Function> function;
if (!helper.firstArgAsFunction().ToLocal(&function)) return;
setFunctionBreakpoint(helper, function,
setFunctionBreakpoint(helper, sessionId, function,
V8DebuggerAgentImpl::DebugCommandBreakpointSource,
String16(), false);
}
void V8Console::monitorFunctionCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
const v8::FunctionCallbackInfo<v8::Value>& info, int sessionId) {
v8::debug::ConsoleCallArguments args(info);
ConsoleHelper helper(args, m_inspector);
v8::Local<v8::Function> function;
......@@ -478,39 +483,40 @@ void V8Console::monitorFunctionCallback(
builder.append(
" called\" + (arguments.length > 0 ? \" with arguments: \" + "
"Array.prototype.join.call(arguments, \", \") : \"\")) && false");
setFunctionBreakpoint(helper, function,
setFunctionBreakpoint(helper, sessionId, function,
V8DebuggerAgentImpl::MonitorCommandBreakpointSource,
builder.toString(), true);
}
void V8Console::unmonitorFunctionCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
const v8::FunctionCallbackInfo<v8::Value>& info, int sessionId) {
v8::debug::ConsoleCallArguments args(info);
ConsoleHelper helper(args, m_inspector);
v8::Local<v8::Function> function;
if (!helper.firstArgAsFunction().ToLocal(&function)) return;
setFunctionBreakpoint(helper, function,
setFunctionBreakpoint(helper, sessionId, function,
V8DebuggerAgentImpl::MonitorCommandBreakpointSource,
String16(), false);
}
void V8Console::lastEvaluationResultCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
const v8::FunctionCallbackInfo<v8::Value>& info, int sessionId) {
v8::debug::ConsoleCallArguments args(info);
ConsoleHelper helper(args, m_inspector);
InjectedScript* injectedScript = helper.injectedScript();
InjectedScript* injectedScript = helper.injectedScript(sessionId);
if (!injectedScript) return;
info.GetReturnValue().Set(injectedScript->lastEvaluationResult());
}
static void inspectImpl(const v8::FunctionCallbackInfo<v8::Value>& info,
bool copyToClipboard, V8InspectorImpl* inspector) {
int sessionId, bool copyToClipboard,
V8InspectorImpl* inspector) {
if (info.Length() < 1) return;
if (!copyToClipboard) info.GetReturnValue().Set(info[0]);
v8::debug::ConsoleCallArguments args(info);
ConsoleHelper helper(args, inspector);
InjectedScript* injectedScript = helper.injectedScript();
InjectedScript* injectedScript = helper.injectedScript(sessionId);
if (!injectedScript) return;
std::unique_ptr<protocol::Runtime::RemoteObject> wrappedObject;
protocol::Response response =
......@@ -521,35 +527,35 @@ static void inspectImpl(const v8::FunctionCallbackInfo<v8::Value>& info,
std::unique_ptr<protocol::DictionaryValue> hints =
protocol::DictionaryValue::create();
if (copyToClipboard) hints->setBoolean("copyToClipboard", true);
helper.forEachSession(
[&wrappedObject, &hints](V8InspectorSessionImpl* session) {
session->runtimeAgent()->inspect(std::move(wrappedObject),
std::move(hints));
});
if (V8InspectorSessionImpl* session = helper.session(sessionId)) {
session->runtimeAgent()->inspect(std::move(wrappedObject),
std::move(hints));
}
}
void V8Console::inspectCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
inspectImpl(info, false, m_inspector);
void V8Console::inspectCallback(const v8::FunctionCallbackInfo<v8::Value>& info,
int sessionId) {
inspectImpl(info, sessionId, false, m_inspector);
}
void V8Console::copyCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
inspectImpl(info, true, m_inspector);
void V8Console::copyCallback(const v8::FunctionCallbackInfo<v8::Value>& info,
int sessionId) {
inspectImpl(info, sessionId, true, m_inspector);
}
void V8Console::inspectedObject(const v8::FunctionCallbackInfo<v8::Value>& info,
unsigned num) {
int sessionId, unsigned num) {
DCHECK(num < V8InspectorSessionImpl::kInspectedObjectBufferSize);
v8::debug::ConsoleCallArguments args(info);
ConsoleHelper helper(args, m_inspector);
helper.forEachSession([&info, &num](V8InspectorSessionImpl* session) {
if (V8InspectorSessionImpl* session = helper.session(sessionId)) {
V8InspectorSession::Inspectable* object = session->inspectedObject(num);
v8::Isolate* isolate = info.GetIsolate();
if (object)
info.GetReturnValue().Set(object->get(isolate->GetCurrentContext()));
else
info.GetReturnValue().Set(v8::Undefined(isolate));
});
}
}
void V8Console::installMemoryGetter(v8::Local<v8::Context> context,
......@@ -570,7 +576,7 @@ void V8Console::installMemoryGetter(v8::Local<v8::Context> context,
}
v8::Local<v8::Object> V8Console::createCommandLineAPI(
v8::Local<v8::Context> context) {
v8::Local<v8::Context> context, int sessionId) {
v8::Isolate* isolate = context->GetIsolate();
v8::MicrotasksScope microtasksScope(isolate,
v8::MicrotasksScope::kDoNotRunMicrotasks);
......@@ -581,7 +587,9 @@ v8::Local<v8::Object> V8Console::createCommandLineAPI(
DCHECK(success);
USE(success);
v8::Local<v8::External> data = v8::External::New(isolate, this);
// TODO(dgozman): this CommandLineAPIData instance leaks. Use PodArray maybe?
v8::Local<v8::External> data =
v8::External::New(isolate, new CommandLineAPIData(this, sessionId));
createBoundFunctionProperty(context, commandLineAPI, data, "dir",
&V8Console::call<&V8Console::Dir>,
"function dir(value) { [Command Line API] }");
......
......@@ -19,7 +19,8 @@ class V8InspectorImpl;
// https://console.spec.whatwg.org/#console-interface
class V8Console : public v8::debug::ConsoleDelegate {
public:
v8::Local<v8::Object> createCommandLineAPI(v8::Local<v8::Context> context);
v8::Local<v8::Object> createCommandLineAPI(v8::Local<v8::Context> context,
int sessionId);
void installMemoryGetter(v8::Local<v8::Context> context,
v8::Local<v8::Object> console);
......@@ -79,12 +80,20 @@ class V8Console : public v8::debug::ConsoleDelegate {
static_cast<V8Console*>(info.Data().As<v8::External>()->Value());
(console->*func)(info);
}
using CommandLineAPIData = std::pair<V8Console*, int>;
template <void (V8Console::*func)(const v8::FunctionCallbackInfo<v8::Value>&,
int)>
static void call(const v8::FunctionCallbackInfo<v8::Value>& info) {
CommandLineAPIData* data = static_cast<CommandLineAPIData*>(
info.Data().As<v8::External>()->Value());
(data->first->*func)(info, data->second);
}
template <void (V8Console::*func)(const v8::debug::ConsoleCallArguments&)>
static void call(const v8::FunctionCallbackInfo<v8::Value>& info) {
V8Console* console =
static_cast<V8Console*>(info.Data().As<v8::External>()->Value());
CommandLineAPIData* data = static_cast<CommandLineAPIData*>(
info.Data().As<v8::External>()->Value());
v8::debug::ConsoleCallArguments args(info);
(console->*func)(args);
(data->first->*func)(args);
}
// TODO(foolip): There is no spec for the Memory Info API, see blink-dev:
......@@ -93,31 +102,43 @@ class V8Console : public v8::debug::ConsoleDelegate {
void memorySetterCallback(const v8::FunctionCallbackInfo<v8::Value>&);
// CommandLineAPI
void keysCallback(const v8::FunctionCallbackInfo<v8::Value>&);
void valuesCallback(const v8::FunctionCallbackInfo<v8::Value>&);
void debugFunctionCallback(const v8::FunctionCallbackInfo<v8::Value>&);
void undebugFunctionCallback(const v8::FunctionCallbackInfo<v8::Value>&);
void monitorFunctionCallback(const v8::FunctionCallbackInfo<v8::Value>&);
void unmonitorFunctionCallback(const v8::FunctionCallbackInfo<v8::Value>&);
void lastEvaluationResultCallback(const v8::FunctionCallbackInfo<v8::Value>&);
void inspectCallback(const v8::FunctionCallbackInfo<v8::Value>&);
void copyCallback(const v8::FunctionCallbackInfo<v8::Value>&);
void keysCallback(const v8::FunctionCallbackInfo<v8::Value>&, int sessionId);
void valuesCallback(const v8::FunctionCallbackInfo<v8::Value>&,
int sessionId);
void debugFunctionCallback(const v8::FunctionCallbackInfo<v8::Value>&,
int sessionId);
void undebugFunctionCallback(const v8::FunctionCallbackInfo<v8::Value>&,
int sessionId);
void monitorFunctionCallback(const v8::FunctionCallbackInfo<v8::Value>&,
int sessionId);
void unmonitorFunctionCallback(const v8::FunctionCallbackInfo<v8::Value>&,
int sessionId);
void lastEvaluationResultCallback(const v8::FunctionCallbackInfo<v8::Value>&,
int sessionId);
void inspectCallback(const v8::FunctionCallbackInfo<v8::Value>&,
int sessionId);
void copyCallback(const v8::FunctionCallbackInfo<v8::Value>&, int sessionId);
void inspectedObject(const v8::FunctionCallbackInfo<v8::Value>&,
unsigned num);
void inspectedObject0(const v8::FunctionCallbackInfo<v8::Value>& info) {
inspectedObject(info, 0);
int sessionId, unsigned num);
void inspectedObject0(const v8::FunctionCallbackInfo<v8::Value>& info,
int sessionId) {
inspectedObject(info, sessionId, 0);
}
void inspectedObject1(const v8::FunctionCallbackInfo<v8::Value>& info) {
inspectedObject(info, 1);
void inspectedObject1(const v8::FunctionCallbackInfo<v8::Value>& info,
int sessionId) {
inspectedObject(info, sessionId, 1);
}
void inspectedObject2(const v8::FunctionCallbackInfo<v8::Value>& info) {
inspectedObject(info, 2);
void inspectedObject2(const v8::FunctionCallbackInfo<v8::Value>& info,
int sessionId) {
inspectedObject(info, sessionId, 2);
}
void inspectedObject3(const v8::FunctionCallbackInfo<v8::Value>& info) {
inspectedObject(info, 3);
void inspectedObject3(const v8::FunctionCallbackInfo<v8::Value>& info,
int sessionId) {
inspectedObject(info, sessionId, 3);
}
void inspectedObject4(const v8::FunctionCallbackInfo<v8::Value>& info) {
inspectedObject(info, 4);
void inspectedObject4(const v8::FunctionCallbackInfo<v8::Value>& info,
int sessionId) {
inspectedObject(info, sessionId, 4);
}
V8InspectorImpl* m_inspector;
......
......@@ -183,9 +183,11 @@ void V8InspectorSessionImpl::reset() {
void V8InspectorSessionImpl::discardInjectedScripts() {
m_inspectedObjects.clear();
m_inspector->forEachContext(m_contextGroupId, [](InspectedContext* context) {
context->discardInjectedScript();
});
int sessionId = m_sessionId;
m_inspector->forEachContext(m_contextGroupId,
[&sessionId](InspectedContext* context) {
context->discardInjectedScript(sessionId);
});
}
Response V8InspectorSessionImpl::findInjectedScript(
......@@ -194,13 +196,14 @@ Response V8InspectorSessionImpl::findInjectedScript(
InspectedContext* context =
m_inspector->getContext(m_contextGroupId, contextId);
if (!context) return Response::Error("Cannot find context with specified id");
if (!context->getInjectedScript()) {
if (!context->createInjectedScript())
injectedScript = context->getInjectedScript(m_sessionId);
if (!injectedScript) {
if (!context->createInjectedScript(m_sessionId))
return Response::Error("Cannot access specified execution context");
injectedScript = context->getInjectedScript(m_sessionId);
if (m_customObjectFormatterEnabled)
context->getInjectedScript()->setCustomObjectFormatterEnabled(true);
injectedScript->setCustomObjectFormatterEnabled(true);
}
injectedScript = context->getInjectedScript();
return Response::OK();
}
......@@ -214,9 +217,10 @@ void V8InspectorSessionImpl::releaseObjectGroup(const StringView& objectGroup) {
}
void V8InspectorSessionImpl::releaseObjectGroup(const String16& objectGroup) {
int sessionId = m_sessionId;
m_inspector->forEachContext(
m_contextGroupId, [&objectGroup](InspectedContext* context) {
InjectedScript* injectedScript = context->getInjectedScript();
m_contextGroupId, [&objectGroup, &sessionId](InspectedContext* context) {
InjectedScript* injectedScript = context->getInjectedScript(sessionId);
if (injectedScript) injectedScript->releaseObjectGroup(objectGroup);
});
}
......@@ -288,9 +292,10 @@ V8InspectorSessionImpl::wrapTable(v8::Local<v8::Context> context,
void V8InspectorSessionImpl::setCustomObjectFormatterEnabled(bool enabled) {
m_customObjectFormatterEnabled = enabled;
int sessionId = m_sessionId;
m_inspector->forEachContext(
m_contextGroupId, [&enabled](InspectedContext* context) {
InjectedScript* injectedScript = context->getInjectedScript();
m_contextGroupId, [&enabled, &sessionId](InspectedContext* context) {
InjectedScript* injectedScript = context->getInjectedScript(sessionId);
if (injectedScript)
injectedScript->setCustomObjectFormatterEnabled(enabled);
});
......
......@@ -642,6 +642,9 @@ class InspectorExtension : public IsolateData::SetupGlobalTask {
inspector->Set(ToV8String(isolate, "fireContextDestroyed"),
v8::FunctionTemplate::New(
isolate, &InspectorExtension::FireContextDestroyed));
inspector->Set(ToV8String(isolate, "addInspectedObject"),
v8::FunctionTemplate::New(
isolate, &InspectorExtension::AddInspectedObject));
inspector->Set(ToV8String(isolate, "setMaxAsyncTaskStacks"),
v8::FunctionTemplate::New(
isolate, &InspectorExtension::SetMaxAsyncTaskStacks));
......@@ -680,6 +683,18 @@ class InspectorExtension : public IsolateData::SetupGlobalTask {
data->FireContextDestroyed(context);
}
static void AddInspectedObject(
const v8::FunctionCallbackInfo<v8::Value>& args) {
if (args.Length() != 2 || !args[0]->IsInt32()) {
fprintf(stderr,
"Internal error: addInspectedObject(session_id, object).");
Exit();
}
v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext();
IsolateData* data = IsolateData::FromContext(context);
data->AddInspectedObject(args[0].As<v8::Int32>()->Value(), args[1]);
}
static void SetMaxAsyncTaskStacks(
const v8::FunctionCallbackInfo<v8::Value>& args) {
if (args.Length() != 1 || !args[0]->IsInt32()) {
......
......@@ -39,6 +39,19 @@ void Print(v8::Isolate* isolate, const v8_inspector::StringView& string) {
fwrite(*utf8_string, sizeof(**utf8_string), utf8_string.length(), stdout);
}
class Inspectable : public v8_inspector::V8InspectorSession::Inspectable {
public:
Inspectable(v8::Isolate* isolate, v8::Local<v8::Value> object)
: object_(isolate, object) {}
~Inspectable() override {}
v8::Local<v8::Value> get(v8::Local<v8::Context> context) override {
return object_.Get(context->GetIsolate());
}
private:
v8::Global<v8::Value> object_;
};
} // namespace
IsolateData::IsolateData(TaskRunner* task_runner,
......@@ -183,6 +196,14 @@ void IsolateData::AsyncTaskFinished(void* task) {
inspector_->asyncTaskFinished(task);
}
void IsolateData::AddInspectedObject(int session_id,
v8::Local<v8::Value> object) {
auto it = sessions_.find(session_id);
if (it == sessions_.end()) return;
std::unique_ptr<Inspectable> inspectable(new Inspectable(isolate_, object));
it->second->addInspectedObject(std::move(inspectable));
}
void IsolateData::SetMaxAsyncTaskStacksForTest(int limit) {
v8_inspector::SetMaxAsyncTaskStacksForTest(inspector_.get(), limit);
}
......
......@@ -58,6 +58,7 @@ class IsolateData : public v8_inspector::V8InspectorClient {
bool recurring);
void AsyncTaskStarted(void* task);
void AsyncTaskFinished(void* task);
void AddInspectedObject(int session_id, v8::Local<v8::Value> object);
// Test utilities.
void SetCurrentTimeMS(double time);
......
......@@ -186,6 +186,10 @@ InspectorTest.Session = class {
this.id = utils.connectSession(this.contextGroup.id, state, this._dispatchMessage.bind(this));
}
async addInspectedObject(serializable) {
return this.Protocol.Runtime.evaluate({expression: `inspector.addInspectedObject(${this.id}, ${JSON.stringify(serializable)})`});
}
sendRawCommand(requestId, command, handler) {
if (InspectorTest._dumpInspectorProtocolMessages)
utils.print("frontend: " + command);
......
Tests that multiple sessions do not share command line api.
Setting $0 in 1
Evaluating $0 in 1
{
id : <messageId>
result : {
result : {
description : 42
type : number
value : 42
}
}
}
Evaluating $0 in 2
{
id : <messageId>
result : {
result : {
type : undefined
}
}
}
Setting $0 in 2
Evaluating $0 in 1
{
id : <messageId>
result : {
result : {
description : 42
type : number
value : 42
}
}
}
Evaluating $0 in 2
{
id : <messageId>
result : {
result : {
description : 17
type : number
value : 17
}
}
}
Setting $_ in 1
Evaluating $_ in 1
{
id : <messageId>
result : {
result : {
description : 42
type : number
value : 42
}
}
}
Evaluating $_ in 2
{
id : <messageId>
result : {
result : {
type : undefined
}
}
}
Setting $_ in 2
Evaluating $_ in 1
{
id : <messageId>
result : {
result : {
description : 42
type : number
value : 42
}
}
}
Evaluating $_ in 2
{
id : <messageId>
result : {
result : {
description : 17
type : number
value : 17
}
}
}
Inspecting in 1
inspectRequested from 1
{
method : Runtime.inspectRequested
params : {
hints : {
}
object : {
description : 42
type : number
value : 42
}
}
}
Inspecting in 1 through variable
inspectRequested from 1
{
method : Runtime.inspectRequested
params : {
hints : {
}
object : {
description : 42
type : number
value : 42
}
}
}
Inspecting in 2
inspectRequested from 2
{
method : Runtime.inspectRequested
params : {
hints : {
}
object : {
description : 17
type : number
value : 17
}
}
}
Inspecting in 2 through variable
inspectRequested from 2
{
method : Runtime.inspectRequested
params : {
hints : {
}
object : {
description : 17
type : number
value : 17
}
}
}
Inspecting in 2 through variable from 1
inspectRequested from 1
{
method : Runtime.inspectRequested
params : {
hints : {
}
object : {
description : 42
type : number
value : 42
}
}
}
Disconnecting 1
Evaluating $0 in 2
{
id : <messageId>
result : {
result : {
description : 17
type : number
value : 17
}
}
}
Evaluating $_ in 2
{
id : <messageId>
result : {
result : {
description : 17
type : number
value : 17
}
}
}
Inspecting in 2
inspectRequested from 2
{
method : Runtime.inspectRequested
params : {
hints : {
}
object : {
description : 17
type : number
value : 17
}
}
}
Inspecting in 2 through variable from 1
Inspecting in 2 through variable
inspectRequested from 2
{
method : Runtime.inspectRequested
params : {
hints : {
}
object : {
description : 17
type : number
value : 17
}
}
}
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
InspectorTest.log('Tests that multiple sessions do not share command line api.');
(async function test() {
var contextGroup = new InspectorTest.ContextGroup();
var session1 = contextGroup.connect();
await session1.Protocol.Runtime.enable();
session1.Protocol.Runtime.onInspectRequested(message => {
InspectorTest.log('inspectRequested from 1');
InspectorTest.logMessage(message);
});
var session2 = contextGroup.connect();
await session2.Protocol.Runtime.enable();
session2.Protocol.Runtime.onInspectRequested(message => {
InspectorTest.log('inspectRequested from 2');
InspectorTest.logMessage(message);
});
InspectorTest.log('Setting $0 in 1');
await session1.addInspectedObject(42);
InspectorTest.log('Evaluating $0 in 1');
InspectorTest.logMessage(await session1.Protocol.Runtime.evaluate({expression: '$0', includeCommandLineAPI: true}));
InspectorTest.log('Evaluating $0 in 2');
InspectorTest.logMessage(await session2.Protocol.Runtime.evaluate({expression: '$0', includeCommandLineAPI: true}));
InspectorTest.log('Setting $0 in 2');
await session2.addInspectedObject(17);
InspectorTest.log('Evaluating $0 in 1');
InspectorTest.logMessage(await session1.Protocol.Runtime.evaluate({expression: '$0', includeCommandLineAPI: true}));
InspectorTest.log('Evaluating $0 in 2');
InspectorTest.logMessage(await session2.Protocol.Runtime.evaluate({expression: '$0', includeCommandLineAPI: true}));
InspectorTest.log('Setting $_ in 1');
await session1.Protocol.Runtime.evaluate({expression: '42', objectGroup: 'console', includeCommandLineAPI: true});
InspectorTest.log('Evaluating $_ in 1');
InspectorTest.logMessage(await session1.Protocol.Runtime.evaluate({expression: '$_', includeCommandLineAPI: true}));
InspectorTest.log('Evaluating $_ in 2');
InspectorTest.logMessage(await session2.Protocol.Runtime.evaluate({expression: '$_', includeCommandLineAPI: true}));
InspectorTest.log('Setting $_ in 2');
await session2.Protocol.Runtime.evaluate({expression: '17', objectGroup: 'console', includeCommandLineAPI: true});
InspectorTest.log('Evaluating $_ in 1');
InspectorTest.logMessage(await session1.Protocol.Runtime.evaluate({expression: '$_', includeCommandLineAPI: true}));
InspectorTest.log('Evaluating $_ in 2');
InspectorTest.logMessage(await session2.Protocol.Runtime.evaluate({expression: '$_', includeCommandLineAPI: true}));
InspectorTest.log('Inspecting in 1');
await session1.Protocol.Runtime.evaluate({expression: 'var inspect1=inspect; inspect(42)', includeCommandLineAPI: true});
InspectorTest.log('Inspecting in 1 through variable');
await session1.Protocol.Runtime.evaluate({expression: 'inspect1(42)', includeCommandLineAPI: true});
InspectorTest.log('Inspecting in 2');
await session2.Protocol.Runtime.evaluate({expression: 'var inspect2=inspect; inspect(17)', includeCommandLineAPI: true});
InspectorTest.log('Inspecting in 2 through variable');
await session2.Protocol.Runtime.evaluate({expression: 'inspect2(17)', includeCommandLineAPI: true});
InspectorTest.log('Inspecting in 2 through variable from 1');
await session2.Protocol.Runtime.evaluate({expression: 'inspect1(42)', includeCommandLineAPI: true});
InspectorTest.log('Disconnecting 1');
session1.disconnect();
InspectorTest.log('Evaluating $0 in 2');
InspectorTest.logMessage(await session2.Protocol.Runtime.evaluate({expression: '$0', includeCommandLineAPI: true}));
InspectorTest.log('Evaluating $_ in 2');
InspectorTest.logMessage(await session2.Protocol.Runtime.evaluate({expression: '$_', includeCommandLineAPI: true}));
InspectorTest.log('Inspecting in 2');
await session2.Protocol.Runtime.evaluate({expression: 'inspect(17)', includeCommandLineAPI: true});
InspectorTest.log('Inspecting in 2 through variable from 1');
await session2.Protocol.Runtime.evaluate({expression: 'inspect1(42)', includeCommandLineAPI: true});
InspectorTest.log('Inspecting in 2 through variable');
await session2.Protocol.Runtime.evaluate({expression: 'inspect2(17)', includeCommandLineAPI: true});
InspectorTest.completeTest();
})();
Tests that multiple sessions do not interfere with each other's remote objects.
Evaluating in 1
Evaluating in 2
Retrieving properties in 2
{
id : <messageId>
result : {
result : [
[0] : {
configurable : true
enumerable : true
isOwn : true
name : a
value : {
description : 17
type : number
value : 17
}
writable : true
}
[1] : {
configurable : true
enumerable : false
isOwn : true
name : __proto__
value : {
className : Object
description : Object
objectId : <objectId>
type : object
}
writable : true
}
]
}
}
Retrieving properties in 1
{
id : <messageId>
result : {
result : [
[0] : {
configurable : true
enumerable : true
isOwn : true
name : a
value : {
description : 42
type : number
value : 42
}
writable : true
}
[1] : {
configurable : true
enumerable : false
isOwn : true
name : __proto__
value : {
className : Object
description : Object
objectId : <objectId>
type : object
}
writable : true
}
]
}
}
Disconnecting 2
Retrieving properties in 1
{
id : <messageId>
result : {
result : [
[0] : {
configurable : true
enumerable : true
isOwn : true
name : a
value : {
description : 42
type : number
value : 42
}
writable : true
}
[1] : {
configurable : true
enumerable : false
isOwn : true
name : __proto__
value : {
className : Object
description : Object
objectId : <objectId>
type : object
}
writable : true
}
]
}
}
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
InspectorTest.log('Tests that multiple sessions do not interfere with each other\'s remote objects.');
(async function test() {
var contextGroup = new InspectorTest.ContextGroup();
var session1 = contextGroup.connect();
var session2 = contextGroup.connect();
InspectorTest.log('Evaluating in 1');
var result1 = await session1.Protocol.Runtime.evaluate({expression: '({a: 42})'});
InspectorTest.log('Evaluating in 2');
var result2 = await session2.Protocol.Runtime.evaluate({expression: '({a: 17})'});
await print(2, session2, result2);
await print(1, session1, result1);
InspectorTest.log('Disconnecting 2');
session2.disconnect();
await print(1, session1, result1);
InspectorTest.completeTest();
})();
async function print(num, session, message) {
InspectorTest.log('Retrieving properties in ' + num);
var objectId = message.result.result.objectId;
InspectorTest.logMessage(await session.Protocol.Runtime.getProperties({objectId, ownProperties: true}));
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment