blob: 6956e2d54d603ead09569fe0806116233b9aced3 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8DebuggerAgent_h
#define V8DebuggerAgent_h
#include "bindings/core/v8/ScriptState.h"
#include "bindings/core/v8/ScriptValue.h"
#include "core/CoreExport.h"
#include "core/InspectorFrontend.h"
#include "core/inspector/InspectorBaseAgent.h"
#include "core/inspector/PromiseTracker.h"
#include "core/inspector/v8/ScriptBreakpoint.h"
#include "core/inspector/v8/V8DebuggerListener.h"
#include "wtf/Forward.h"
#include "wtf/HashMap.h"
#include "wtf/HashSet.h"
#include "wtf/ListHashSet.h"
#include "wtf/PassRefPtr.h"
#include "wtf/Vector.h"
#include "wtf/text/StringHash.h"
namespace blink {
class AsyncCallChain;
class AsyncCallStack;
class DevToolsFunctionInfo;
class InjectedScript;
class InjectedScriptManager;
class JavaScriptCallFrame;
class JSONObject;
class RemoteCallFrameId;
class ScriptAsyncCallStack;
class ScriptRegexp;
class V8AsyncCallTracker;
class V8Debugger;
typedef String ErrorString;
class CORE_EXPORT V8DebuggerAgent
: public NoBaseWillBeGarbageCollectedFinalized<V8DebuggerAgent>
, public V8DebuggerListener
, public InspectorBackendDispatcher::DebuggerCommandHandler
, public PromiseTracker::Listener {
WTF_MAKE_NONCOPYABLE(V8DebuggerAgent);
WTF_MAKE_FAST_ALLOCATED_WILL_BE_REMOVED(V8DebuggerAgent);
WILL_BE_USING_GARBAGE_COLLECTED_MIXIN(V8DebuggerAgent);
public:
enum BreakpointSource {
UserBreakpointSource,
DebugCommandBreakpointSource,
MonitorCommandBreakpointSource
};
static const char backtraceObjectGroup[];
class CORE_EXPORT Client {
public:
virtual ~Client() { }
virtual void debuggerAgentEnabled() = 0;
virtual void debuggerAgentDisabled() = 0;
virtual void muteConsole() = 0;
virtual void unmuteConsole() = 0;
virtual InjectedScript defaultInjectedScript() = 0;
};
V8DebuggerAgent(InjectedScriptManager*, V8Debugger*, Client*, int contextGroupId);
~V8DebuggerAgent() override;
DECLARE_TRACE();
void setInspectorState(InspectorState* state) { m_state = state; }
void setFrontend(InspectorFrontend::Debugger* frontend) { m_frontend = frontend; }
void clearFrontend();
void restore();
void disable(ErrorString*) final;
bool isPaused();
// Part of the protocol.
void enable(ErrorString*) override;
void setBreakpointsActive(ErrorString*, bool active) final;
void setSkipAllPauses(ErrorString*, bool skipped) final;
void setBreakpointByUrl(ErrorString*, int lineNumber, const String* optionalURL, const String* optionalURLRegex, const int* optionalColumnNumber, const String* optionalCondition, TypeBuilder::Debugger::BreakpointId*, RefPtr<TypeBuilder::Array<TypeBuilder::Debugger::Location>>& locations) final;
void setBreakpoint(ErrorString*, const RefPtr<JSONObject>& location, const String* optionalCondition, TypeBuilder::Debugger::BreakpointId*, RefPtr<TypeBuilder::Debugger::Location>& actualLocation) final;
void removeBreakpoint(ErrorString*, const String& breakpointId) final;
void continueToLocation(ErrorString*, const RefPtr<JSONObject>& location, const bool* interstateLocationOpt) final;
void getStepInPositions(ErrorString*, const String& callFrameId, RefPtr<TypeBuilder::Array<TypeBuilder::Debugger::Location>>& positions) final;
void getBacktrace(ErrorString*, RefPtr<TypeBuilder::Array<TypeBuilder::Debugger::CallFrame>>&, RefPtr<TypeBuilder::Debugger::StackTrace>&) final;
void searchInContent(ErrorString*, const String& scriptId, const String& query, const bool* optionalCaseSensitive, const bool* optionalIsRegex, RefPtr<TypeBuilder::Array<TypeBuilder::Debugger::SearchMatch>>&) final;
void canSetScriptSource(ErrorString*, bool* result) final { *result = true; }
void setScriptSource(ErrorString*, RefPtr<TypeBuilder::Debugger::SetScriptSourceError>&, const String& scriptId, const String& newContent, const bool* preview, RefPtr<TypeBuilder::Array<TypeBuilder::Debugger::CallFrame>>& newCallFrames, TypeBuilder::OptOutput<bool>* stackChanged, RefPtr<TypeBuilder::Debugger::StackTrace>& asyncStackTrace) final;
void restartFrame(ErrorString*, const String& callFrameId, RefPtr<TypeBuilder::Array<TypeBuilder::Debugger::CallFrame>>& newCallFrames, RefPtr<TypeBuilder::Debugger::StackTrace>& asyncStackTrace) final;
void getScriptSource(ErrorString*, const String& scriptId, String* scriptSource) final;
void getFunctionDetails(ErrorString*, const String& functionId, RefPtr<TypeBuilder::Debugger::FunctionDetails>&) final;
void getGeneratorObjectDetails(ErrorString*, const String& objectId, RefPtr<TypeBuilder::Debugger::GeneratorObjectDetails>&) final;
void getCollectionEntries(ErrorString*, const String& objectId, RefPtr<TypeBuilder::Array<TypeBuilder::Debugger::CollectionEntry>>&) final;
void pause(ErrorString*) final;
void resume(ErrorString*) final;
void stepOver(ErrorString*) final;
void stepInto(ErrorString*) final;
void stepOut(ErrorString*) final;
void stepIntoAsync(ErrorString*) final;
void setPauseOnExceptions(ErrorString*, const String& pauseState) final;
void evaluateOnCallFrame(ErrorString*,
const String& callFrameId,
const String& expression,
const String* objectGroup,
const bool* includeCommandLineAPI,
const bool* doNotPauseOnExceptionsAndMuteConsole,
const bool* returnByValue,
const bool* generatePreview,
RefPtr<TypeBuilder::Runtime::RemoteObject>& result,
TypeBuilder::OptOutput<bool>* wasThrown,
RefPtr<TypeBuilder::Debugger::ExceptionDetails>&) final;
void compileScript(ErrorString*, const String& expression, const String& sourceURL, bool persistScript, const int* executionContextId, TypeBuilder::OptOutput<TypeBuilder::Debugger::ScriptId>*, RefPtr<TypeBuilder::Debugger::ExceptionDetails>&) override;
void runScript(ErrorString*, const TypeBuilder::Debugger::ScriptId&, const int* executionContextId, const String* objectGroup, const bool* doNotPauseOnExceptionsAndMuteConsole, RefPtr<TypeBuilder::Runtime::RemoteObject>& result, RefPtr<TypeBuilder::Debugger::ExceptionDetails>&) override;
void setVariableValue(ErrorString*, int in_scopeNumber, const String& in_variableName, const RefPtr<JSONObject>& in_newValue, const String* in_callFrame, const String* in_functionObjectId) final;
void skipStackFrames(ErrorString*, const String* pattern, const bool* skipContentScripts) final;
void setAsyncCallStackDepth(ErrorString*, int depth) final;
void enablePromiseTracker(ErrorString*, const bool* captureStacks) final;
void disablePromiseTracker(ErrorString*) final;
void getPromiseById(ErrorString*, int promiseId, const String* objectGroup, RefPtr<TypeBuilder::Runtime::RemoteObject>& promise) final;
void flushAsyncOperationEvents(ErrorString*) final;
void setAsyncOperationBreakpoint(ErrorString*, int operationId) final;
void removeAsyncOperationBreakpoint(ErrorString*, int operationId) final;
void schedulePauseOnNextStatement(InspectorFrontend::Debugger::Reason::Enum breakReason, PassRefPtr<JSONObject> data);
void cancelPauseOnNextStatement();
bool canBreakProgram();
void breakProgram(InspectorFrontend::Debugger::Reason::Enum breakReason, PassRefPtr<JSONObject> data);
void willExecuteScript(int scriptId);
void didExecuteScript();
bool enabled();
V8Debugger& debugger() { return *m_debugger; }
void setBreakpoint(const String& scriptId, int lineNumber, int columnNumber, BreakpointSource, const String& condition = String());
void removeBreakpoint(const String& scriptId, int lineNumber, int columnNumber, BreakpointSource);
// Async call stacks implementation
PassRefPtrWillBeRawPtr<ScriptAsyncCallStack> currentAsyncStackTraceForConsole();
static const int unknownAsyncOperationId;
int traceAsyncOperationStarting(const String& description);
void traceAsyncCallbackStarting(int operationId);
void traceAsyncCallbackCompleted();
void traceAsyncOperationCompleted(int operationId);
bool trackingAsyncCalls() const { return m_maxAsyncCallStackDepth; }
class CORE_EXPORT AsyncCallTrackingListener : public WillBeGarbageCollectedMixin {
public:
virtual ~AsyncCallTrackingListener() { }
DEFINE_INLINE_VIRTUAL_TRACE() { }
virtual void asyncCallTrackingStateChanged(bool tracking) = 0;
virtual void resetAsyncOperations() = 0;
};
void addAsyncCallTrackingListener(AsyncCallTrackingListener*);
void removeAsyncCallTrackingListener(AsyncCallTrackingListener*);
// PromiseTracker::Listener
void didUpdatePromise(InspectorFrontend::Debugger::EventType::Enum, PassRefPtr<TypeBuilder::Debugger::PromiseDetails>) final;
InjectedScript injectedScriptForEval(ErrorString*, const int* executionContextId);
InjectedScriptManager* injectedScriptManager() { return m_injectedScriptManager; }
void reset();
private:
bool checkEnabled(ErrorString*);
void enable();
SkipPauseRequest didPause(v8::Local<v8::Context>, v8::Local<v8::Object> callFrames, v8::Local<v8::Value> exception, const Vector<String>& hitBreakpoints, bool isPromiseRejection) final;
void didContinue() final;
SkipPauseRequest shouldSkipExceptionPause();
SkipPauseRequest shouldSkipStepPause();
void schedulePauseOnNextStatementIfSteppingInto();
PassRefPtr<TypeBuilder::Array<TypeBuilder::Debugger::CallFrame>> currentCallFrames();
PassRefPtr<TypeBuilder::Debugger::StackTrace> currentAsyncStackTrace();
bool callStackForId(ErrorString*, const RemoteCallFrameId&, v8::Local<v8::Object>* callStack, bool* isAsync);
void clearCurrentAsyncOperation();
void resetAsyncCallTracker();
void changeJavaScriptRecursionLevel(int step);
void didParseSource(const ParsedScript&) final;
bool v8AsyncTaskEventsEnabled() const final;
void didReceiveV8AsyncTaskEvent(v8::Local<v8::Context>, const String& eventType, const String& eventName, int id) final;
bool v8PromiseEventsEnabled() const final;
void didReceiveV8PromiseEvent(v8::Local<v8::Context>, v8::Local<v8::Object> promise, v8::Local<v8::Value> parentPromise, int status) final;
void setPauseOnExceptionsImpl(ErrorString*, int);
PassRefPtr<TypeBuilder::Debugger::Location> resolveBreakpoint(const String& breakpointId, const String& scriptId, const ScriptBreakpoint&, BreakpointSource);
void removeBreakpoint(const String& breakpointId);
void clearStepIntoAsync();
bool assertPaused(ErrorString*);
void clearBreakDetails();
String sourceMapURLForScript(const Script&, CompileResult);
bool isCallStackEmptyOrBlackboxed();
bool isTopCallFrameBlackboxed();
bool isCallFrameWithUnknownScriptOrBlackboxed(PassRefPtr<JavaScriptCallFrame>);
void internalSetAsyncCallStackDepth(int);
void increaseCachedSkipStackGeneration();
PassRefPtr<TypeBuilder::Debugger::ExceptionDetails> createExceptionDetails(v8::Isolate*, v8::Local<v8::Message>);
typedef HashMap<String, Script> ScriptsMap;
typedef HashMap<String, Vector<String>> BreakpointIdToDebuggerBreakpointIdsMap;
typedef HashMap<String, std::pair<String, BreakpointSource>> DebugServerBreakpointToBreakpointIdAndSourceMap;
enum DebuggerStep {
NoStep = 0,
StepInto,
StepOver,
StepOut
};
RawPtrWillBeMember<InjectedScriptManager> m_injectedScriptManager;
V8Debugger* m_debugger;
Client* m_client;
int m_contextGroupId;
InspectorState* m_state;
InspectorFrontend::Debugger* m_frontend;
v8::Isolate* m_isolate;
RefPtr<ScriptState> m_pausedScriptState;
v8::Global<v8::Object> m_currentCallStack;
ScriptsMap m_scripts;
BreakpointIdToDebuggerBreakpointIdsMap m_breakpointIdToDebuggerBreakpointIds;
DebugServerBreakpointToBreakpointIdAndSourceMap m_serverBreakpoints;
String m_continueToLocationBreakpointId;
InspectorFrontend::Debugger::Reason::Enum m_breakReason;
RefPtr<JSONObject> m_breakAuxData;
DebuggerStep m_scheduledDebuggerStep;
bool m_skipNextDebuggerStepOut;
bool m_javaScriptPauseScheduled;
bool m_steppingFromFramework;
bool m_pausingOnNativeEvent;
bool m_pausingOnAsyncOperation;
int m_skippedStepFrameCount;
int m_recursionLevelForStepOut;
int m_recursionLevelForStepFrame;
bool m_skipAllPauses;
bool m_skipContentScripts;
OwnPtr<ScriptRegexp> m_cachedSkipStackRegExp;
unsigned m_cachedSkipStackGeneration;
WillBeHeapHashSet<RawPtrWillBeWeakMember<AsyncCallTrackingListener>> m_asyncCallTrackingListeners;
// This field must be destroyed before the listeners set above.
OwnPtrWillBeMember<V8AsyncCallTracker> m_v8AsyncCallTracker;
OwnPtrWillBeMember<PromiseTracker> m_promiseTracker;
using AsyncOperationIdToAsyncCallChain = WillBeHeapHashMap<int, RefPtrWillBeMember<AsyncCallChain>>;
AsyncOperationIdToAsyncCallChain m_asyncOperations;
int m_lastAsyncOperationId;
ListHashSet<int> m_asyncOperationNotifications;
HashSet<int> m_asyncOperationBreakpoints;
HashSet<int> m_pausingAsyncOperations;
unsigned m_maxAsyncCallStackDepth;
RefPtrWillBeMember<AsyncCallChain> m_currentAsyncCallChain;
unsigned m_nestedAsyncCallCount;
int m_currentAsyncOperationId;
bool m_pendingTraceAsyncOperationCompleted;
bool m_startingStepIntoAsync;
V8GlobalValueMap<String, v8::Script, v8::kNotWeak> m_compiledScripts;
};
} // namespace blink
#endif // V8DebuggerAgent_h