[inspector] Support multiple sessions per context group
This patch adds ability to connect multiple sessions to a single context group. This is an experimental feature, which is already supported in test harness. So far covered runtime domain with tests (and found a bug thanks to the test). More tests to follow in next patches, probably with code adjustments as well. BUG=chromium:590878 Review-Url: https://codereview.chromium.org/2906153002 Cr-Commit-Position: refs/heads/master@{#45667}
This commit is contained in:
parent
fa89ce5349
commit
375bea1c45
@ -428,7 +428,8 @@ InjectedScript::Scope::Scope(V8InspectorSessionImpl* session)
|
||||
|
||||
Response InjectedScript::Scope::initialize() {
|
||||
cleanup();
|
||||
V8InspectorSessionImpl* session = m_inspector->sessionById(m_sessionId);
|
||||
V8InspectorSessionImpl* session =
|
||||
m_inspector->sessionById(m_contextGroupId, m_sessionId);
|
||||
if (!session) return Response::InternalError();
|
||||
Response response = findInjectedScript(session);
|
||||
if (!response.isSuccess()) return response;
|
||||
|
@ -23,8 +23,7 @@ InspectedContext::InspectedContext(V8InspectorImpl* inspector,
|
||||
m_contextGroupId(info.contextGroupId),
|
||||
m_origin(toString16(info.origin)),
|
||||
m_humanReadableName(toString16(info.humanReadableName)),
|
||||
m_auxData(toString16(info.auxData)),
|
||||
m_reported(false) {
|
||||
m_auxData(toString16(info.auxData)) {
|
||||
v8::debug::SetContextId(info.context, contextId);
|
||||
if (!info.hasMemoryOnConsole) return;
|
||||
v8::Context::Scope contextScope(info.context);
|
||||
@ -54,6 +53,17 @@ v8::Isolate* InspectedContext::isolate() const {
|
||||
return m_inspector->isolate();
|
||||
}
|
||||
|
||||
bool InspectedContext::isReported(int sessionId) const {
|
||||
return m_reportedSessionIds.find(sessionId) != m_reportedSessionIds.cend();
|
||||
}
|
||||
|
||||
void InspectedContext::setReported(int sessionId, bool reported) {
|
||||
if (reported)
|
||||
m_reportedSessionIds.insert(sessionId);
|
||||
else
|
||||
m_reportedSessionIds.erase(sessionId);
|
||||
}
|
||||
|
||||
bool InspectedContext::createInjectedScript() {
|
||||
DCHECK(!m_injectedScript);
|
||||
std::unique_ptr<InjectedScript> injectedScript = InjectedScript::create(this);
|
||||
|
@ -5,6 +5,9 @@
|
||||
#ifndef V8_INSPECTOR_INSPECTEDCONTEXT_H_
|
||||
#define V8_INSPECTOR_INSPECTEDCONTEXT_H_
|
||||
|
||||
#include <map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "src/base/macros.h"
|
||||
#include "src/inspector/string-16.h"
|
||||
|
||||
@ -30,8 +33,8 @@ class InspectedContext {
|
||||
String16 humanReadableName() const { return m_humanReadableName; }
|
||||
String16 auxData() const { return m_auxData; }
|
||||
|
||||
bool isReported() const { return m_reported; }
|
||||
void setReported(bool reported) { m_reported = reported; }
|
||||
bool isReported(int sessionId) const;
|
||||
void setReported(int sessionId, bool reported);
|
||||
|
||||
v8::Isolate* isolate() const;
|
||||
V8InspectorImpl* inspector() const { return m_inspector; }
|
||||
@ -51,7 +54,7 @@ class InspectedContext {
|
||||
const String16 m_origin;
|
||||
const String16 m_humanReadableName;
|
||||
const String16 m_auxData;
|
||||
bool m_reported;
|
||||
std::unordered_set<int> m_reportedSessionIds;
|
||||
std::unique_ptr<InjectedScript> m_injectedScript;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(InspectedContext);
|
||||
|
@ -459,13 +459,12 @@ void V8ConsoleMessageStorage::addMessage(
|
||||
V8InspectorImpl* inspector = m_inspector;
|
||||
if (message->type() == ConsoleAPIType::kClear) clear();
|
||||
|
||||
V8InspectorSessionImpl* session =
|
||||
inspector->sessionForContextGroup(contextGroupId);
|
||||
if (session) {
|
||||
if (message->origin() == V8MessageOrigin::kConsole)
|
||||
session->consoleAgent()->messageAdded(message.get());
|
||||
session->runtimeAgent()->messageAdded(message.get());
|
||||
}
|
||||
inspector->forEachSession(
|
||||
contextGroupId, [&message](V8InspectorSessionImpl* session) {
|
||||
if (message->origin() == V8MessageOrigin::kConsole)
|
||||
session->consoleAgent()->messageAdded(message.get());
|
||||
session->runtimeAgent()->messageAdded(message.get());
|
||||
});
|
||||
if (!inspector->hasConsoleMessageStorage(contextGroupId)) return;
|
||||
|
||||
DCHECK(m_messages.size() <= maxConsoleMessageCount);
|
||||
@ -486,10 +485,10 @@ void V8ConsoleMessageStorage::addMessage(
|
||||
void V8ConsoleMessageStorage::clear() {
|
||||
m_messages.clear();
|
||||
m_estimatedSize = 0;
|
||||
if (V8InspectorSessionImpl* session =
|
||||
m_inspector->sessionForContextGroup(m_contextGroupId)) {
|
||||
session->releaseObjectGroup("console");
|
||||
}
|
||||
m_inspector->forEachSession(m_contextGroupId,
|
||||
[](V8InspectorSessionImpl* session) {
|
||||
session->releaseObjectGroup("console");
|
||||
});
|
||||
m_data.clear();
|
||||
}
|
||||
|
||||
|
@ -124,24 +124,8 @@ class ConsoleHelper {
|
||||
return func;
|
||||
}
|
||||
|
||||
V8ProfilerAgentImpl* profilerAgent() {
|
||||
if (V8InspectorSessionImpl* session = currentSession()) {
|
||||
if (session && session->profilerAgent()->enabled())
|
||||
return session->profilerAgent();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
V8DebuggerAgentImpl* debuggerAgent() {
|
||||
if (V8InspectorSessionImpl* session = currentSession()) {
|
||||
if (session && session->debuggerAgent()->enabled())
|
||||
return session->debuggerAgent();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
V8InspectorSessionImpl* currentSession() {
|
||||
return m_inspector->sessionForContextGroup(m_groupId);
|
||||
void forEachSession(std::function<void(V8InspectorSessionImpl*)> callback) {
|
||||
m_inspector->forEachSession(m_groupId, callback);
|
||||
}
|
||||
|
||||
private:
|
||||
@ -288,9 +272,12 @@ void V8Console::Assert(const v8::debug::ConsoleCallArguments& info) {
|
||||
toV8String(m_inspector->isolate(), String16("console.assert")));
|
||||
helper.reportCall(ConsoleAPIType::kAssert, arguments);
|
||||
|
||||
if (V8DebuggerAgentImpl* debuggerAgent = helper.debuggerAgent())
|
||||
debuggerAgent->breakProgramOnException(
|
||||
protocol::Debugger::Paused::ReasonEnum::Assert, nullptr);
|
||||
helper.forEachSession([](V8InspectorSessionImpl* session) {
|
||||
if (session->debuggerAgent()->enabled()) {
|
||||
session->debuggerAgent()->breakProgramOnException(
|
||||
protocol::Debugger::Paused::ReasonEnum::Assert, nullptr);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void V8Console::MarkTimeline(const v8::debug::ConsoleCallArguments& info) {
|
||||
@ -304,14 +291,18 @@ void V8Console::MarkTimeline(const v8::debug::ConsoleCallArguments& info) {
|
||||
|
||||
void V8Console::Profile(const v8::debug::ConsoleCallArguments& info) {
|
||||
ConsoleHelper helper(info, m_inspector);
|
||||
if (V8ProfilerAgentImpl* profilerAgent = helper.profilerAgent())
|
||||
profilerAgent->consoleProfile(helper.firstArgToString(String16()));
|
||||
helper.forEachSession([&helper](V8InspectorSessionImpl* session) {
|
||||
session->profilerAgent()->consoleProfile(
|
||||
helper.firstArgToString(String16()));
|
||||
});
|
||||
}
|
||||
|
||||
void V8Console::ProfileEnd(const v8::debug::ConsoleCallArguments& info) {
|
||||
ConsoleHelper helper(info, m_inspector);
|
||||
if (V8ProfilerAgentImpl* profilerAgent = helper.profilerAgent())
|
||||
profilerAgent->consoleProfileEnd(helper.firstArgToString(String16()));
|
||||
helper.forEachSession([&helper](V8InspectorSessionImpl* session) {
|
||||
session->profilerAgent()->consoleProfileEnd(
|
||||
helper.firstArgToString(String16()));
|
||||
});
|
||||
}
|
||||
|
||||
static void timeFunction(const v8::debug::ConsoleCallArguments& info,
|
||||
@ -426,20 +417,24 @@ static void setFunctionBreakpoint(ConsoleHelper& helper,
|
||||
v8::Local<v8::Function> function,
|
||||
V8DebuggerAgentImpl::BreakpointSource source,
|
||||
const String16& condition, bool enable) {
|
||||
V8DebuggerAgentImpl* debuggerAgent = helper.debuggerAgent();
|
||||
if (!debuggerAgent) return;
|
||||
String16 scriptId = String16::fromInteger(function->ScriptId());
|
||||
int lineNumber = function->GetScriptLineNumber();
|
||||
int columnNumber = function->GetScriptColumnNumber();
|
||||
if (lineNumber == v8::Function::kLineOffsetNotFound ||
|
||||
columnNumber == v8::Function::kLineOffsetNotFound)
|
||||
return;
|
||||
if (enable)
|
||||
debuggerAgent->setBreakpointAt(scriptId, lineNumber, columnNumber, source,
|
||||
condition);
|
||||
else
|
||||
debuggerAgent->removeBreakpointAt(scriptId, lineNumber, columnNumber,
|
||||
source);
|
||||
|
||||
helper.forEachSession([&enable, &scriptId, &lineNumber, &columnNumber,
|
||||
&source, &condition](V8InspectorSessionImpl* session) {
|
||||
if (!session->debuggerAgent()->enabled()) return;
|
||||
if (enable) {
|
||||
session->debuggerAgent()->setBreakpointAt(
|
||||
scriptId, lineNumber, columnNumber, source, condition);
|
||||
} else {
|
||||
session->debuggerAgent()->removeBreakpointAt(scriptId, lineNumber,
|
||||
columnNumber, source);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void V8Console::debugFunctionCallback(
|
||||
@ -526,10 +521,11 @@ static void inspectImpl(const v8::FunctionCallbackInfo<v8::Value>& info,
|
||||
std::unique_ptr<protocol::DictionaryValue> hints =
|
||||
protocol::DictionaryValue::create();
|
||||
if (copyToClipboard) hints->setBoolean("copyToClipboard", true);
|
||||
if (V8InspectorSessionImpl* session = helper.currentSession()) {
|
||||
session->runtimeAgent()->inspect(std::move(wrappedObject),
|
||||
std::move(hints));
|
||||
}
|
||||
helper.forEachSession(
|
||||
[&wrappedObject, &hints](V8InspectorSessionImpl* session) {
|
||||
session->runtimeAgent()->inspect(std::move(wrappedObject),
|
||||
std::move(hints));
|
||||
});
|
||||
}
|
||||
|
||||
void V8Console::inspectCallback(
|
||||
@ -546,14 +542,14 @@ void V8Console::inspectedObject(const v8::FunctionCallbackInfo<v8::Value>& info,
|
||||
DCHECK(num < V8InspectorSessionImpl::kInspectedObjectBufferSize);
|
||||
v8::debug::ConsoleCallArguments args(info);
|
||||
ConsoleHelper helper(args, m_inspector);
|
||||
if (V8InspectorSessionImpl* session = helper.currentSession()) {
|
||||
helper.forEachSession([&info, &num](V8InspectorSessionImpl* session) {
|
||||
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,
|
||||
|
@ -1238,7 +1238,15 @@ void V8DebuggerAgentImpl::breakProgram(
|
||||
std::vector<BreakReason> currentScheduledReason;
|
||||
currentScheduledReason.swap(m_breakReason);
|
||||
pushBreakDetails(breakReason, std::move(data));
|
||||
if (!m_debugger->breakProgram(m_session->contextGroupId())) return;
|
||||
|
||||
int contextGroupId = m_session->contextGroupId();
|
||||
int sessionId = m_session->sessionId();
|
||||
V8InspectorImpl* inspector = m_inspector;
|
||||
m_debugger->breakProgram(contextGroupId);
|
||||
// Check that session and |this| are still around.
|
||||
if (!inspector->sessionById(contextGroupId, sessionId)) return;
|
||||
if (!enabled()) return;
|
||||
|
||||
popBreakDetails();
|
||||
m_breakReason.swap(currentScheduledReason);
|
||||
if (!m_breakReason.empty()) {
|
||||
|
@ -347,16 +347,13 @@ bool V8Debugger::canBreakProgram() {
|
||||
return !v8::debug::AllFramesOnStackAreBlackboxed(m_isolate);
|
||||
}
|
||||
|
||||
bool V8Debugger::breakProgram(int targetContextGroupId) {
|
||||
void V8Debugger::breakProgram(int targetContextGroupId) {
|
||||
// Don't allow nested breaks.
|
||||
if (isPaused()) return true;
|
||||
if (!canBreakProgram()) return true;
|
||||
if (isPaused()) return;
|
||||
if (!canBreakProgram()) return;
|
||||
DCHECK(targetContextGroupId);
|
||||
m_targetContextGroupId = targetContextGroupId;
|
||||
v8::debug::BreakRightNow(m_isolate);
|
||||
V8InspectorSessionImpl* session =
|
||||
m_inspector->sessionForContextGroup(targetContextGroupId);
|
||||
return session && session->debuggerAgent()->enabled();
|
||||
}
|
||||
|
||||
void V8Debugger::continueProgram(int targetContextGroupId) {
|
||||
@ -600,10 +597,19 @@ void V8Debugger::handleProgramBreak(v8::Local<v8::Context> pausedContext,
|
||||
m_stepIntoAsyncCallback.reset();
|
||||
}
|
||||
m_breakRequested = false;
|
||||
V8InspectorSessionImpl* session =
|
||||
m_inspector->sessionForContextGroup(contextGroupId);
|
||||
if (!session || !session->debuggerAgent()->enabled()) return;
|
||||
if (!m_scheduledOOMBreak && session->debuggerAgent()->skipAllPauses()) return;
|
||||
|
||||
bool scheduledOOMBreak = m_scheduledOOMBreak;
|
||||
auto agentCheck = [&scheduledOOMBreak](V8DebuggerAgentImpl* agent) {
|
||||
return agent->enabled() && (scheduledOOMBreak || !agent->skipAllPauses());
|
||||
};
|
||||
|
||||
bool hasAgents = false;
|
||||
m_inspector->forEachSession(
|
||||
contextGroupId,
|
||||
[&agentCheck, &hasAgents](V8InspectorSessionImpl* session) {
|
||||
if (agentCheck(session->debuggerAgent())) hasAgents = true;
|
||||
});
|
||||
if (!hasAgents) return;
|
||||
|
||||
std::vector<String16> breakpointIds;
|
||||
if (!hitBreakpointNumbers.IsEmpty()) {
|
||||
@ -627,9 +633,17 @@ void V8Debugger::handleProgramBreak(v8::Local<v8::Context> pausedContext,
|
||||
m_pausedContext = pausedContext;
|
||||
m_executionState = executionState;
|
||||
m_pausedContextGroupId = contextGroupId;
|
||||
session->debuggerAgent()->didPause(
|
||||
InspectedContext::contextId(pausedContext), exception, breakpointIds,
|
||||
isPromiseRejection, isUncaught, m_scheduledOOMBreak);
|
||||
|
||||
m_inspector->forEachSession(
|
||||
contextGroupId, [&agentCheck, &pausedContext, &exception, &breakpointIds,
|
||||
&isPromiseRejection, &isUncaught,
|
||||
&scheduledOOMBreak](V8InspectorSessionImpl* session) {
|
||||
if (agentCheck(session->debuggerAgent())) {
|
||||
session->debuggerAgent()->didPause(
|
||||
InspectedContext::contextId(pausedContext), exception,
|
||||
breakpointIds, isPromiseRejection, isUncaught, scheduledOOMBreak);
|
||||
}
|
||||
});
|
||||
{
|
||||
v8::Context::Scope scope(pausedContext);
|
||||
v8::Local<v8::Context> context = m_isolate->GetCurrentContext();
|
||||
@ -638,10 +652,12 @@ void V8Debugger::handleProgramBreak(v8::Local<v8::Context> pausedContext,
|
||||
m_inspector->client()->runMessageLoopOnPause(contextGroupId);
|
||||
m_pausedContextGroupId = 0;
|
||||
}
|
||||
// The agent may have been removed in the nested loop.
|
||||
session = m_inspector->sessionForContextGroup(contextGroupId);
|
||||
if (session && session->debuggerAgent()->enabled())
|
||||
session->debuggerAgent()->didContinue();
|
||||
m_inspector->forEachSession(contextGroupId,
|
||||
[](V8InspectorSessionImpl* session) {
|
||||
if (session->debuggerAgent()->enabled())
|
||||
session->debuggerAgent()->didContinue();
|
||||
});
|
||||
|
||||
if (m_scheduledOOMBreak) m_isolate->RestoreOriginalHeapLimit();
|
||||
m_scheduledOOMBreak = false;
|
||||
m_pausedContext.Clear();
|
||||
@ -662,16 +678,26 @@ void V8Debugger::ScriptCompiled(v8::Local<v8::debug::Script> script,
|
||||
bool has_compile_error) {
|
||||
int contextId;
|
||||
if (!script->ContextId().To(&contextId)) return;
|
||||
V8InspectorSessionImpl* session = m_inspector->sessionForContextGroup(
|
||||
m_inspector->contextGroupId(contextId));
|
||||
if (!session || !session->debuggerAgent()->enabled()) return;
|
||||
if (script->IsWasm()) {
|
||||
m_wasmTranslation.AddScript(script.As<v8::debug::WasmScript>(),
|
||||
session->debuggerAgent());
|
||||
WasmTranslation* wasmTranslation = &m_wasmTranslation;
|
||||
m_inspector->forEachSession(
|
||||
m_inspector->contextGroupId(contextId),
|
||||
[&script, &wasmTranslation](V8InspectorSessionImpl* session) {
|
||||
if (!session->debuggerAgent()->enabled()) return;
|
||||
wasmTranslation->AddScript(script.As<v8::debug::WasmScript>(),
|
||||
session->debuggerAgent());
|
||||
});
|
||||
} else if (m_ignoreScriptParsedEventsCounter == 0) {
|
||||
session->debuggerAgent()->didParseSource(
|
||||
V8DebuggerScript::Create(m_isolate, script, inLiveEditScope),
|
||||
!has_compile_error);
|
||||
v8::Isolate* isolate = m_isolate;
|
||||
m_inspector->forEachSession(
|
||||
m_inspector->contextGroupId(contextId),
|
||||
[&isolate, &script,
|
||||
&has_compile_error](V8InspectorSessionImpl* session) {
|
||||
if (!session->debuggerAgent()->enabled()) return;
|
||||
session->debuggerAgent()->didParseSource(
|
||||
V8DebuggerScript::Create(isolate, script, inLiveEditScope),
|
||||
!has_compile_error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -704,11 +730,19 @@ bool V8Debugger::IsFunctionBlackboxed(v8::Local<v8::debug::Script> script,
|
||||
const v8::debug::Location& end) {
|
||||
int contextId;
|
||||
if (!script->ContextId().To(&contextId)) return false;
|
||||
V8InspectorSessionImpl* session = m_inspector->sessionForContextGroup(
|
||||
m_inspector->contextGroupId(contextId));
|
||||
if (!session || !session->debuggerAgent()->enabled()) return false;
|
||||
return session->debuggerAgent()->isFunctionBlackboxed(
|
||||
String16::fromInteger(script->Id()), start, end);
|
||||
bool hasAgents = false;
|
||||
bool allBlackboxed = true;
|
||||
String16 scriptId = String16::fromInteger(script->Id());
|
||||
m_inspector->forEachSession(
|
||||
m_inspector->contextGroupId(contextId),
|
||||
[&hasAgents, &allBlackboxed, &scriptId, &start,
|
||||
&end](V8InspectorSessionImpl* session) {
|
||||
V8DebuggerAgentImpl* agent = session->debuggerAgent();
|
||||
if (!agent->enabled()) return;
|
||||
hasAgents = true;
|
||||
allBlackboxed &= agent->isFunctionBlackboxed(scriptId, start, end);
|
||||
});
|
||||
return hasAgents && allBlackboxed;
|
||||
}
|
||||
|
||||
void V8Debugger::PromiseEventOccurred(v8::debug::PromiseDebugActionType type,
|
||||
@ -1084,10 +1118,14 @@ std::unique_ptr<V8StackTraceImpl> V8Debugger::captureStackTrace(
|
||||
if (!contextGroupId) return nullptr;
|
||||
|
||||
int stackSize = 1;
|
||||
V8InspectorSessionImpl* session =
|
||||
m_inspector->sessionForContextGroup(contextGroupId);
|
||||
if (fullStack || (session && session->runtimeAgent()->enabled())) {
|
||||
if (fullStack) {
|
||||
stackSize = V8StackTraceImpl::maxCallStackSizeToCapture;
|
||||
} else {
|
||||
m_inspector->forEachSession(
|
||||
contextGroupId, [&stackSize](V8InspectorSessionImpl* session) {
|
||||
if (session->runtimeAgent()->enabled())
|
||||
stackSize = V8StackTraceImpl::maxCallStackSizeToCapture;
|
||||
});
|
||||
}
|
||||
return V8StackTraceImpl::capture(this, contextGroupId, stackSize);
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ class V8Debugger : public v8::debug::DebugDelegate {
|
||||
v8::debug::ExceptionBreakState getPauseOnExceptionsState();
|
||||
void setPauseOnExceptionsState(v8::debug::ExceptionBreakState);
|
||||
bool canBreakProgram();
|
||||
bool breakProgram(int targetContextGroupId);
|
||||
void breakProgram(int targetContextGroupId);
|
||||
void continueProgram(int targetContextGroupId);
|
||||
|
||||
void setPauseOnNextStatement(bool, int targetContextGroupId);
|
||||
|
@ -151,20 +151,18 @@ std::unique_ptr<V8StackTrace> V8InspectorImpl::createStackTrace(
|
||||
std::unique_ptr<V8InspectorSession> V8InspectorImpl::connect(
|
||||
int contextGroupId, V8Inspector::Channel* channel,
|
||||
const StringView& state) {
|
||||
DCHECK(m_sessions.find(contextGroupId) == m_sessions.cend());
|
||||
int sessionId = ++m_lastSessionId;
|
||||
std::unique_ptr<V8InspectorSessionImpl> session =
|
||||
V8InspectorSessionImpl::create(this, contextGroupId, sessionId, channel,
|
||||
state);
|
||||
m_sessions[contextGroupId] = session.get();
|
||||
m_sessionById[sessionId] = session.get();
|
||||
m_sessions[contextGroupId][sessionId] = session.get();
|
||||
return std::move(session);
|
||||
}
|
||||
|
||||
void V8InspectorImpl::disconnect(V8InspectorSessionImpl* session) {
|
||||
DCHECK(m_sessions.find(session->contextGroupId()) != m_sessions.end());
|
||||
m_sessions.erase(session->contextGroupId());
|
||||
m_sessionById.erase(session->sessionId());
|
||||
auto& map = m_sessions[session->contextGroupId()];
|
||||
map.erase(session->sessionId());
|
||||
if (map.empty()) m_sessions.erase(session->contextGroupId());
|
||||
}
|
||||
|
||||
InspectedContext* V8InspectorImpl::getContext(int groupId,
|
||||
@ -196,9 +194,10 @@ void V8InspectorImpl::contextCreated(const V8ContextInfo& info) {
|
||||
|
||||
DCHECK(contextById->find(contextId) == contextById->cend());
|
||||
(*contextById)[contextId].reset(context);
|
||||
SessionMap::iterator sessionIt = m_sessions.find(info.contextGroupId);
|
||||
if (sessionIt != m_sessions.end())
|
||||
sessionIt->second->runtimeAgent()->reportExecutionContextCreated(context);
|
||||
forEachSession(
|
||||
info.contextGroupId, [&context](V8InspectorSessionImpl* session) {
|
||||
session->runtimeAgent()->reportExecutionContextCreated(context);
|
||||
});
|
||||
}
|
||||
|
||||
void V8InspectorImpl::contextDestroyed(v8::Local<v8::Context> context) {
|
||||
@ -213,31 +212,34 @@ void V8InspectorImpl::contextDestroyed(v8::Local<v8::Context> context) {
|
||||
InspectedContext* inspectedContext = getContext(groupId, contextId);
|
||||
if (!inspectedContext) return;
|
||||
|
||||
SessionMap::iterator iter = m_sessions.find(groupId);
|
||||
if (iter != m_sessions.end())
|
||||
iter->second->runtimeAgent()->reportExecutionContextDestroyed(
|
||||
inspectedContext);
|
||||
forEachSession(groupId, [&inspectedContext](V8InspectorSessionImpl* session) {
|
||||
session->runtimeAgent()->reportExecutionContextDestroyed(inspectedContext);
|
||||
});
|
||||
discardInspectedContext(groupId, contextId);
|
||||
}
|
||||
|
||||
void V8InspectorImpl::resetContextGroup(int contextGroupId) {
|
||||
m_consoleStorageMap.erase(contextGroupId);
|
||||
m_muteExceptionsMap.erase(contextGroupId);
|
||||
SessionMap::iterator session = m_sessions.find(contextGroupId);
|
||||
if (session != m_sessions.end()) session->second->reset();
|
||||
forEachSession(contextGroupId,
|
||||
[](V8InspectorSessionImpl* session) { session->reset(); });
|
||||
m_contexts.erase(contextGroupId);
|
||||
m_debugger->wasmTranslation()->Clear();
|
||||
}
|
||||
|
||||
void V8InspectorImpl::idleStarted() {
|
||||
for (auto it = m_sessions.begin(); it != m_sessions.end(); ++it) {
|
||||
if (it->second->profilerAgent()->idleStarted()) return;
|
||||
for (auto& it : m_sessions) {
|
||||
for (auto& it2 : it.second) {
|
||||
if (it2.second->profilerAgent()->idleStarted()) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void V8InspectorImpl::idleFinished() {
|
||||
for (auto it = m_sessions.begin(); it != m_sessions.end(); ++it) {
|
||||
if (it->second->profilerAgent()->idleFinished()) return;
|
||||
for (auto& it : m_sessions) {
|
||||
for (auto& it2 : it.second) {
|
||||
if (it2.second->profilerAgent()->idleFinished()) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -312,16 +314,12 @@ void V8InspectorImpl::discardInspectedContext(int contextGroupId,
|
||||
if (m_contexts[contextGroupId]->empty()) m_contexts.erase(contextGroupId);
|
||||
}
|
||||
|
||||
V8InspectorSessionImpl* V8InspectorImpl::sessionForContextGroup(
|
||||
int contextGroupId) {
|
||||
if (!contextGroupId) return nullptr;
|
||||
SessionMap::iterator iter = m_sessions.find(contextGroupId);
|
||||
return iter == m_sessions.end() ? nullptr : iter->second;
|
||||
}
|
||||
|
||||
V8InspectorSessionImpl* V8InspectorImpl::sessionById(int sessionId) {
|
||||
auto it = m_sessionById.find(sessionId);
|
||||
return it == m_sessionById.end() ? nullptr : it->second;
|
||||
V8InspectorSessionImpl* V8InspectorImpl::sessionById(int contextGroupId,
|
||||
int sessionId) {
|
||||
auto it = m_sessions.find(contextGroupId);
|
||||
if (it == m_sessions.end()) return nullptr;
|
||||
auto it2 = it->second.find(sessionId);
|
||||
return it2 == it->second.end() ? nullptr : it2->second;
|
||||
}
|
||||
|
||||
V8Console* V8InspectorImpl::console() {
|
||||
@ -346,4 +344,21 @@ void V8InspectorImpl::forEachContext(
|
||||
}
|
||||
}
|
||||
|
||||
void V8InspectorImpl::forEachSession(
|
||||
int contextGroupId, std::function<void(V8InspectorSessionImpl*)> callback) {
|
||||
auto it = m_sessions.find(contextGroupId);
|
||||
if (it == m_sessions.end()) return;
|
||||
std::vector<int> ids;
|
||||
ids.reserve(it->second.size());
|
||||
for (auto& sessionIt : it->second) ids.push_back(sessionIt.first);
|
||||
|
||||
// Retrieve by ids each time since |callback| may destroy some contexts.
|
||||
for (auto& sessionId : ids) {
|
||||
it = m_sessions.find(contextGroupId);
|
||||
if (it == m_sessions.end()) continue;
|
||||
auto sessionIt = it->second.find(sessionId);
|
||||
if (sessionIt != it->second.end()) callback(sessionIt->second);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace v8_inspector
|
||||
|
@ -104,12 +104,13 @@ class V8InspectorImpl : public V8Inspector {
|
||||
bool hasConsoleMessageStorage(int contextGroupId);
|
||||
void discardInspectedContext(int contextGroupId, int contextId);
|
||||
void disconnect(V8InspectorSessionImpl*);
|
||||
V8InspectorSessionImpl* sessionForContextGroup(int contextGroupId);
|
||||
V8InspectorSessionImpl* sessionById(int sessionId);
|
||||
V8InspectorSessionImpl* sessionById(int contextGroupId, int sessionId);
|
||||
InspectedContext* getContext(int groupId, int contextId) const;
|
||||
V8Console* console();
|
||||
void forEachContext(int contextGroupId,
|
||||
std::function<void(InspectedContext*)> callback);
|
||||
void forEachSession(int contextGroupId,
|
||||
std::function<void(V8InspectorSessionImpl*)> callback);
|
||||
|
||||
private:
|
||||
v8::Isolate* m_isolate;
|
||||
@ -130,9 +131,9 @@ class V8InspectorImpl : public V8Inspector {
|
||||
protocol::HashMap<int, std::unique_ptr<ContextByIdMap>>;
|
||||
ContextsByGroupMap m_contexts;
|
||||
|
||||
using SessionMap = protocol::HashMap<int, V8InspectorSessionImpl*>;
|
||||
SessionMap m_sessions;
|
||||
protocol::HashMap<int, V8InspectorSessionImpl*> m_sessionById;
|
||||
// contextGroupId -> sessionId -> session
|
||||
protocol::HashMap<int, protocol::HashMap<int, V8InspectorSessionImpl*>>
|
||||
m_sessions;
|
||||
|
||||
using ConsoleStorageMap =
|
||||
protocol::HashMap<int, std::unique_ptr<V8ConsoleMessageStorage>>;
|
||||
|
@ -161,6 +161,7 @@ class ProtocolPromiseHandler {
|
||||
std::unique_ptr<Callback> callback)
|
||||
: m_inspector(session->inspector()),
|
||||
m_sessionId(session->sessionId()),
|
||||
m_contextGroupId(session->contextGroupId()),
|
||||
m_executionContextId(executionContextId),
|
||||
m_objectGroup(objectGroup),
|
||||
m_returnByValue(returnByValue),
|
||||
@ -185,7 +186,8 @@ class ProtocolPromiseHandler {
|
||||
|
||||
std::unique_ptr<protocol::Runtime::RemoteObject> wrapObject(
|
||||
v8::Local<v8::Value> value) {
|
||||
V8InspectorSessionImpl* session = m_inspector->sessionById(m_sessionId);
|
||||
V8InspectorSessionImpl* session =
|
||||
m_inspector->sessionById(m_contextGroupId, m_sessionId);
|
||||
if (!session) {
|
||||
m_callback->sendFailure(Response::Error("No session"));
|
||||
return nullptr;
|
||||
@ -209,6 +211,7 @@ class ProtocolPromiseHandler {
|
||||
|
||||
V8InspectorImpl* m_inspector;
|
||||
int m_sessionId;
|
||||
int m_contextGroupId;
|
||||
int m_executionContextId;
|
||||
String16 m_objectGroup;
|
||||
bool m_returnByValue;
|
||||
@ -696,9 +699,11 @@ Response V8RuntimeAgentImpl::disable() {
|
||||
void V8RuntimeAgentImpl::reset() {
|
||||
m_compiledScripts.clear();
|
||||
if (m_enabled) {
|
||||
m_inspector->forEachContext(
|
||||
m_session->contextGroupId(),
|
||||
[](InspectedContext* context) { context->setReported(false); });
|
||||
int sessionId = m_session->sessionId();
|
||||
m_inspector->forEachContext(m_session->contextGroupId(),
|
||||
[&sessionId](InspectedContext* context) {
|
||||
context->setReported(sessionId, false);
|
||||
});
|
||||
m_frontend.executionContextsCleared();
|
||||
}
|
||||
}
|
||||
@ -706,7 +711,7 @@ void V8RuntimeAgentImpl::reset() {
|
||||
void V8RuntimeAgentImpl::reportExecutionContextCreated(
|
||||
InspectedContext* context) {
|
||||
if (!m_enabled) return;
|
||||
context->setReported(true);
|
||||
context->setReported(m_session->sessionId(), true);
|
||||
std::unique_ptr<protocol::Runtime::ExecutionContextDescription> description =
|
||||
protocol::Runtime::ExecutionContextDescription::create()
|
||||
.setId(context->contextId())
|
||||
@ -721,8 +726,8 @@ void V8RuntimeAgentImpl::reportExecutionContextCreated(
|
||||
|
||||
void V8RuntimeAgentImpl::reportExecutionContextDestroyed(
|
||||
InspectedContext* context) {
|
||||
if (m_enabled && context->isReported()) {
|
||||
context->setReported(false);
|
||||
if (m_enabled && context->isReported(m_session->sessionId())) {
|
||||
context->setReported(m_session->sessionId(), false);
|
||||
m_frontend.executionContextDestroyed(context->contextId());
|
||||
}
|
||||
}
|
||||
|
7
test/inspector/debugger/set-skip-all-pauses-expected.txt
Normal file
7
test/inspector/debugger/set-skip-all-pauses-expected.txt
Normal file
@ -0,0 +1,7 @@
|
||||
Tests that Debugger.setSkipAllPauses skips breaks and does not block resumed notifications
|
||||
paused at:
|
||||
#debugger;
|
||||
|
||||
paused at:
|
||||
#debugger
|
||||
|
31
test/inspector/debugger/set-skip-all-pauses.js
Normal file
31
test/inspector/debugger/set-skip-all-pauses.js
Normal file
@ -0,0 +1,31 @@
|
||||
// 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.
|
||||
|
||||
let {session, contextGroup, Protocol} = InspectorTest.start('Tests that Debugger.setSkipAllPauses skips breaks and does not block resumed notifications');
|
||||
session.setupScriptMap();
|
||||
|
||||
(async function test() {
|
||||
await Protocol.Debugger.enable();
|
||||
Protocol.Runtime.evaluate({expression: 'debugger;'});
|
||||
await waitForPause();
|
||||
await Protocol.Debugger.resume();
|
||||
|
||||
await Protocol.Debugger.setSkipAllPauses({skip: true});
|
||||
await Protocol.Runtime.evaluate({expression: 'debugger'});
|
||||
|
||||
await Protocol.Debugger.setSkipAllPauses({skip: false});
|
||||
Protocol.Runtime.evaluate({expression: 'debugger'});
|
||||
await waitForPause();
|
||||
Protocol.Debugger.setSkipAllPauses({skip: true});
|
||||
Protocol.Debugger.resume();
|
||||
await Protocol.Debugger.onceResumed();
|
||||
|
||||
InspectorTest.completeTest();
|
||||
})();
|
||||
|
||||
async function waitForPause() {
|
||||
var message = await Protocol.Debugger.oncePaused();
|
||||
InspectorTest.log('paused at:');
|
||||
session.logSourceLocation(message.params.callFrames[0].location);
|
||||
}
|
@ -54,6 +54,7 @@ IsolateData::IsolateData(TaskRunner* task_runner,
|
||||
isolate_->SetMicrotasksPolicy(v8::MicrotasksPolicy::kScoped);
|
||||
if (with_inspector) {
|
||||
isolate_->AddMessageListener(&IsolateData::MessageHandler);
|
||||
isolate_->SetPromiseRejectCallback(&IsolateData::PromiseRejectHandler);
|
||||
inspector_ = v8_inspector::V8Inspector::create(isolate_, this);
|
||||
}
|
||||
}
|
||||
@ -191,11 +192,11 @@ void IsolateData::DumpAsyncTaskStacksStateForTest() {
|
||||
}
|
||||
|
||||
// static
|
||||
void IsolateData::MessageHandler(v8::Local<v8::Message> message,
|
||||
v8::Local<v8::Value> exception) {
|
||||
int IsolateData::HandleMessage(v8::Local<v8::Message> message,
|
||||
v8::Local<v8::Value> exception) {
|
||||
v8::Isolate* isolate = v8::Isolate::GetCurrent();
|
||||
v8::Local<v8::Context> context = isolate->GetEnteredContext();
|
||||
if (context.IsEmpty()) return;
|
||||
if (context.IsEmpty()) return 0;
|
||||
v8_inspector::V8Inspector* inspector =
|
||||
IsolateData::FromContext(context)->inspector_.get();
|
||||
|
||||
@ -222,9 +223,51 @@ void IsolateData::MessageHandler(v8::Local<v8::Message> message,
|
||||
}
|
||||
v8_inspector::StringView url(url_string.start(), url_string.length());
|
||||
|
||||
inspector->exceptionThrown(context, message_text, exception, detailed_message,
|
||||
url, line_number, column_number,
|
||||
inspector->createStackTrace(stack), script_id);
|
||||
return inspector->exceptionThrown(
|
||||
context, message_text, exception, detailed_message, url, line_number,
|
||||
column_number, inspector->createStackTrace(stack), script_id);
|
||||
}
|
||||
|
||||
// static
|
||||
void IsolateData::MessageHandler(v8::Local<v8::Message> message,
|
||||
v8::Local<v8::Value> exception) {
|
||||
HandleMessage(message, exception);
|
||||
}
|
||||
|
||||
// static
|
||||
void IsolateData::PromiseRejectHandler(v8::PromiseRejectMessage data) {
|
||||
v8::Isolate* isolate = v8::Isolate::GetCurrent();
|
||||
v8::Local<v8::Context> context = isolate->GetEnteredContext();
|
||||
if (context.IsEmpty()) return;
|
||||
v8::Local<v8::Promise> promise = data.GetPromise();
|
||||
v8::Local<v8::Private> id_private = v8::Private::ForApi(
|
||||
isolate,
|
||||
v8::String::NewFromUtf8(isolate, "id", v8::NewStringType::kNormal)
|
||||
.ToLocalChecked());
|
||||
|
||||
if (data.GetEvent() == v8::kPromiseHandlerAddedAfterReject) {
|
||||
v8::Local<v8::Value> id;
|
||||
if (!promise->GetPrivate(context, id_private).ToLocal(&id)) return;
|
||||
if (!id->IsInt32()) return;
|
||||
v8_inspector::V8Inspector* inspector =
|
||||
IsolateData::FromContext(context)->inspector_.get();
|
||||
const char* reason_str = "Handler added to rejected promise";
|
||||
inspector->exceptionRevoked(
|
||||
context, id.As<v8::Int32>()->Value(),
|
||||
v8_inspector::StringView(reinterpret_cast<const uint8_t*>(reason_str),
|
||||
strlen(reason_str)));
|
||||
return;
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> exception = data.GetValue();
|
||||
int exception_id = HandleMessage(
|
||||
v8::Exception::CreateMessage(isolate, exception), exception);
|
||||
if (exception_id) {
|
||||
promise
|
||||
->SetPrivate(isolate->GetCurrentContext(), id_private,
|
||||
v8::Int32::New(isolate, exception_id))
|
||||
.ToChecked();
|
||||
}
|
||||
}
|
||||
|
||||
void IsolateData::FireContextCreated(v8::Local<v8::Context> context,
|
||||
|
@ -83,6 +83,9 @@ class IsolateData : public v8_inspector::V8InspectorClient {
|
||||
v8::Local<v8::Module> referrer);
|
||||
static void MessageHandler(v8::Local<v8::Message> message,
|
||||
v8::Local<v8::Value> exception);
|
||||
static void PromiseRejectHandler(v8::PromiseRejectMessage data);
|
||||
static int HandleMessage(v8::Local<v8::Message> message,
|
||||
v8::Local<v8::Value> exception);
|
||||
std::vector<int> GetSessionIds(int context_group_id);
|
||||
|
||||
// V8InspectorClient implementation.
|
||||
|
132
test/inspector/sessions/create-session-expected.txt
Normal file
132
test/inspector/sessions/create-session-expected.txt
Normal file
@ -0,0 +1,132 @@
|
||||
Tests that creating multiple sessions works.
|
||||
Connecting session 1
|
||||
From session 1
|
||||
{
|
||||
method : Runtime.executionContextCreated
|
||||
params : {
|
||||
context : {
|
||||
id : 1
|
||||
name :
|
||||
origin :
|
||||
}
|
||||
}
|
||||
}
|
||||
Connecting session 2
|
||||
From session 2
|
||||
{
|
||||
method : Runtime.executionContextCreated
|
||||
params : {
|
||||
context : {
|
||||
id : 1
|
||||
name :
|
||||
origin :
|
||||
}
|
||||
}
|
||||
}
|
||||
Reconnecting session 2
|
||||
From session 2
|
||||
{
|
||||
method : Runtime.executionContextCreated
|
||||
params : {
|
||||
context : {
|
||||
id : 1
|
||||
name :
|
||||
origin :
|
||||
}
|
||||
}
|
||||
}
|
||||
Reconnecting session 1
|
||||
From session 1
|
||||
{
|
||||
method : Runtime.executionContextCreated
|
||||
params : {
|
||||
context : {
|
||||
id : 1
|
||||
name :
|
||||
origin :
|
||||
}
|
||||
}
|
||||
}
|
||||
Connecting session 3
|
||||
From session 3
|
||||
{
|
||||
method : Runtime.executionContextCreated
|
||||
params : {
|
||||
context : {
|
||||
id : 1
|
||||
name :
|
||||
origin :
|
||||
}
|
||||
}
|
||||
}
|
||||
Destroying and creating context
|
||||
From session 3
|
||||
{
|
||||
method : Runtime.executionContextDestroyed
|
||||
params : {
|
||||
executionContextId : <executionContextId>
|
||||
}
|
||||
}
|
||||
id matching: true
|
||||
From session 1
|
||||
{
|
||||
method : Runtime.executionContextDestroyed
|
||||
params : {
|
||||
executionContextId : <executionContextId>
|
||||
}
|
||||
}
|
||||
id matching: true
|
||||
From session 2
|
||||
{
|
||||
method : Runtime.executionContextDestroyed
|
||||
params : {
|
||||
executionContextId : <executionContextId>
|
||||
}
|
||||
}
|
||||
id matching: true
|
||||
From session 3
|
||||
{
|
||||
method : Runtime.executionContextCreated
|
||||
params : {
|
||||
context : {
|
||||
id : 2
|
||||
name :
|
||||
origin :
|
||||
}
|
||||
}
|
||||
}
|
||||
From session 1
|
||||
{
|
||||
method : Runtime.executionContextCreated
|
||||
params : {
|
||||
context : {
|
||||
id : 2
|
||||
name :
|
||||
origin :
|
||||
}
|
||||
}
|
||||
}
|
||||
From session 2
|
||||
{
|
||||
method : Runtime.executionContextCreated
|
||||
params : {
|
||||
context : {
|
||||
id : 2
|
||||
name :
|
||||
origin :
|
||||
}
|
||||
}
|
||||
}
|
||||
Disconnecting all sessions
|
||||
Connecting session 4
|
||||
From session 4
|
||||
{
|
||||
method : Runtime.executionContextCreated
|
||||
params : {
|
||||
context : {
|
||||
id : 2
|
||||
name :
|
||||
origin :
|
||||
}
|
||||
}
|
||||
}
|
58
test/inspector/sessions/create-session.js
Normal file
58
test/inspector/sessions/create-session.js
Normal file
@ -0,0 +1,58 @@
|
||||
// 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 creating multiple sessions works.');
|
||||
|
||||
function connect(contextGroup, num) {
|
||||
var session = contextGroup.connect();
|
||||
var executionContextId;
|
||||
session.Protocol.Runtime.onExecutionContextCreated(message => {
|
||||
InspectorTest.log('From session ' + num);
|
||||
InspectorTest.logMessage(message);
|
||||
executionContextId = message.params.context.id;
|
||||
});
|
||||
session.Protocol.Runtime.onExecutionContextDestroyed(message => {
|
||||
InspectorTest.log('From session ' + num);
|
||||
InspectorTest.logMessage(message);
|
||||
InspectorTest.log('id matching: ' + (message.params.executionContextId === executionContextId));
|
||||
});
|
||||
return session;
|
||||
}
|
||||
|
||||
(async function test() {
|
||||
var contextGroup = new InspectorTest.ContextGroup();
|
||||
InspectorTest.log('Connecting session 1');
|
||||
var session1 = connect(contextGroup, 1);
|
||||
await session1.Protocol.Runtime.enable();
|
||||
|
||||
InspectorTest.log('Connecting session 2');
|
||||
var session2 = connect(contextGroup, 2);
|
||||
await session2.Protocol.Runtime.enable();
|
||||
|
||||
InspectorTest.log('Reconnecting session 2');
|
||||
session2.reconnect();
|
||||
await session2.Protocol.Runtime.enable();
|
||||
|
||||
InspectorTest.log('Reconnecting session 1');
|
||||
session1.reconnect();
|
||||
await session1.Protocol.Runtime.enable();
|
||||
|
||||
InspectorTest.log('Connecting session 3');
|
||||
var session3 = connect(contextGroup, 3);
|
||||
await session3.Protocol.Runtime.enable();
|
||||
|
||||
InspectorTest.log('Destroying and creating context');
|
||||
await session2.Protocol.Runtime.evaluate({expression: 'inspector.fireContextDestroyed(); inspector.fireContextCreated(); '});
|
||||
|
||||
InspectorTest.log('Disconnecting all sessions');
|
||||
session1.disconnect();
|
||||
session2.disconnect();
|
||||
session3.disconnect();
|
||||
|
||||
InspectorTest.log('Connecting session 4');
|
||||
var session4 = connect(contextGroup, 4);
|
||||
await session4.Protocol.Runtime.enable();
|
||||
|
||||
InspectorTest.completeTest();
|
||||
})();
|
217
test/inspector/sessions/runtime-console-api-called-expected.txt
Normal file
217
test/inspector/sessions/runtime-console-api-called-expected.txt
Normal file
@ -0,0 +1,217 @@
|
||||
Tests that all sessions get console api notifications.
|
||||
Error in 2
|
||||
From session 2
|
||||
{
|
||||
method : Runtime.consoleAPICalled
|
||||
params : {
|
||||
args : [
|
||||
[0] : {
|
||||
description : 1
|
||||
type : number
|
||||
value : 1
|
||||
}
|
||||
]
|
||||
executionContextId : <executionContextId>
|
||||
stackTrace : {
|
||||
callFrames : [
|
||||
[0] : {
|
||||
columnNumber : 8
|
||||
functionName :
|
||||
lineNumber : 0
|
||||
scriptId : <scriptId>
|
||||
url :
|
||||
}
|
||||
]
|
||||
}
|
||||
timestamp : <timestamp>
|
||||
type : error
|
||||
}
|
||||
}
|
||||
From session 1
|
||||
{
|
||||
method : Runtime.consoleAPICalled
|
||||
params : {
|
||||
args : [
|
||||
[0] : {
|
||||
description : 1
|
||||
type : number
|
||||
value : 1
|
||||
}
|
||||
]
|
||||
executionContextId : <executionContextId>
|
||||
stackTrace : {
|
||||
callFrames : [
|
||||
[0] : {
|
||||
columnNumber : 8
|
||||
functionName :
|
||||
lineNumber : 0
|
||||
scriptId : <scriptId>
|
||||
url :
|
||||
}
|
||||
]
|
||||
}
|
||||
timestamp : <timestamp>
|
||||
type : error
|
||||
}
|
||||
}
|
||||
Logging in 1
|
||||
From session 2
|
||||
{
|
||||
method : Runtime.consoleAPICalled
|
||||
params : {
|
||||
args : [
|
||||
[0] : {
|
||||
description : 2
|
||||
type : number
|
||||
value : 2
|
||||
}
|
||||
]
|
||||
executionContextId : <executionContextId>
|
||||
stackTrace : {
|
||||
callFrames : [
|
||||
[0] : {
|
||||
columnNumber : 8
|
||||
functionName :
|
||||
lineNumber : 0
|
||||
scriptId : <scriptId>
|
||||
url :
|
||||
}
|
||||
]
|
||||
}
|
||||
timestamp : <timestamp>
|
||||
type : log
|
||||
}
|
||||
}
|
||||
From session 1
|
||||
{
|
||||
method : Runtime.consoleAPICalled
|
||||
params : {
|
||||
args : [
|
||||
[0] : {
|
||||
description : 2
|
||||
type : number
|
||||
value : 2
|
||||
}
|
||||
]
|
||||
executionContextId : <executionContextId>
|
||||
stackTrace : {
|
||||
callFrames : [
|
||||
[0] : {
|
||||
columnNumber : 8
|
||||
functionName :
|
||||
lineNumber : 0
|
||||
scriptId : <scriptId>
|
||||
url :
|
||||
}
|
||||
]
|
||||
}
|
||||
timestamp : <timestamp>
|
||||
type : log
|
||||
}
|
||||
}
|
||||
Error in setTimeout 1
|
||||
From session 2
|
||||
{
|
||||
method : Runtime.consoleAPICalled
|
||||
params : {
|
||||
args : [
|
||||
[0] : {
|
||||
type : string
|
||||
value : a
|
||||
}
|
||||
]
|
||||
executionContextId : <executionContextId>
|
||||
stackTrace : {
|
||||
callFrames : [
|
||||
[0] : {
|
||||
columnNumber : 25
|
||||
functionName : setTimeout
|
||||
lineNumber : 0
|
||||
scriptId : <scriptId>
|
||||
url :
|
||||
}
|
||||
]
|
||||
}
|
||||
timestamp : <timestamp>
|
||||
type : error
|
||||
}
|
||||
}
|
||||
From session 1
|
||||
{
|
||||
method : Runtime.consoleAPICalled
|
||||
params : {
|
||||
args : [
|
||||
[0] : {
|
||||
type : string
|
||||
value : a
|
||||
}
|
||||
]
|
||||
executionContextId : <executionContextId>
|
||||
stackTrace : {
|
||||
callFrames : [
|
||||
[0] : {
|
||||
columnNumber : 25
|
||||
functionName : setTimeout
|
||||
lineNumber : 0
|
||||
scriptId : <scriptId>
|
||||
url :
|
||||
}
|
||||
]
|
||||
}
|
||||
timestamp : <timestamp>
|
||||
type : error
|
||||
}
|
||||
}
|
||||
Logging in setTimeout 2
|
||||
From session 2
|
||||
{
|
||||
method : Runtime.consoleAPICalled
|
||||
params : {
|
||||
args : [
|
||||
[0] : {
|
||||
type : string
|
||||
value : b
|
||||
}
|
||||
]
|
||||
executionContextId : <executionContextId>
|
||||
stackTrace : {
|
||||
callFrames : [
|
||||
[0] : {
|
||||
columnNumber : 25
|
||||
functionName : setTimeout
|
||||
lineNumber : 0
|
||||
scriptId : <scriptId>
|
||||
url :
|
||||
}
|
||||
]
|
||||
}
|
||||
timestamp : <timestamp>
|
||||
type : log
|
||||
}
|
||||
}
|
||||
From session 1
|
||||
{
|
||||
method : Runtime.consoleAPICalled
|
||||
params : {
|
||||
args : [
|
||||
[0] : {
|
||||
type : string
|
||||
value : b
|
||||
}
|
||||
]
|
||||
executionContextId : <executionContextId>
|
||||
stackTrace : {
|
||||
callFrames : [
|
||||
[0] : {
|
||||
columnNumber : 25
|
||||
functionName : setTimeout
|
||||
lineNumber : 0
|
||||
scriptId : <scriptId>
|
||||
url :
|
||||
}
|
||||
]
|
||||
}
|
||||
timestamp : <timestamp>
|
||||
type : log
|
||||
}
|
||||
}
|
38
test/inspector/sessions/runtime-console-api-called.js
Normal file
38
test/inspector/sessions/runtime-console-api-called.js
Normal file
@ -0,0 +1,38 @@
|
||||
// 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 all sessions get console api notifications.');
|
||||
|
||||
function connect(contextGroup, num) {
|
||||
var session = contextGroup.connect();
|
||||
session.Protocol.Runtime.onConsoleAPICalled(message => {
|
||||
InspectorTest.log('From session ' + num);
|
||||
InspectorTest.logMessage(message);
|
||||
});
|
||||
return session;
|
||||
}
|
||||
|
||||
(async function test() {
|
||||
var contextGroup = new InspectorTest.ContextGroup();
|
||||
var session1 = connect(contextGroup, 1);
|
||||
var session2 = connect(contextGroup, 2);
|
||||
await session1.Protocol.Runtime.enable();
|
||||
await session2.Protocol.Runtime.enable();
|
||||
|
||||
InspectorTest.log('Error in 2');
|
||||
await session2.Protocol.Runtime.evaluate({expression: 'console.error(1)'});
|
||||
|
||||
InspectorTest.log('Logging in 1');
|
||||
await session1.Protocol.Runtime.evaluate({expression: 'console.log(2)'});
|
||||
|
||||
InspectorTest.log('Error in setTimeout 1');
|
||||
await session1.Protocol.Runtime.evaluate({expression: 'setTimeout(() => console.error("a"), 0)'});
|
||||
await InspectorTest.waitForPendingTasks();
|
||||
|
||||
InspectorTest.log('Logging in setTimeout 2');
|
||||
await session2.Protocol.Runtime.evaluate({expression: 'setTimeout(() => console.log("b"), 0)'});
|
||||
await InspectorTest.waitForPendingTasks();
|
||||
|
||||
InspectorTest.completeTest();
|
||||
})();
|
277
test/inspector/sessions/runtime-evaluate-exception-expected.txt
Normal file
277
test/inspector/sessions/runtime-evaluate-exception-expected.txt
Normal file
@ -0,0 +1,277 @@
|
||||
Tests that all sessions get exception notifications.
|
||||
Throwing in 2
|
||||
Throwing in 1
|
||||
Throwing in setTimeout 1
|
||||
From session 2
|
||||
{
|
||||
method : Runtime.exceptionThrown
|
||||
params : {
|
||||
exceptionDetails : {
|
||||
columnNumber : 19
|
||||
exception : {
|
||||
type : string
|
||||
value : error3
|
||||
}
|
||||
exceptionId : <exceptionId>
|
||||
executionContextId : <executionContextId>
|
||||
lineNumber : 0
|
||||
stackTrace : {
|
||||
callFrames : [
|
||||
[0] : {
|
||||
columnNumber : 19
|
||||
functionName : setTimeout
|
||||
lineNumber : 0
|
||||
scriptId : <scriptId>
|
||||
url :
|
||||
}
|
||||
]
|
||||
}
|
||||
text : Uncaught error3
|
||||
}
|
||||
timestamp : <timestamp>
|
||||
}
|
||||
}
|
||||
From session 1
|
||||
{
|
||||
method : Runtime.exceptionThrown
|
||||
params : {
|
||||
exceptionDetails : {
|
||||
columnNumber : 19
|
||||
exception : {
|
||||
type : string
|
||||
value : error3
|
||||
}
|
||||
exceptionId : <exceptionId>
|
||||
executionContextId : <executionContextId>
|
||||
lineNumber : 0
|
||||
stackTrace : {
|
||||
callFrames : [
|
||||
[0] : {
|
||||
columnNumber : 19
|
||||
functionName : setTimeout
|
||||
lineNumber : 0
|
||||
scriptId : <scriptId>
|
||||
url :
|
||||
}
|
||||
]
|
||||
}
|
||||
text : Uncaught error3
|
||||
}
|
||||
timestamp : <timestamp>
|
||||
}
|
||||
}
|
||||
Throwing in setTimeout 2
|
||||
From session 2
|
||||
{
|
||||
method : Runtime.exceptionThrown
|
||||
params : {
|
||||
exceptionDetails : {
|
||||
columnNumber : 19
|
||||
exception : {
|
||||
type : string
|
||||
value : error4
|
||||
}
|
||||
exceptionId : <exceptionId>
|
||||
executionContextId : <executionContextId>
|
||||
lineNumber : 0
|
||||
stackTrace : {
|
||||
callFrames : [
|
||||
[0] : {
|
||||
columnNumber : 19
|
||||
functionName : setTimeout
|
||||
lineNumber : 0
|
||||
scriptId : <scriptId>
|
||||
url :
|
||||
}
|
||||
]
|
||||
}
|
||||
text : Uncaught error4
|
||||
}
|
||||
timestamp : <timestamp>
|
||||
}
|
||||
}
|
||||
From session 1
|
||||
{
|
||||
method : Runtime.exceptionThrown
|
||||
params : {
|
||||
exceptionDetails : {
|
||||
columnNumber : 19
|
||||
exception : {
|
||||
type : string
|
||||
value : error4
|
||||
}
|
||||
exceptionId : <exceptionId>
|
||||
executionContextId : <executionContextId>
|
||||
lineNumber : 0
|
||||
stackTrace : {
|
||||
callFrames : [
|
||||
[0] : {
|
||||
columnNumber : 19
|
||||
functionName : setTimeout
|
||||
lineNumber : 0
|
||||
scriptId : <scriptId>
|
||||
url :
|
||||
}
|
||||
]
|
||||
}
|
||||
text : Uncaught error4
|
||||
}
|
||||
timestamp : <timestamp>
|
||||
}
|
||||
}
|
||||
Rejecting in 2
|
||||
From session 2
|
||||
{
|
||||
method : Runtime.exceptionThrown
|
||||
params : {
|
||||
exceptionDetails : {
|
||||
columnNumber : 40
|
||||
exception : {
|
||||
type : string
|
||||
value : error5
|
||||
}
|
||||
exceptionId : <exceptionId>
|
||||
executionContextId : <executionContextId>
|
||||
lineNumber : 0
|
||||
stackTrace : {
|
||||
callFrames : [
|
||||
[0] : {
|
||||
columnNumber : 40
|
||||
functionName : setTimeout
|
||||
lineNumber : 0
|
||||
scriptId : <scriptId>
|
||||
url :
|
||||
}
|
||||
]
|
||||
}
|
||||
text : Uncaught error5
|
||||
}
|
||||
timestamp : <timestamp>
|
||||
}
|
||||
}
|
||||
From session 1
|
||||
{
|
||||
method : Runtime.exceptionThrown
|
||||
params : {
|
||||
exceptionDetails : {
|
||||
columnNumber : 40
|
||||
exception : {
|
||||
type : string
|
||||
value : error5
|
||||
}
|
||||
exceptionId : <exceptionId>
|
||||
executionContextId : <executionContextId>
|
||||
lineNumber : 0
|
||||
stackTrace : {
|
||||
callFrames : [
|
||||
[0] : {
|
||||
columnNumber : 40
|
||||
functionName : setTimeout
|
||||
lineNumber : 0
|
||||
scriptId : <scriptId>
|
||||
url :
|
||||
}
|
||||
]
|
||||
}
|
||||
text : Uncaught error5
|
||||
}
|
||||
timestamp : <timestamp>
|
||||
}
|
||||
}
|
||||
Revoking in 2
|
||||
From session 2
|
||||
{
|
||||
method : Runtime.exceptionRevoked
|
||||
params : {
|
||||
exceptionId : <exceptionId>
|
||||
reason : Handler added to rejected promise
|
||||
}
|
||||
}
|
||||
id matching: true
|
||||
From session 1
|
||||
{
|
||||
method : Runtime.exceptionRevoked
|
||||
params : {
|
||||
exceptionId : <exceptionId>
|
||||
reason : Handler added to rejected promise
|
||||
}
|
||||
}
|
||||
id matching: true
|
||||
Rejecting in 1
|
||||
From session 2
|
||||
{
|
||||
method : Runtime.exceptionThrown
|
||||
params : {
|
||||
exceptionDetails : {
|
||||
columnNumber : 40
|
||||
exception : {
|
||||
type : string
|
||||
value : error6
|
||||
}
|
||||
exceptionId : <exceptionId>
|
||||
executionContextId : <executionContextId>
|
||||
lineNumber : 0
|
||||
stackTrace : {
|
||||
callFrames : [
|
||||
[0] : {
|
||||
columnNumber : 40
|
||||
functionName : setTimeout
|
||||
lineNumber : 0
|
||||
scriptId : <scriptId>
|
||||
url :
|
||||
}
|
||||
]
|
||||
}
|
||||
text : Uncaught error6
|
||||
}
|
||||
timestamp : <timestamp>
|
||||
}
|
||||
}
|
||||
From session 1
|
||||
{
|
||||
method : Runtime.exceptionThrown
|
||||
params : {
|
||||
exceptionDetails : {
|
||||
columnNumber : 40
|
||||
exception : {
|
||||
type : string
|
||||
value : error6
|
||||
}
|
||||
exceptionId : <exceptionId>
|
||||
executionContextId : <executionContextId>
|
||||
lineNumber : 0
|
||||
stackTrace : {
|
||||
callFrames : [
|
||||
[0] : {
|
||||
columnNumber : 40
|
||||
functionName : setTimeout
|
||||
lineNumber : 0
|
||||
scriptId : <scriptId>
|
||||
url :
|
||||
}
|
||||
]
|
||||
}
|
||||
text : Uncaught error6
|
||||
}
|
||||
timestamp : <timestamp>
|
||||
}
|
||||
}
|
||||
Revoking in 1
|
||||
From session 2
|
||||
{
|
||||
method : Runtime.exceptionRevoked
|
||||
params : {
|
||||
exceptionId : <exceptionId>
|
||||
reason : Handler added to rejected promise
|
||||
}
|
||||
}
|
||||
id matching: true
|
||||
From session 1
|
||||
{
|
||||
method : Runtime.exceptionRevoked
|
||||
params : {
|
||||
exceptionId : <exceptionId>
|
||||
reason : Handler added to rejected promise
|
||||
}
|
||||
}
|
||||
id matching: true
|
59
test/inspector/sessions/runtime-evaluate-exception.js
Normal file
59
test/inspector/sessions/runtime-evaluate-exception.js
Normal file
@ -0,0 +1,59 @@
|
||||
// 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 all sessions get exception notifications.');
|
||||
|
||||
function connect(contextGroup, num) {
|
||||
var session = contextGroup.connect();
|
||||
var exceptionId;
|
||||
session.Protocol.Runtime.onExceptionThrown(message => {
|
||||
InspectorTest.log('From session ' + num);
|
||||
InspectorTest.logMessage(message);
|
||||
exceptionId = message.params.exceptionDetails.exceptionId;
|
||||
});
|
||||
session.Protocol.Runtime.onExceptionRevoked(message => {
|
||||
InspectorTest.log('From session ' + num);
|
||||
InspectorTest.logMessage(message);
|
||||
InspectorTest.log('id matching: ' + (message.params.exceptionId === exceptionId));
|
||||
});
|
||||
return session;
|
||||
}
|
||||
|
||||
(async function test() {
|
||||
var contextGroup = new InspectorTest.ContextGroup();
|
||||
var session1 = connect(contextGroup, 1);
|
||||
var session2 = connect(contextGroup, 2);
|
||||
await session1.Protocol.Runtime.enable();
|
||||
await session2.Protocol.Runtime.enable();
|
||||
|
||||
InspectorTest.log('Throwing in 2');
|
||||
await session2.Protocol.Runtime.evaluate({expression: 'throw "error1";'});
|
||||
|
||||
InspectorTest.log('Throwing in 1');
|
||||
await session1.Protocol.Runtime.evaluate({expression: 'throw "error2";'});
|
||||
|
||||
InspectorTest.log('Throwing in setTimeout 1');
|
||||
await session1.Protocol.Runtime.evaluate({expression: 'setTimeout(() => { throw "error3"; }, 0)'});
|
||||
await InspectorTest.waitForPendingTasks();
|
||||
|
||||
InspectorTest.log('Throwing in setTimeout 2');
|
||||
await session2.Protocol.Runtime.evaluate({expression: 'setTimeout(() => { throw "error4"; }, 0)'});
|
||||
await InspectorTest.waitForPendingTasks();
|
||||
|
||||
InspectorTest.log('Rejecting in 2');
|
||||
await session2.Protocol.Runtime.evaluate({expression: 'var p2; setTimeout(() => { p2 = Promise.reject("error5") }, 0)'});
|
||||
await InspectorTest.waitForPendingTasks();
|
||||
InspectorTest.log('Revoking in 2');
|
||||
await session2.Protocol.Runtime.evaluate({expression: 'setTimeout(() => { p2.catch() }, 0);'});
|
||||
await InspectorTest.waitForPendingTasks();
|
||||
|
||||
InspectorTest.log('Rejecting in 1');
|
||||
await session1.Protocol.Runtime.evaluate({expression: 'var p1; setTimeout(() => { p1 = Promise.reject("error6")} , 0)'});
|
||||
await InspectorTest.waitForPendingTasks();
|
||||
InspectorTest.log('Revoking in 1');
|
||||
await session1.Protocol.Runtime.evaluate({expression: 'setTimeout(() => { p1.catch() }, 0);'});
|
||||
await InspectorTest.waitForPendingTasks();
|
||||
|
||||
InspectorTest.completeTest();
|
||||
})();
|
25
test/inspector/sessions/runtime-evaluate-expected.txt
Normal file
25
test/inspector/sessions/runtime-evaluate-expected.txt
Normal file
@ -0,0 +1,25 @@
|
||||
Tests that multiple sessions share the context.
|
||||
Assigning in 1
|
||||
Evaluating in 2
|
||||
{
|
||||
id : <messageId>
|
||||
result : {
|
||||
result : {
|
||||
description : 42
|
||||
type : number
|
||||
value : 42
|
||||
}
|
||||
}
|
||||
}
|
||||
Awaiting in 1
|
||||
Resolving in 2
|
||||
Should resolve in 1
|
||||
{
|
||||
id : <messageId>
|
||||
result : {
|
||||
result : {
|
||||
type : string
|
||||
value : foo
|
||||
}
|
||||
}
|
||||
}
|
25
test/inspector/sessions/runtime-evaluate.js
Normal file
25
test/inspector/sessions/runtime-evaluate.js
Normal file
@ -0,0 +1,25 @@
|
||||
// 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 share the context.');
|
||||
|
||||
(async function test() {
|
||||
var contextGroup = new InspectorTest.ContextGroup();
|
||||
var session1 = contextGroup.connect();
|
||||
var session2 = contextGroup.connect();
|
||||
|
||||
InspectorTest.log('Assigning in 1');
|
||||
await session1.Protocol.Runtime.evaluate({expression: 'var a = 42;'});
|
||||
InspectorTest.log('Evaluating in 2');
|
||||
InspectorTest.logMessage(await session2.Protocol.Runtime.evaluate({expression: 'a'}));
|
||||
|
||||
InspectorTest.log('Awaiting in 1');
|
||||
var promise = session1.Protocol.Runtime.evaluate({expression: 'var cb; new Promise(f => cb = f)', awaitPromise: true});
|
||||
InspectorTest.log('Resolving in 2');
|
||||
await session2.Protocol.Runtime.evaluate({expression: 'cb("foo")'});
|
||||
InspectorTest.log('Should resolve in 1');
|
||||
InspectorTest.logMessage(await promise);
|
||||
|
||||
InspectorTest.completeTest();
|
||||
})();
|
Loading…
Reference in New Issue
Block a user