Ignore:
Timestamp:
Aug 24, 2020, 10:34:12 AM (5 years ago)
Author:
Devin Rousso
Message:

Web Inspector: allow event breakpoints to be configured
https://bugs.webkit.org/show_bug.cgi?id=215362
<rdar://problem/66932921>

Reviewed by Brian Burg.

Source/JavaScriptCore:

This allows developers to do things like:

  • only pause when window.event.type is a certain value
  • ignore the first N pauses
  • evaluate JavaScript whenever an event listener is invoked without pausing
  • inspector/protocol/DOM.json:

Add an options paramater to DOM.setBreakpointForEventListener to allow configuration.

  • inspector/protocol/DOMDebugger.json:

Add an options paramater to DOMDebugger.setEventBreakpoint to allow configuration.

  • debugger/Breakpoint.h:

(JSC::Breakpoint::id const): Added.
(JSC::Breakpoint::sourceID const): Added.
(JSC::Breakpoint::lineNumber const): Added.
(JSC::Breakpoint::columnNumber const): Added.
(JSC::Breakpoint::condition const): Added.
(JSC::Breakpoint::actions const): Added.
(JSC::Breakpoint::isAutoContinue const): Added.
(JSC::Breakpoint::resetHitCount): Added.
(JSC::Breakpoint::isLinked const): Added.
(JSC::Breakpoint::isResolved const): Added.
(JSC::BreakpointsList::~BreakpointsList): Deleted.

  • debugger/Breakpoint.cpp: Added.

(JSC::Breakpoint::Action::Action): Added.
(JSC::Breakpoint::create): Added.
(JSC::Breakpoint::Breakpoint): Added.
(JSC::Breakpoint::link): Added.
(JSC::Breakpoint::resolve): Added.
(JSC::Breakpoint::shouldPause): Added.
Unify JSC::Breakpoint and Inspector::ScriptBreakpoint.

  • debugger/DebuggerPrimitives.h:
  • debugger/Debugger.h:
  • debugger/Debugger.cpp:

(JSC::Debugger::Debugger):
(JSC::Debugger::addObserver): Added.
(JSC::Debugger::removeObserver): Added.
(JSC::Debugger::canDispatchFunctionToObservers const): Added.
(JSC::Debugger::dispatchFunctionToObservers): Added.
(JSC::Debugger::sourceParsed): Added.
(JSC::Debugger::toggleBreakpoint):
(JSC::Debugger::applyBreakpoints):
(JSC::Debugger::resolveBreakpoint):
(JSC::Debugger::setBreakpoint):
(JSC::Debugger::removeBreakpoint):
(JSC::Debugger::didHitBreakpoint): Added.
(JSC::Debugger::clearBreakpoints):
(JSC::Debugger::evaluateBreakpointCondition): Added.
(JSC::Debugger::evaluateBreakpointActions): Added.
(JSC::Debugger::schedulePauseAtNextOpportunity): Added.
(JSC::Debugger::cancelPauseAtNextOpportunity): Added.
(JSC::Debugger::schedulePauseForSpecialBreakpoint): Added.
(JSC::Debugger::cancelPauseForSpecialBreakpoint): Added.
(JSC::Debugger::continueProgram):
(JSC::Debugger::stepNextExpression):
(JSC::Debugger::stepIntoStatement):
(JSC::Debugger::stepOverStatement):
(JSC::Debugger::stepOutOfFunction):
(JSC::Debugger::pauseIfNeeded):
(JSC::Debugger::handlePause): Added.
(JSC::Debugger::exceptionOrCaughtValue): Added.
(JSC::Debugger::atExpression):
(JSC::Debugger::clearNextPauseState):
(JSC::Debugger::willRunMicrotask): Added.
(JSC::Debugger::didRunMicrotask): Added.
(JSC::Debugger::hasBreakpoint): Deleted.
(JSC::Debugger::setPauseOnNextStatement): Deleted.
Unify JSC::Debugger and Inspector::ScriptDebugServer to simplify breakpoint logic.
Introduce the concept of a "special breakpoint", which is essentially a JSC::Breakpoint
that is expected to pause at the next opportunity but isn't tied to a particular location.
As an example, whenever an event breakpoint is hit, instead of just pausing at the next
opportunity, the newly managed JSC::Breakpoint is used as a "special breakpoint", allowing
for it's configuration (ie.g. condition, ignore count, actions, auto-continue) to be used.

  • inspector/agents/InspectorDebuggerAgent.h:
  • inspector/agents/InspectorDebuggerAgent.cpp:

(Inspector::objectGroupForBreakpointAction):
(Inspector::breakpointActionTypeForString): Added.
(Inspector::parseBreakpointOptions): Added.
(Inspector::InspectorDebuggerAgent::ProtocolBreakpoint::fromPayload): Added.
(Inspector::InspectorDebuggerAgent::ProtocolBreakpoint::ProtocolBreakpoint): Added.
(Inspector::InspectorDebuggerAgent::ProtocolBreakpoint::createDebuggerBreakpoint const): Added.
(Inspector::InspectorDebuggerAgent::ProtocolBreakpoint::matchesScriptURL const): Added.
(Inspector::InspectorDebuggerAgent::debuggerBreakpointFromPayload): Added.
(Inspector::InspectorDebuggerAgent::enable):
(Inspector::InspectorDebuggerAgent::disable):
(Inspector::InspectorDebuggerAgent::buildBreakpointPauseReason):
(Inspector::InspectorDebuggerAgent::handleConsoleAssert):
(Inspector::InspectorDebuggerAgent::didScheduleAsyncCall):
(Inspector::buildDebuggerLocation):
(Inspector::InspectorDebuggerAgent::setBreakpointByUrl):
(Inspector::InspectorDebuggerAgent::setBreakpoint):
(Inspector::InspectorDebuggerAgent::didSetBreakpoint):
(Inspector::InspectorDebuggerAgent::resolveBreakpoint):
(Inspector::InspectorDebuggerAgent::removeBreakpoint):
(Inspector::InspectorDebuggerAgent::continueToLocation):
(Inspector::InspectorDebuggerAgent::schedulePauseAtNextOpportunity): Added.
(Inspector::InspectorDebuggerAgent::cancelPauseAtNextOpportunity): Added.
(Inspector::InspectorDebuggerAgent::schedulePauseForSpecialBreakpoint): Added.
(Inspector::InspectorDebuggerAgent::cancelPauseForSpecialBreakpoint): Added.
(Inspector::InspectorDebuggerAgent::pause):
(Inspector::InspectorDebuggerAgent::resume):
(Inspector::InspectorDebuggerAgent::didBecomeIdle):
(Inspector::InspectorDebuggerAgent::sourceMapURLForScript):
(Inspector::InspectorDebuggerAgent::didParseSource):
(Inspector::InspectorDebuggerAgent::willRunMicrotask):
(Inspector::InspectorDebuggerAgent::didRunMicrotask):
(Inspector::InspectorDebuggerAgent::didPause):
(Inspector::InspectorDebuggerAgent::breakpointActionSound):
(Inspector::InspectorDebuggerAgent::breakpointActionProbe):
(Inspector::InspectorDebuggerAgent::clearInspectorBreakpointState):
(Inspector::InspectorDebuggerAgent::clearDebuggerBreakpointState):
(Inspector::matches): Deleted.
(Inspector::buildObjectForBreakpointCookie): Deleted.
(Inspector::InspectorDebuggerAgent::breakpointActionsFromProtocol): Deleted.
(Inspector::InspectorDebuggerAgent::schedulePauseOnNextStatement): Deleted.
(Inspector::InspectorDebuggerAgent::cancelPauseOnNextStatement): Deleted.
Create a private ProtocolBreakpoint class that holds the data sent by the frontend. This
is necessary because breakpoints in the frontend have a potentially one-to-many relationship
with breakpoints in the backend, as the same script can be loaded many times on a page. Each
of those scripts is independent, however, and can execute differently, meaning that the same
breakpoint for each script also needs a different state (e.g. ignore count). As such, the
ProtocolBreakpoint is effectively a template that is actualized whenever a new script is
parsed that matches the URL of the ProtocolBreakpoint to create a JSC::Breakpoint that
is used by the JSC::Debugger. ProtocolBreakpoint also parses breakpoint configurations.

  • inspector/InspectorEnvironment.h:
  • inspector/JSGlobalObjectScriptDebugServer.h:
  • inspector/JSGlobalObjectScriptDebugServer.cpp:

(Inspector::JSGlobalObjectScriptDebugServer::JSGlobalObjectScriptDebugServer):
(Inspector::JSGlobalObjectScriptDebugServer::attachDebugger):
(Inspector::JSGlobalObjectScriptDebugServer::detachDebugger):
(Inspector::JSGlobalObjectScriptDebugServer::runEventLoopWhilePaused):

  • inspector/agents/InspectorAuditAgent.h:
  • inspector/agents/InspectorAuditAgent.cpp:

(Inspector::InspectorAuditAgent::run):

  • inspector/agents/InspectorRuntimeAgent.h:
  • inspector/agents/InspectorRuntimeAgent.cpp:

(Inspector::setPauseOnExceptionsState):
(Inspector::InspectorRuntimeAgent::evaluate):
(Inspector::InspectorRuntimeAgent::callFunctionOn):
(Inspector::InspectorRuntimeAgent::getPreview):
(Inspector::InspectorRuntimeAgent::getProperties):
(Inspector::InspectorRuntimeAgent::getDisplayableProperties):

  • inspector/agents/InspectorScriptProfilerAgent.cpp:
  • inspector/agents/JSGlobalObjectDebuggerAgent.h:

Replace Inspector::ScriptDebugServer with JSC::Debugger.

  • runtime/JSMicrotask.cpp:

(JSC::JSMicrotask::run):
Drive-by: r248894 mistakenly omitted the call to notify the debugger that the microtask ran.

  • inspector/ScriptBreakpoint.h: Removed.
  • inspector/ScriptDebugListener.h: Removed.
  • inspector/ScriptDebugServer.h: Removed.
  • inspector/ScriptDebugServer.cpp: Removed.
  • CMakeLists.txt:
  • JavaScriptCore.xcodeproj/project.pbxproj:
  • Sources.txt:

Source/WebCore:

This allows developers to do things like:

  • only pause when window.event.type is a certain value
  • ignore the first N pauses
  • evaluate JavaScript whenever an event listener is invoked without pausing

Tests: inspector/dom/breakpoint-for-event-listener.html

inspector/dom-debugger/event-animation-frame-breakpoints.html
inspector/dom-debugger/event-interval-breakpoints.html
inspector/dom-debugger/event-listener-breakpoints.html
inspector/dom-debugger/event-timeout-breakpoints.html

  • inspector/agents/InspectorDOMAgent.h:
  • inspector/agents/InspectorDOMAgent.cpp:

(WebCore::InspectorDOMAgent::getEventListenersForNode):
(WebCore::InspectorDOMAgent::setBreakpointForEventListener):
(WebCore::InspectorDOMAgent::removeBreakpointForEventListener):
(WebCore::InspectorDOMAgent::buildObjectForEventListener):
(WebCore::InspectorDOMAgent::breakpointForEventListener):
(WebCore::InspectorDOMAgent::hasBreakpointForEventListener): Deleted.

  • inspector/agents/InspectorDOMDebuggerAgent.h:
  • inspector/agents/InspectorDOMDebuggerAgent.cpp:

(WebCore::InspectorDOMDebuggerAgent::disable):
(WebCore::InspectorDOMDebuggerAgent::mainFrameNavigated):
(WebCore::InspectorDOMDebuggerAgent::setEventBreakpoint):
(WebCore::InspectorDOMDebuggerAgent::removeEventBreakpoint):
(WebCore::InspectorDOMDebuggerAgent::willHandleEvent):
(WebCore::InspectorDOMDebuggerAgent::didHandleEvent):
(WebCore::InspectorDOMDebuggerAgent::willFireTimer):
(WebCore::InspectorDOMDebuggerAgent::didFireTimer):

  • inspector/agents/page/PageDOMDebuggerAgent.h:
  • inspector/agents/page/PageDOMDebuggerAgent.cpp:

(WebCore::PageDOMDebuggerAgent::disable):
(WebCore::PageDOMDebuggerAgent::mainFrameNavigated):
(WebCore::PageDOMDebuggerAgent::willFireAnimationFrame):
(WebCore::PageDOMDebuggerAgent::didFireAnimationFrame):
(WebCore::PageDOMDebuggerAgent::setAnimationFrameBreakpoint):

  • inspector/agents/worker/WorkerDOMDebuggerAgent.h:
  • inspector/agents/worker/WorkerDOMDebuggerAgent.cpp:

(WebCore::WorkerDOMDebuggerAgent::setAnimationFrameBreakpoint):
Keep a JSC::Breakpoint for each event breakpoint instead of a simple bool, allowing for
configuration when the breakpoint is first set. When any of these breakpoints are hit, pass
it to the JSC::Debugger as a "special breakpoint", which behaves the same as "pause ASAP"
but also supports a condition, an ignore count, actions, and auto-continue. Reset the hit
count for any of these "special breakpoints" that persist across Web Inspector sessions
when the main frame navigates.

  • inspector/PageScriptDebugServer.h:
  • inspector/PageScriptDebugServer.cpp:

(WebCore::PageScriptDebugServer::PageScriptDebugServer):
(WebCore::PageScriptDebugServer::attachDebugger):
(WebCore::PageScriptDebugServer::detachDebugger):
(WebCore::PageScriptDebugServer::didPause):
(WebCore::PageScriptDebugServer::didContinue):
(WebCore::PageScriptDebugServer::runEventLoopWhilePaused):
(WebCore::PageScriptDebugServer::runEventLoopWhilePausedInternal):
(WebCore::PageScriptDebugServer::isContentScript const):
(WebCore::PageScriptDebugServer::reportException const):

  • inspector/WorkerScriptDebugServer.h:
  • inspector/WorkerScriptDebugServer.cpp:

(WebCore::WorkerScriptDebugServer::WorkerScriptDebugServer):
(WebCore::WorkerScriptDebugServer::attachDebugger):
(WebCore::WorkerScriptDebugServer::detachDebugger):
(WebCore::WorkerScriptDebugServer::runEventLoopWhilePaused):
(WebCore::WorkerScriptDebugServer::reportException const):

  • inspector/agents/page/PageDebuggerAgent.h:
  • inspector/agents/page/PageDebuggerAgent.cpp:

(WebCore::PageDebuggerAgent::sourceMapURLForScript):
Replace Inspector::ScriptDebugServer with JSC::Debugger.

  • inspector/TimelineRecordFactory.h:
  • inspector/TimelineRecordFactory.cpp:

(WebCore::TimelineRecordFactory::createProbeSampleData):

  • inspector/agents/InspectorTimelineAgent.h:
  • inspector/agents/InspectorTimelineAgent.cpp:

(WebCore::InspectorTimelineAgent::internalStart):
(WebCore::InspectorTimelineAgent::internalStop):
(WebCore::InspectorTimelineAgent::breakpointActionProbe):
Replace Inspector::ScriptBreakpoint with JSC::Breakpoint.

  • inspector/InspectorInstrumentation.h:

(WebCore::InspectorInstrumentation::didHandleEvent):
(WebCore::InspectorInstrumentation::didFireTimer):

  • inspector/InspectorInstrumentation.cpp:

(WebCore::InspectorInstrumentation::didHandleEventImpl):
(WebCore::InspectorInstrumentation::didFireTimerImpl):
(WebCore::InspectorInstrumentation::didCommitLoadImpl):
(WebCore::InspectorInstrumentation::didFireAnimationFrameImpl):

  • dom/EventTarget.cpp:

(WebCore::EventTarget::innerInvokeEventListeners):

  • page/DOMTimer.cpp:

(WebCore::DOMTimer::fired):
When notifying Web Inspector that activity did occur, include all information previously
included when notifying Web Inspector that that activity was about to occur so that Web
Inspector can know whether a pause for the "special breakpoint" for that activity is still
scheduled and if so cancel it.

Source/WebInspectorUI:

This allows developers to do things like:

  • only pause when window.event.type is a certain value
  • ignore the first N pauses
  • evaluate JavaScript whenever an event listener is invoked without pausing
  • UserInterface/Models/Breakpoint.js:

(WI.Breakpoint):
(WI.Breakpoint.prototype.toJSON):
(WI.Breakpoint.prototype.get special): Added.
(WI.Breakpoint.prototype.get removable): Added.
(WI.Breakpoint.prototype.get editable): Added.
(WI.Breakpoint.prototype.set condition):
(WI.Breakpoint.prototype.get ignoreCount):
(WI.Breakpoint.prototype.set ignoreCount):
(WI.Breakpoint.prototype.get autoContinue):
(WI.Breakpoint.prototype.set autoContinue):
(WI.Breakpoint.prototype.get actions):
(WI.Breakpoint.prototype.get probeActions):
(WI.Breakpoint.prototype.cycleToNextMode):
(WI.Breakpoint.prototype.createAction):
(WI.Breakpoint.prototype.recreateAction):
(WI.Breakpoint.prototype.removeAction):
(WI.Breakpoint.prototype.clearActions):
(WI.Breakpoint.prototype.remove): Added.
(WI.Breakpoint.prototype.optionsToProtocol): Added.
(WI.Breakpoint.prototype.breakpointActionDidChange):
(WI.Breakpoint.fromJSON): Deleted.
(WI.Breakpoint.prototype.get sourceCodeLocation): Deleted.
(WI.Breakpoint.prototype.get contentIdentifier): Deleted.
(WI.Breakpoint.prototype.get scriptIdentifier): Deleted.
(WI.Breakpoint.prototype.get target): Deleted.
(WI.Breakpoint.prototype.get identifier): Deleted.
(WI.Breakpoint.prototype.set identifier): Deleted.
(WI.Breakpoint.prototype.get resolved): Deleted.
(WI.Breakpoint.prototype.set resolved): Deleted.
(WI.Breakpoint.prototype.saveIdentityToCookie): Deleted.
(WI.Breakpoint.prototype._isSpecial): Deleted.
(WI.Breakpoint.prototype._sourceCodeLocationLocationChanged): Deleted.
(WI.Breakpoint.prototype._sourceCodeLocationDisplayLocationChanged): Deleted.

  • UserInterface/Models/DOMBreakpoint.js:

(WI.DOMBreakpoint):
(WI.DOMBreakpoint.fromJSON): Added.
(WI.DOMBreakpoint.prototype.remove): Added.
(WI.DOMBreakpoint.prototype.toJSON):
(WI.DOMBreakpoint.deserialize): Deleted.
(WI.DOMBreakpoint.prototype.get disabled): Deleted.
(WI.DOMBreakpoint.prototype.set disabled): Deleted.

  • UserInterface/Models/EventBreakpoint.js:

(WI.EventBreakpoint):
(WI.EventBreakpoint.fromJSON): Added.
(WI.EventBreakpoint.prototype.get special): Added.
(WI.EventBreakpoint.prototype.get editable): Added.
(WI.EventBreakpoint.prototype.remove): Added.
(WI.EventBreakpoint.prototype.saveIdentityToCookie):
(WI.EventBreakpoint.prototype.toJSON):
(WI.EventBreakpoint.deserialize): Deleted.
(WI.EventBreakpoint.prototype.get disabled): Deleted.
(WI.EventBreakpoint.prototype.set disabled): Deleted.

  • UserInterface/Models/JavaScriptBreakpoint.js: Copied from UserInterface/Models/Breakpoint.js.

(WI.JavaScriptBreakpoint):
(WI.JavaScriptBreakpoint.fromJSON):
(WI.JavaScriptBreakpoint.prototype.toJSON):
(WI.JavaScriptBreakpoint.prototype.get sourceCodeLocation):
(WI.JavaScriptBreakpoint.prototype.get contentIdentifier):
(WI.JavaScriptBreakpoint.prototype.get scriptIdentifier):
(WI.JavaScriptBreakpoint.prototype.get target):
(WI.JavaScriptBreakpoint.prototype.get special): Added.
(WI.JavaScriptBreakpoint.prototype.get removable): Added.
(WI.JavaScriptBreakpoint.prototype.get editable): Added.
(WI.JavaScriptBreakpoint.prototype.get identifier):
(WI.JavaScriptBreakpoint.prototype.set identifier):
(WI.JavaScriptBreakpoint.prototype.get resolved):
(WI.JavaScriptBreakpoint.prototype.set resolved):
(WI.JavaScriptBreakpoint.prototype.remove): Added.
(WI.JavaScriptBreakpoint.prototype.saveIdentityToCookie):
(WI.JavaScriptBreakpoint.prototype._isSpecial):
(WI.JavaScriptBreakpoint.prototype._sourceCodeLocationLocationChanged):
(WI.JavaScriptBreakpoint.prototype._sourceCodeLocationDisplayLocationChanged):

  • UserInterface/Models/URLBreakpoint.js:

(WI.URLBreakpoint):
(WI.URLBreakpoint.fromJSON): Added.
(WI.URLBreakpoint.prototype.get special): Added.
(WI.URLBreakpoint.prototype.remove): Added.
(WI.URLBreakpoint.prototype.toJSON):
(WI.URLBreakpoint.deserialize): Deleted.
(WI.URLBreakpoint.prototype.get disabled): Deleted.
(WI.URLBreakpoint.prototype.set disabled): Deleted.
Rename WI.Breakpoint to WI.JavaScriptBreakpoint and use WI.Breakpoint as a new common
base class for all breakpoint types, allowing more logic to be shared (e.g. disabled state).
Additionally, breakpoints are now able to

  • determine whether or not they're
    • special
    • removable
    • editable (i.e. configurable)
  • remove themselves

without the caller needing to know what manager to consult with.

  • UserInterface/Controllers/DOMManager.js:

(WI.DOMManager):
(WI.DOMManager.supportsEventListenerBreakpointConfiguration): Added.
(WI.DOMManager.prototype.setBreakpointForEventListener):
(WI.DOMManager.prototype.removeBreakpointForEventListener):
(WI.DOMManager.prototype._setEventBreakpoint): Added.
(WI.DOMManager.prototype._removeEventBreakpoint): Added.
(WI.DOMManager.prototype._handleEventBreakpointEditablePropertyChanged): Added.
(WI.DOMManager.prototype._handleEventBreakpointActionsChanged): Added.
(WI.DOMManager.prototype._updateEventBreakpoint): Deleted.
Keep track of configuration changes for specific listener breakpoints.

  • UserInterface/Controllers/DOMDebuggerManager.js:

(WI.DOMDebuggerManager):
(WI.DOMDebuggerManager.prototype.initializeTarget):
(WI.DOMDebuggerManager.prototype.addDOMBreakpoint):
(WI.DOMDebuggerManager.prototype.removeDOMBreakpoint):
(WI.DOMDebuggerManager.prototype.addEventBreakpoint):
(WI.DOMDebuggerManager.prototype.removeEventBreakpoint):
(WI.DOMDebuggerManager.prototype.addURLBreakpoint):
(WI.DOMDebuggerManager.prototype.removeURLBreakpoint):
(WI.DOMDebuggerManager.prototype._commandArgumentsForEventBreakpoint): Added.
(WI.DOMDebuggerManager.prototype._setEventBreakpoint): Added.
(WI.DOMDebuggerManager.prototype._removeEventBreakpoint): Added.
(WI.DOMDebuggerManager.prototype._handleEventBreakpointDisabledStateChanged): Added.
(WI.DOMDebuggerManager.prototype._handleEventBreakpointEditablePropertyChanged): Added.
(WI.DOMDebuggerManager.prototype._handleEventBreakpointActionsChanged): Added.
(WI.DOMDebuggerManager.prototype.isBreakpointSpecial): Deleted.
(WI.DOMDebuggerManager.prototype._updateEventBreakpoint): Deleted.
Keep track of configuration changes for special event breakpoints.
Store special event breakpoints inside WI.objectStores.eventBreakpoints.

  • UserInterface/Controllers/DebuggerManager.js:

(WI.DebuggerManager):
(WI.DebuggerManager.prototype.addBreakpoint):
(WI.DebuggerManager.prototype.removeBreakpoint):
(WI.DebuggerManager.prototype.addProbesForBreakpoint): Added.
(WI.DebuggerManager.prototype.removeProbesForBreakpoint): Added.
(WI.DebuggerManager.prototype.updateProbesForBreakpoint): Added.
(WI.DebuggerManager.prototype._setBreakpoint):
(WI.DebuggerManager.prototype._breakpointEditablePropertyDidChange):
(WI.DebuggerManager.prototype._handleBreakpointActionsDidChange):
(WI.DebuggerManager.prototype.isBreakpointRemovable): Deleted.
(WI.DebuggerManager.prototype.isBreakpointSpecial): Deleted.
(WI.DebuggerManager.prototype.isBreakpointEditable): Deleted.
(WI.DebuggerManager.prototype._debuggerBreakpointActionType): Deleted.
(WI.DebuggerManager.prototype._debuggerBreakpointOptions): Deleted.
(WI.DebuggerManager.prototype._addProbesForBreakpoint): Deleted.
(WI.DebuggerManager.prototype._removeProbesForBreakpoint): Deleted.
(WI.DebuggerManager.prototype._updateProbesForBreakpoint): Deleted.
Replace WI.Breakpoint with WI.JavaScriptBreakpoint.
Probes now support WI.EventBreakpoint in addition to WI.JavaScriptBreakpoint.

  • UserInterface/Controllers/BreakpointPopoverController.js:

(WI.BreakpointPopoverController.prototype.appendContextMenuItems):
(WI.BreakpointPopoverController.prototype._createPopoverContent):
Allow any breakpoint instead of only WI.JavaScriptBreakpoint.
Drive-by: the existing ignoreCount value wasn't being used to populate the <input>.

  • UserInterface/Views/BreakpointTreeElement.js:

(WI.BreakpointTreeElement.prototype.ondelete):
(WI.BreakpointTreeElement.prototype.get listenerSet): Added.
(WI.BreakpointTreeElement.prototype.updateStatus): Added.
(WI.BreakpointTreeElement.prototype.updateTitles): Added.
(WI.BreakpointTreeElement.prototype.get breakpoint): Deleted.
(WI.BreakpointTreeElement.prototype.get filterableData): Deleted.
(WI.BreakpointTreeElement.prototype._updateTitles): Deleted.
(WI.BreakpointTreeElement.prototype._updateStatus): Deleted.
(WI.BreakpointTreeElement.prototype._breakpointLocationDidChange): Deleted.

  • UserInterface/Views/BreakpointTreeElement.css:

(.item.breakpoint .status > .status-image):
(.item.breakpoint.paused .icon): Added.
(@media (prefers-color-scheme: dark) .item.breakpoint.paused .icon): Added.
(.item.breakpoint .status > .status-image.resolved): Deleted.
(body:not(.window-inactive, .window-docked-inactive) .tree-outline:focus-within .item.breakpoint.selected .status > .status-image.resolved): Deleted.
(.item.breakpoint .subtitle.formatted-location): Deleted.
(.breakpoint-debugger-statement-icon .icon): Deleted.
(.breakpoint-exception-icon .icon): Deleted.
(.breakpoint-assertion-icon .icon): Deleted.
(.breakpoint-microtask-icon .icon): Deleted.
(.breakpoint-paused-icon .icon): Deleted.
(.breakpoint-generic-line-icon .icon): Deleted.
(.breakpoint-generic-line-icon .icon > span): Deleted.
(.data-updated.breakpoint-generic-line-icon .icon > span): Deleted.
(@media (prefers-color-scheme: dark) .breakpoint-debugger-statement-icon .icon): Deleted.
(@media (prefers-color-scheme: dark) .breakpoint-exception-icon .icon): Deleted.
(@media (prefers-color-scheme: dark) .breakpoint-assertion-icon .icon): Deleted.
(@media (prefers-color-scheme: dark) .breakpoint-microtask-icon .icon): Deleted.
(@media (prefers-color-scheme: dark) .breakpoint-paused-icon .icon): Deleted.
(@media (prefers-color-scheme: dark) .breakpoint-generic-line-icon .icon): Deleted.

  • UserInterface/Views/DOMBreakpointTreeElement.js:

(WI.DOMBreakpointTreeElement):
(WI.DOMBreakpointTreeElement.prototype.onattach): Deleted.
(WI.DOMBreakpointTreeElement.prototype.ondetach): Deleted.
(WI.DOMBreakpointTreeElement.prototype.ondelete): Deleted.
(WI.DOMBreakpointTreeElement.prototype.onenter): Deleted.
(WI.DOMBreakpointTreeElement.prototype.onspace): Deleted.
(WI.DOMBreakpointTreeElement.prototype.populateContextMenu): Deleted.
(WI.DOMBreakpointTreeElement.prototype._statusImageElementClicked): Deleted.
(WI.DOMBreakpointTreeElement.prototype._statusImageElementFocused): Deleted.
(WI.DOMBreakpointTreeElement.prototype._statusImageElementMouseDown): Deleted.
(WI.DOMBreakpointTreeElement.prototype._toggleBreakpoint): Deleted.
(WI.DOMBreakpointTreeElement.prototype._updateStatus): Deleted.

  • UserInterface/Views/DOMBreakpointTreeElement.css:

(.item.breakpoint.dom.subtree-modified:not(.paused) .icon): Added.
(.item.breakpoint.dom.attribute-modified:not(.paused) .icon): Added.
(.item.breakpoint.dom.node-removed:not(.paused) .icon): Added.
(@media (prefers-color-scheme: dark) .item.breakpoint.dom.subtree-modified:not(.paused) .icon): Added.
(@media (prefers-color-scheme: dark) .item.breakpoint.dom.attribute-modified:not(.paused) .icon): Added.
(@media (prefers-color-scheme: dark) .item.breakpoint.dom.node-removed:not(.paused) .icon): Added.
(.breakpoint.dom.breakpoint-for-subtree-modified:not(.breakpoint-paused-icon) .icon): Deleted.
(.breakpoint.dom.breakpoint-for-attribute-modified:not(.breakpoint-paused-icon) .icon): Deleted.
(.breakpoint.dom.breakpoint-for-node-removed:not(.breakpoint-paused-icon) .icon): Deleted.
(@media (prefers-color-scheme: dark) .breakpoint.dom.breakpoint-for-subtree-modified:not(.breakpoint-paused-icon) .icon): Deleted.
(@media (prefers-color-scheme: dark) .breakpoint.dom.breakpoint-for-attribute-modified:not(.breakpoint-paused-icon) .icon): Deleted.
(@media (prefers-color-scheme: dark) .breakpoint.dom.breakpoint-for-node-removed:not(.breakpoint-paused-icon) .icon): Deleted.

  • UserInterface/Views/EventBreakpointTreeElement.js:

(WI.EventBreakpointTreeElement):
(WI.EventBreakpointTreeElement.prototype.onattach): Deleted.
(WI.EventBreakpointTreeElement.prototype.ondetach): Deleted.
(WI.EventBreakpointTreeElement.prototype.ondelete): Deleted.
(WI.EventBreakpointTreeElement.prototype.onenter): Deleted.
(WI.EventBreakpointTreeElement.prototype.onspace): Deleted.
(WI.EventBreakpointTreeElement.prototype.populateContextMenu): Deleted.
(WI.EventBreakpointTreeElement.prototype._statusImageElementClicked): Deleted.
(WI.EventBreakpointTreeElement.prototype._statusImageElementFocused): Deleted.
(WI.EventBreakpointTreeElement.prototype._statusImageElementMouseDown): Deleted.
(WI.EventBreakpointTreeElement.prototype._toggleBreakpoint): Deleted.
(WI.EventBreakpointTreeElement.prototype._updateStatus): Deleted.

  • UserInterface/Views/EventBreakpointTreeElement.css:

(.item.breakpoint.event.animation-frame:not(.paused) .icon): Added.
(.item.breakpoint.event.interval:not(.paused) .icon): Added.
(.item.breakpoint.event.listener:not(.paused) .icon): Added.
(.item.breakpoint.event.timeout:not(.paused) .icon): Added.
(@media(prefers-color-scheme: dark) .item.breakpoint.event.animation-frame:not(.paused) .icon): Added.
(@media(prefers-color-scheme: dark) .item.breakpoint.event.interval:not(.paused) .icon): Added.
(@media(prefers-color-scheme: dark) .item.breakpoint.event.listener:not(.paused) .icon): Added.
(@media(prefers-color-scheme: dark) .item.breakpoint.event.timeout:not(.paused) .icon): Added.
(.breakpoint.event.breakpoint-for-animation-frame:not(.breakpoint-paused-icon) .icon): Deleted.
(.breakpoint.event.breakpoint-for-interval:not(.breakpoint-paused-icon) .icon): Deleted.
(.breakpoint.event.breakpoint-for-listener:not(.breakpoint-paused-icon) .icon): Deleted.
(.breakpoint.event.breakpoint-for-timeout:not(.breakpoint-paused-icon) .icon): Deleted.
(@media(prefers-color-scheme: dark) .breakpoint.event.breakpoint-for-animation-frame:not(.breakpoint-paused-icon) .icon): Deleted.
(@media(prefers-color-scheme: dark) .breakpoint.event.breakpoint-for-interval:not(.breakpoint-paused-icon) .icon): Deleted.
(@media(prefers-color-scheme: dark) .breakpoint.event.breakpoint-for-listener:not(.breakpoint-paused-icon) .icon): Deleted.
(@media(prefers-color-scheme: dark) .breakpoint.event.breakpoint-for-timeout:not(.breakpoint-paused-icon) .icon): Deleted.

  • UserInterface/Views/JavaScriptBreakpointTreeElement.js: Copied from Source/WebInspectorUI/UserInterface/Views/BreakpointTreeElement.js.

(WI.JavaScriptBreakpointTreeElement):
(WI.JavaScriptBreakpointTreeElement.prototype.get filterableData):
(WI.JavaScriptBreakpointTreeElement.prototype.updateStatus): Added.
(WI.JavaScriptBreakpointTreeElement.prototype.updateTitles): Added.
(WI.JavaScriptBreakpointTreeElement.prototype._breakpointLocationDidChange):

  • UserInterface/Views/JavaScriptBreakpointTreeElement.css: Copied from Source/WebInspectorUI/UserInterface/Views/BreakpointTreeElement.css.

(.item.breakpoint.javascript .status > .status-image): Added.
(.item.breakpoint.javascript .status > .status-image.resolved): Added.
(body:not(.window-inactive, .window-docked-inactive) .tree-outline:focus-within .item.breakpoint.javascript.selected .status > .status-image.resolved): Added.
(.item.breakpoint.javascript .subtitle.formatted-location): Added.
(.item.breakpoint.javascript.line .icon): Added.
(.item.breakpoint.javascript.line .icon > span): Added.
(.data-updated.item.breakpoint.javascript.line .icon > span): Added.
(.item.breakpoint.javascript.debugger-statement .icon): Added.
(.item.breakpoint.javascript.exception .icon): Added.
(.item.breakpoint.javascript.assertion .icon): Added.
(.item.breakpoint.javascript.microtask .icon): Added.
(@media (prefers-color-scheme: dark) .item.breakpoint.javascript.line .icon): Added.
(@media (prefers-color-scheme: dark) .item.breakpoint.javascript.debugger-statement .icon): Added.
(@media (prefers-color-scheme: dark) .item.breakpoint.javascript.exception .icon): Added.
(@media (prefers-color-scheme: dark) .item.breakpoint.javascript.assertion .icon): Added.
(@media (prefers-color-scheme: dark) .item.breakpoint.javascript.microtask .icon): Added.

  • UserInterface/Views/URLBreakpointTreeElement.js:

(WI.URLBreakpointTreeElement):
(WI.URLBreakpointTreeElement.prototype.onattach): Deleted.
(WI.URLBreakpointTreeElement.prototype.ondetach): Deleted.
(WI.URLBreakpointTreeElement.prototype.ondelete): Deleted.
(WI.URLBreakpointTreeElement.prototype.onenter): Deleted.
(WI.URLBreakpointTreeElement.prototype.onspace): Deleted.
(WI.URLBreakpointTreeElement.prototype.populateContextMenu): Deleted.
(WI.URLBreakpointTreeElement.prototype._statusImageElementClicked): Deleted.
(WI.URLBreakpointTreeElement.prototype._statusImageElementFocused): Deleted.
(WI.URLBreakpointTreeElement.prototype._statusImageElementMouseDown): Deleted.
(WI.URLBreakpointTreeElement.prototype._toggleBreakpoint): Deleted.
(WI.URLBreakpointTreeElement.prototype._updateStatus): Deleted.

  • UserInterface/Views/URLBreakpointTreeElement.css:

(.item.breakpoint.url .subtitle): Added.
(.item.breakpoint.url:not(.paused) .icon): Added.
(@media (prefers-color-scheme: dark) .item.breakpoint.url:not(.paused) .icon): Added.
(.breakpoint.url .subtitle): Deleted.
(.breakpoint.url:not(.breakpoint-paused-icon) .icon): Deleted.
(@media (prefers-color-scheme: dark) .breakpoint.url:not(.breakpoint-paused-icon) .icon): Deleted.
Rename WI.BreakpointTreeElement to WI.JavaScriptBreakpointTreeElement and use
WI.BreakpointTreeElement as a new common base class for all breakpoint tree elements,
allowing more logic and styles to be shared (e.g. disabled state).

  • UserInterface/Views/SourcesNavigationSidebarPanel.js:

(WI.SourcesNavigationSidebarPanel):
(WI.SourcesNavigationSidebarPanel.prototype.closed):
(WI.SourcesNavigationSidebarPanel.prototype._insertDebuggerTreeElement):
(WI.SourcesNavigationSidebarPanel.prototype._compareJavaScriptBreakpointTreeElements): Added.
(WI.SourcesNavigationSidebarPanel.prototype._addBreakpoint):
(WI.SourcesNavigationSidebarPanel.prototype._removeAllBreakpoints):
(WI.SourcesNavigationSidebarPanel.prototype._breakpointsBeneathTreeElement):
(WI.SourcesNavigationSidebarPanel.prototype._addIssue):
(WI.SourcesNavigationSidebarPanel.prototype._updatePauseReasonSection):
(WI.SourcesNavigationSidebarPanel.prototype._handleTreeSelectionDidChange):
(WI.SourcesNavigationSidebarPanel.prototype._handleBreakpointElementAddedOrRemoved):
(WI.SourcesNavigationSidebarPanel.prototype._populateCreateBreakpointContextMenu.addToggleForSpecialEventBreakpoint):
(WI.SourcesNavigationSidebarPanel.prototype._populateCreateBreakpointContextMenu):
(WI.SourcesNavigationSidebarPanel.prototype._handleDebuggerObjectDisplayLocationDidChange):
(WI.SourcesNavigationSidebarPanel.prototype._compareBreakpointTreeElements): Deleted.

  • UserInterface/Models/ProbeSet.js:

(WI.ProbeSet):
(WI.ProbeSet.prototype.createProbe):
(WI.ProbeSet.prototype.willRemove):

  • UserInterface/Controllers/TimelineManager.js:

(WI.TimelineManager.prototype._processRecord):

  • UserInterface/Views/ProbeSetDetailsSection.js:

(WI.ProbeSetDetailsSection):

  • UserInterface/Views/ProbeDetailsSidebarPanel.js:

(WI.ProbeDetailsSidebarPanel.prototype.inspect):

  • UserInterface/Views/SourceCodeTextEditor.js:

(WI.SourceCodeTextEditor):
(WI.SourceCodeTextEditor.prototype.close):
(WI.SourceCodeTextEditor.prototype.textEditorBreakpointAdded):

  • UserInterface/Views/TextResourceContentView.js:

(WI.TextResourceContentView.prototype.get supplementalRepresentedObjects):
(WI.TextResourceContentView.prototype._probeSetsChanged):
Probes now support WI.EventBreakpoint in addition to WI.JavaScriptBreakpoint.

  • UserInterface/Views/ContentView.js:

(WI.ContentView.createFromRepresentedObject):
(WI.ContentView.resolvedRepresentedObjectForRepresentedObject):
(WI.ContentView.isViewable):

  • UserInterface/Views/ContextMenuUtilities.js:

(WI.appendContextMenuItemsForSourceCode):
Replace WI.Breakpoint with WI.JavaScriptBreakpoint.

  • UserInterface/Views/DOMTreeContentView.js:

(WI.DOMTreeContentView):
Replace WI.DOMBreakpoint with WI.Breakpoint.

  • UserInterface/Views/EventListenerSectionGroup.js:

(WI.EventListenerSectionGroup):

  • UserInterface/Views/EventListenerSectionGroup.css:

(.event-listener-section > .content input[type="checkbox"] + .go-to-arrow): Added.
(.event-listener-section > .content input[type="checkbox"]:not(:checked) + .go-to-arrow): Added.
Add a go-to arrow next to the Breakpoint checkbox that reveals the WI.EventBreakpoint in
the Sources Tab.

  • UserInterface/Views/BreakpointActionView.js:

(WI.BreakpointActionView.prototype._appendActionButtonClicked):
Drive-by: minor code cleanup.

  • UserInterface/Views/CallFrameTreeElement.js:

(WI.CallFrameTreeElement.prototype.populateContextMenu):
Drive-by: include source code location context menu items.

  • UserInterface/Base/Setting.js:
  • UserInterface/Main.html:
  • UserInterface/Test.html:

LayoutTests:

  • inspector/dom-debugger/resources/event-breakpoint-utilities.js:

(TestPage.registerInitializer.InspectorTest.EventBreakpoint.addBreakpointOptionsTestCases): Added.
(TestPage.registerInitializer.InspectorTest.EventBreakpoint.async teardown):
(TestPage.registerInitializer.InspectorTest.EventBreakpoint.createBreakpoint):
(TestPage.registerInitializer.InspectorTest.EventBreakpoint.removeBreakpoint):

  • inspector/dom/breakpoint-for-event-listener.html:
  • inspector/dom/breakpoint-for-event-listener-expected.txt:
  • inspector/dom-debugger/event-animation-frame-breakpoints.html:
  • inspector/dom-debugger/event-animation-frame-breakpoints-expected.txt:
  • inspector/dom-debugger/event-interval-breakpoints.html:
  • inspector/dom-debugger/event-interval-breakpoints-expected.txt:
  • inspector/dom-debugger/event-listener-breakpoints.html:
  • inspector/dom-debugger/event-listener-breakpoints-expected.txt:
  • inspector/dom-debugger/event-timeout-breakpoints.html:
  • inspector/dom-debugger/event-timeout-breakpoints-expected.txt:

Add tests for new event breakpoint configuration options.

  • http/tests/inspector/debugger/debugger-test.js:

(TestPage.registerInitializer.InspectorTest.startTracingBreakpoints):

  • http/tests/inspector/resources/probe-test.js:

(TestPage.registerInitializer.ProtocolTest.Probe.installTracingListeners):

  • inspector/debugger/breakpoint-action-eval.html:
  • inspector/debugger/breakpoint-action-log.html:
  • inspector/debugger/breakpoint-columns.html:
  • inspector/debugger/breakpoint-scope.html:
  • inspector/debugger/debugger-stack-overflow.html:
  • inspector/debugger/pause-reason.html:
  • inspector/debugger/probe-manager-add-remove-actions.html:
  • inspector/debugger/stepping/stepping-through-autoContinue-breakpoint.html:
  • inspector/debugger/tail-deleted-frames-this-value.html:
  • inspector/debugger/tail-recursion.html:
  • inspector/worker/debugger-pause.html:
  • inspector/worker/debugger-shared-breakpoint.html:

Update existing breakpoint tests to use new model objects.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/debugger/Debugger.cpp

    r261895 r266074  
    2525#include "CodeBlock.h"
    2626#include "DebuggerCallFrame.h"
     27#include "DebuggerScope.h"
    2728#include "HeapIterationScope.h"
    2829#include "JSCInlines.h"
    2930#include "MarkedSpaceInlines.h"
    3031#include "VMEntryScope.h"
     32#include <wtf/HashMap.h>
     33#include <wtf/HashSet.h>
     34#include <wtf/RefPtr.h>
     35#include <wtf/Vector.h>
     36#include <wtf/text/TextPosition.h>
    3137
    3238namespace JSC {
     
    9096    , m_lastExecutedLine(UINT_MAX)
    9197    , m_lastExecutedSourceID(noSourceID)
    92     , m_topBreakpointID(noBreakpointID)
    9398    , m_pausingBreakpointID(noBreakpointID)
    9499{
     
    206211}
    207212
     213void Debugger::addObserver(Observer& observer)
     214{
     215    bool wasEmpty = m_observers.isEmpty();
     216
     217    m_observers.add(&observer);
     218
     219    if (wasEmpty)
     220        attachDebugger();
     221}
     222
     223void Debugger::removeObserver(Observer& observer, bool isBeingDestroyed)
     224{
     225    m_observers.remove(&observer);
     226
     227    if (m_observers.isEmpty())
     228        detachDebugger(isBeingDestroyed);
     229}
     230
     231bool Debugger::canDispatchFunctionToObservers() const
     232{
     233    return !m_dispatchingFunctionToObservers && !m_observers.isEmpty();
     234}
     235
     236void Debugger::dispatchFunctionToObservers(Function<void(Observer&)> func)
     237{
     238    if (!canDispatchFunctionToObservers())
     239        return;
     240
     241    SetForScope<bool> change(m_dispatchingFunctionToObservers, true);
     242
     243    for (auto* observer : copyToVector(m_observers))
     244        func(*observer);
     245}
     246
    208247void Debugger::setProfilingClient(ProfilingClient* client)
    209248{
     
    212251}
    213252
     253void Debugger::sourceParsed(JSGlobalObject* globalObject, SourceProvider* sourceProvider, int errorLine, const String& errorMessage)
     254{
     255    // Preemptively check whether we can dispatch so that we don't do any unnecessary allocations.
     256    if (!canDispatchFunctionToObservers())
     257        return;
     258
     259    if (errorLine != -1) {
     260        auto sourceURL = sourceProvider->sourceURL();
     261        auto data = sourceProvider->source().toString();
     262        auto firstLine = sourceProvider->startPosition().m_line.oneBasedInt();
     263        dispatchFunctionToObservers([&] (Observer& observer) {
     264            observer.failedToParseSource(sourceURL, data, firstLine, errorLine, errorMessage);
     265        });
     266        return;
     267    }
     268
     269    JSC::SourceID sourceID = sourceProvider->asID();
     270
     271    // FIXME: <https://webkit.org/b/162773> Web Inspector: Simplify Debugger::Script to use SourceProvider
     272    Debugger::Script script;
     273    script.sourceProvider = sourceProvider;
     274    script.url = sourceProvider->sourceURL();
     275    script.source = sourceProvider->source().toString();
     276    script.startLine = sourceProvider->startPosition().m_line.zeroBasedInt();
     277    script.startColumn = sourceProvider->startPosition().m_column.zeroBasedInt();
     278    script.isContentScript = isContentScript(globalObject);
     279    script.sourceURL = sourceProvider->sourceURLDirective();
     280    script.sourceMappingURL = sourceProvider->sourceMappingURLDirective();
     281
     282    int sourceLength = script.source.length();
     283    int lineCount = 1;
     284    int lastLineStart = 0;
     285    for (int i = 0; i < sourceLength; ++i) {
     286        if (script.source[i] == '\n') {
     287            lineCount += 1;
     288            lastLineStart = i + 1;
     289        }
     290    }
     291
     292    script.endLine = script.startLine + lineCount - 1;
     293    if (lineCount == 1)
     294        script.endColumn = script.startColumn + sourceLength;
     295    else
     296        script.endColumn = sourceLength - lastLineStart;
     297
     298    dispatchFunctionToObservers([&] (Observer& observer) {
     299        observer.didParseSource(sourceID, script);
     300    });
     301}
     302
    214303Seconds Debugger::willEvaluateScript()
    215304{
     
    224313void Debugger::toggleBreakpoint(CodeBlock* codeBlock, Breakpoint& breakpoint, BreakpointState enabledOrNot)
    225314{
    226     ASSERT(breakpoint.resolved);
     315    ASSERT(breakpoint.isResolved());
    227316
    228317    ScriptExecutable* executable = codeBlock->ownerExecutable();
    229318
    230319    SourceID sourceID = static_cast<SourceID>(executable->sourceID());
    231     if (breakpoint.sourceID != sourceID)
     320    if (breakpoint.sourceID() != sourceID)
    232321        return;
    233322
     
    239328    // Inspector breakpoint line and column values are zero-based but the executable
    240329    // and CodeBlock line and column values are one-based.
    241     unsigned line = breakpoint.line + 1;
     330    unsigned line = breakpoint.lineNumber() + 1;
    242331    Optional<unsigned> column;
    243     if (breakpoint.column)
    244         column = breakpoint.column + 1;
     332    if (breakpoint.columnNumber())
     333        column = breakpoint.columnNumber() + 1;
    245334
    246335    if (line < startLine || line > endLine)
     
    264353void Debugger::applyBreakpoints(CodeBlock* codeBlock)
    265354{
    266     BreakpointIDToBreakpointMap& breakpoints = m_breakpointIDToBreakpoint;
    267     for (auto* breakpoint : breakpoints.values())
    268         toggleBreakpoint(codeBlock, *breakpoint, BreakpointEnabled);
     355    for (auto& breakpoint : m_breakpoints)
     356        toggleBreakpoint(codeBlock, breakpoint, BreakpointEnabled);
    269357}
    270358
     
    315403}
    316404
    317 void Debugger::resolveBreakpoint(Breakpoint& breakpoint, SourceProvider* sourceProvider)
    318 {
    319     RELEASE_ASSERT(!breakpoint.resolved);
    320     ASSERT(breakpoint.sourceID != noSourceID);
     405bool Debugger::resolveBreakpoint(Breakpoint& breakpoint, SourceProvider* sourceProvider)
     406{
     407    RELEASE_ASSERT(!breakpoint.isResolved());
     408    ASSERT(breakpoint.isLinked());
    321409
    322410    // FIXME: <https://webkit.org/b/162771> Web Inspector: Adopt TextPosition in Inspector to avoid oneBasedInt/zeroBasedInt ambiguity
    323411    // Inspector breakpoint line and column values are zero-based but the executable
    324412    // and CodeBlock line values are one-based while column is zero-based.
    325     int line = breakpoint.line + 1;
    326     int column = breakpoint.column;
     413    int line = breakpoint.lineNumber() + 1;
     414    int column = breakpoint.columnNumber();
    327415
    328416    // Account for a <script>'s start position on the first line only.
    329417    int providerStartLine = sourceProvider->startPosition().m_line.oneBasedInt(); // One based to match the already adjusted line.
    330418    int providerStartColumn = sourceProvider->startPosition().m_column.zeroBasedInt(); // Zero based so column zero is zero.
    331     if (line == providerStartLine && breakpoint.column) {
     419    if (line == providerStartLine && breakpoint.columnNumber()) {
    332420        ASSERT(providerStartColumn <= column);
    333421        if (providerStartColumn)
     
    335423    }
    336424
    337     DebuggerParseData& parseData = debuggerParseData(breakpoint.sourceID, sourceProvider);
     425    DebuggerParseData& parseData = debuggerParseData(breakpoint.sourceID(), sourceProvider);
    338426    Optional<JSTextPosition> resolvedPosition = parseData.pausePositions.breakpointLocationForLineColumn(line, column);
    339427    if (!resolvedPosition)
    340         return;
     428        return false;
    341429
    342430    int resolvedLine = resolvedPosition->line;
     
    344432
    345433    // Re-account for a <script>'s start position on the first line only.
    346     if (resolvedLine == providerStartLine && breakpoint.column) {
     434    if (resolvedLine == providerStartLine && breakpoint.columnNumber()) {
    347435        if (providerStartColumn)
    348436            resolvedColumn += providerStartColumn;
    349437    }
    350438
    351     breakpoint.line = resolvedLine - 1;
    352     breakpoint.column = resolvedColumn;
    353     breakpoint.resolved = true;
    354 }
    355 
    356 BreakpointID Debugger::setBreakpoint(Breakpoint& breakpoint, bool& existing)
    357 {
    358     ASSERT(breakpoint.resolved);
    359     ASSERT(breakpoint.sourceID != noSourceID);
    360 
    361     SourceID sourceID = breakpoint.sourceID;
    362     unsigned line = breakpoint.line;
    363     unsigned column = breakpoint.column;
    364 
    365     SourceIDToBreakpointsMap::iterator it = m_sourceIDToBreakpoints.find(breakpoint.sourceID);
    366     if (it == m_sourceIDToBreakpoints.end())
    367         it = m_sourceIDToBreakpoints.set(sourceID, LineToBreakpointsMap()).iterator;
    368 
    369     LineToBreakpointsMap::iterator breaksIt = it->value.find(line);
    370     if (breaksIt == it->value.end())
    371         breaksIt = it->value.set(line, adoptRef(new BreakpointsList)).iterator;
    372 
    373     BreakpointsList& breakpoints = *breaksIt->value;
    374     for (Breakpoint* current = breakpoints.head(); current; current = current->next()) {
    375         if (current->column == column) {
     439    return breakpoint.resolve(resolvedLine - 1, resolvedColumn);
     440}
     441
     442bool Debugger::setBreakpoint(Breakpoint& breakpoint)
     443{
     444    ASSERT(breakpoint.isResolved());
     445
     446    auto& breakpointsForLine = m_breakpointsForSourceID.ensure(breakpoint.sourceID(), [] {
     447        return LineToBreakpointsMap();
     448    }).iterator->value;
     449
     450    auto& breakpoints = breakpointsForLine.ensure(breakpoint.lineNumber(), [] {
     451        return BreakpointsVector();
     452    }).iterator->value;
     453
     454    for (auto& existingBreakpoint : breakpoints) {
     455        if (existingBreakpoint->columnNumber() == breakpoint.columnNumber()) {
     456            ASSERT(existingBreakpoint->id() != breakpoint.id());
    376457            // Found existing breakpoint. Do not create a duplicate at this location.
    377             existing = true;
    378             return current->id;
     458            return false;
    379459        }
    380460    }
    381461
    382     existing = false;
    383     BreakpointID id = ++m_topBreakpointID;
    384     RELEASE_ASSERT(id != noBreakpointID);
    385 
    386     breakpoint.id = id;
    387 
    388     Breakpoint* newBreakpoint = new Breakpoint(breakpoint);
    389     breakpoints.append(newBreakpoint);
    390     m_breakpointIDToBreakpoint.set(id, newBreakpoint);
    391 
    392     toggleBreakpoint(*newBreakpoint, BreakpointEnabled);
    393 
    394     return id;
    395 }
    396 
    397 void Debugger::removeBreakpoint(BreakpointID id)
    398 {
    399     ASSERT(id != noBreakpointID);
    400 
    401     BreakpointIDToBreakpointMap::iterator idIt = m_breakpointIDToBreakpoint.find(id);
    402     ASSERT(idIt != m_breakpointIDToBreakpoint.end());
    403     Breakpoint* breakpoint = idIt->value;
    404 
    405     SourceID sourceID = breakpoint->sourceID;
    406     ASSERT(sourceID);
    407     SourceIDToBreakpointsMap::iterator it = m_sourceIDToBreakpoints.find(sourceID);
    408     ASSERT(it != m_sourceIDToBreakpoints.end());
    409     LineToBreakpointsMap::iterator breaksIt = it->value.find(breakpoint->line);
    410     ASSERT(breaksIt != it->value.end());
    411 
    412     toggleBreakpoint(*breakpoint, BreakpointDisabled);
    413 
    414     BreakpointsList& breakpoints = *breaksIt->value;
     462    breakpoints.append(makeRef(breakpoint));
     463
     464    m_breakpoints.add(makeRef(breakpoint));
     465
     466    toggleBreakpoint(breakpoint, BreakpointEnabled);
     467
     468    return true;
     469}
     470
     471bool Debugger::removeBreakpoint(Breakpoint& breakpoint)
     472{
     473    ASSERT(breakpoint.isResolved());
     474
     475    auto breakpointsForLineIterator = m_breakpointsForSourceID.find(breakpoint.sourceID());
     476    if (breakpointsForLineIterator == m_breakpointsForSourceID.end())
     477        return false;
     478
     479    auto breakpointsIterator = breakpointsForLineIterator->value.find(breakpoint.lineNumber());
     480    if (breakpointsIterator == breakpointsForLineIterator->value.end())
     481        return false;
     482
     483    toggleBreakpoint(breakpoint, BreakpointDisabled);
     484
     485    auto& breakpoints = breakpointsIterator->value;
     486
    415487#if ASSERT_ENABLED
    416488    bool found = false;
    417     for (Breakpoint* current = breakpoints.head(); current && !found; current = current->next()) {
    418         if (current->id == breakpoint->id)
     489    for (auto& existingBreakpoint : breakpoints) {
     490        if (existingBreakpoint->columnNumber() == breakpoint.columnNumber()) {
     491            ASSERT(existingBreakpoint->id() == breakpoint.id());
     492            ASSERT(!found);
    419493            found = true;
    420     }
    421     ASSERT(found);
     494        }
     495    }
    422496#endif // ASSERT_ENABLED
    423497
    424     m_breakpointIDToBreakpoint.remove(idIt);
    425     breakpoints.remove(breakpoint);
    426     delete breakpoint;
     498    auto removed = m_breakpoints.remove(breakpoint);
     499    removed |= !breakpoints.removeAllMatching([&] (const Ref<Breakpoint>& existingBreakpoint) {
     500        return &breakpoint == existingBreakpoint.ptr();
     501    });
    427502
    428503    if (breakpoints.isEmpty()) {
    429         it->value.remove(breaksIt);
    430         if (it->value.isEmpty())
    431             m_sourceIDToBreakpoints.remove(it);
    432     }
    433 }
    434 
    435 bool Debugger::hasBreakpoint(SourceID sourceID, const TextPosition& position, Breakpoint *hitBreakpoint)
     504        breakpointsForLineIterator->value.remove(breakpointsIterator);
     505        if (breakpointsForLineIterator->value.isEmpty())
     506            m_breakpointsForSourceID.remove(breakpointsForLineIterator);
     507    }
     508
     509    return removed;
     510}
     511
     512RefPtr<Breakpoint> Debugger::didHitBreakpoint(JSGlobalObject* globalObject, SourceID sourceID, const TextPosition& position)
    436513{
    437514    if (!m_breakpointsActivated)
    438         return false;
    439 
    440     SourceIDToBreakpointsMap::const_iterator it = m_sourceIDToBreakpoints.find(sourceID);
    441     if (it == m_sourceIDToBreakpoints.end())
    442         return false;
     515        return nullptr;
     516
     517    auto breakpointsForLineIterator = m_breakpointsForSourceID.find(sourceID);
     518    if (breakpointsForLineIterator == m_breakpointsForSourceID.end())
     519        return nullptr;
    443520
    444521    unsigned line = position.m_line.zeroBasedInt();
    445522    unsigned column = position.m_column.zeroBasedInt();
    446523   
    447     LineToBreakpointsMap::const_iterator breaksIt = it->value.find(line);
    448     if (breaksIt == it->value.end())
    449         return false;
    450 
    451     bool hit = false;
    452     const BreakpointsList& breakpoints = *breaksIt->value;
    453     Breakpoint* breakpoint;
    454     for (breakpoint = breakpoints.head(); breakpoint; breakpoint = breakpoint->next()) {
    455         unsigned breakLine = breakpoint->line;
    456         unsigned breakColumn = breakpoint->column;
     524    auto breakpointsIterator = breakpointsForLineIterator->value.find(line);
     525    if (breakpointsIterator == breakpointsForLineIterator->value.end())
     526        return nullptr;
     527
     528    for (auto& breakpoint : breakpointsIterator->value) {
     529        unsigned breakLine = breakpoint->lineNumber();
     530        unsigned breakColumn = breakpoint->columnNumber();
     531
    457532        // Since frontend truncates the indent, the first statement in a line must match the breakpoint (line,0).
    458533        ASSERT(this == m_currentCallFrame->codeBlock()->globalObject()->debugger());
    459         if ((line != m_lastExecutedLine && line == breakLine && !breakColumn)
    460             || (line == breakLine && column == breakColumn)) {
    461             hit = true;
     534        if ((line != m_lastExecutedLine && line == breakLine && !breakColumn) || (line == breakLine && column == breakColumn)) {
     535            if (breakpoint->shouldPause(*this, globalObject))
     536                return breakpoint.copyRef();
    462537            break;
    463538        }
    464539    }
    465     if (!hit)
    466         return false;
    467 
    468     if (hitBreakpoint)
    469         *hitBreakpoint = *breakpoint;
    470 
    471     breakpoint->hitCount++;
    472     if (breakpoint->ignoreCount >= breakpoint->hitCount)
    473         return false;
    474 
    475     if (breakpoint->condition.isEmpty())
    476         return true;
    477 
    478     // We cannot stop in the debugger while executing condition code,
    479     // so make it looks like the debugger is already paused.
    480     TemporaryPausedState pausedState(*this);
    481 
    482     NakedPtr<Exception> exception;
    483     DebuggerCallFrame& debuggerCallFrame = currentDebuggerCallFrame();
    484     JSObject* scopeExtensionObject = nullptr;
    485     JSValue result = debuggerCallFrame.evaluateWithScopeExtension(breakpoint->condition, scopeExtensionObject, exception);
    486 
    487     // We can lose the debugger while executing JavaScript.
    488     if (!m_currentCallFrame)
    489         return false;
    490 
    491     JSGlobalObject* globalObject = m_currentCallFrame->lexicalGlobalObject(m_vm);
    492     if (exception) {
    493         // An erroneous condition counts as "false".
    494         handleExceptionInBreakpointCondition(globalObject, exception);
    495         return false;
    496     }
    497 
    498     return result.toBoolean(globalObject);
     540
     541    return nullptr;
    499542}
    500543
     
    520563    m_vm.heap.completeAllJITPlans();
    521564
    522     m_topBreakpointID = noBreakpointID;
    523     m_breakpointIDToBreakpoint.clear();
    524     m_sourceIDToBreakpoints.clear();
     565    m_breakpointsForSourceID.clear();
     566    m_breakpoints.clear();
     567    m_specialBreakpoint = nullptr;
    525568
    526569    ClearCodeBlockDebuggerRequestsFunctor functor(this);
    527570    m_vm.heap.forEachCodeBlock(functor);
     571}
     572
     573bool Debugger::evaluateBreakpointCondition(Breakpoint& breakpoint, JSGlobalObject* globalObject)
     574{
     575    const String& condition = breakpoint.condition();
     576    if (condition.isEmpty())
     577        return true;
     578
     579    // We cannot stop in the debugger while executing condition code,
     580    // so make it looks like the debugger is already paused.
     581    TemporaryPausedState pausedState(*this);
     582
     583    NakedPtr<Exception> exception;
     584    DebuggerCallFrame& debuggerCallFrame = currentDebuggerCallFrame();
     585    JSObject* scopeExtensionObject = nullptr;
     586    JSValue result = debuggerCallFrame.evaluateWithScopeExtension(condition, scopeExtensionObject, exception);
     587
     588    // We can lose the debugger while executing JavaScript.
     589    if (!m_currentCallFrame)
     590        return false;
     591
     592    if (exception) {
     593        reportException(globalObject, exception);
     594        return false;
     595    }
     596
     597    return result.toBoolean(globalObject);
     598}
     599
     600void Debugger::evaluateBreakpointActions(Breakpoint& breakpoint, JSGlobalObject* globalObject)
     601{
     602    ASSERT(m_isPaused);
     603    ASSERT(isAttached(globalObject));
     604
     605    m_currentProbeBatchId++;
     606
     607    for (auto& action : breakpoint.actions()) {
     608        auto& debuggerCallFrame = currentDebuggerCallFrame();
     609
     610        switch (action.type) {
     611        case Breakpoint::Action::Type::Log:
     612            dispatchFunctionToObservers([&] (Observer& observer) {
     613                observer.breakpointActionLog(debuggerCallFrame.globalObject(), action.data);
     614            });
     615            break;
     616
     617        case Breakpoint::Action::Type::Evaluate: {
     618            NakedPtr<Exception> exception;
     619            JSObject* scopeExtensionObject = nullptr;
     620            debuggerCallFrame.evaluateWithScopeExtension(action.data, scopeExtensionObject, exception);
     621            if (exception)
     622                reportException(debuggerCallFrame.globalObject(), exception);
     623            break;
     624        }
     625
     626        case Breakpoint::Action::Type::Sound:
     627            dispatchFunctionToObservers([&] (Observer& observer) {
     628                observer.breakpointActionSound(action.id);
     629            });
     630            break;
     631
     632        case Breakpoint::Action::Type::Probe: {
     633            NakedPtr<Exception> exception;
     634            JSObject* scopeExtensionObject = nullptr;
     635            JSValue result = debuggerCallFrame.evaluateWithScopeExtension(action.data, scopeExtensionObject, exception);
     636            JSC::JSGlobalObject* debuggerGlobalObject = debuggerCallFrame.globalObject();
     637            if (exception)
     638                reportException(debuggerGlobalObject, exception);
     639
     640            dispatchFunctionToObservers([&] (Observer& observer) {
     641                observer.breakpointActionProbe(debuggerGlobalObject, action.id, m_currentProbeBatchId, m_nextProbeSampleId++, exception ? exception->value() : result);
     642            });
     643            break;
     644        }
     645        }
     646
     647        if (!isAttached(globalObject))
     648            return;
     649    }
    528650}
    529651
     
    572694}
    573695
    574 void Debugger::setPauseOnNextStatement(bool pause)
    575 {
    576     m_pauseAtNextOpportunity = pause;
    577     if (pause)
    578         setSteppingMode(SteppingModeEnabled);
     696void Debugger::schedulePauseAtNextOpportunity()
     697{
     698    m_pauseAtNextOpportunity = true;
     699
     700    setSteppingMode(SteppingModeEnabled);
     701}
     702
     703void Debugger::cancelPauseAtNextOpportunity()
     704{
     705    m_pauseAtNextOpportunity = false;
     706}
     707
     708bool Debugger::schedulePauseForSpecialBreakpoint(Breakpoint& breakpoint)
     709{
     710    if (m_specialBreakpoint)
     711        return false;
     712
     713    m_specialBreakpoint = makeRef(breakpoint);
     714    setSteppingMode(SteppingModeEnabled);
     715    return true;
     716}
     717
     718bool Debugger::cancelPauseForSpecialBreakpoint(Breakpoint& breakpoint)
     719{
     720    if (&breakpoint != m_specialBreakpoint)
     721        return false;
     722
     723    m_specialBreakpoint = nullptr;
     724    return true;
    579725}
    580726
     
    600746        return;
    601747
    602     notifyDoneProcessingDebuggerEvents();
     748    m_doneProcessingDebuggerEvents = true;
    603749}
    604750
     
    611757    m_pauseOnStepNext = true;
    612758    setSteppingMode(SteppingModeEnabled);
    613     notifyDoneProcessingDebuggerEvents();
     759    m_doneProcessingDebuggerEvents = true;
    614760}
    615761
     
    621767    m_pauseAtNextOpportunity = true;
    622768    setSteppingMode(SteppingModeEnabled);
    623     notifyDoneProcessingDebuggerEvents();
     769    m_doneProcessingDebuggerEvents = true;
    624770}
    625771
     
    631777    m_pauseOnCallFrame = m_currentCallFrame;
    632778    setSteppingMode(SteppingModeEnabled);
    633     notifyDoneProcessingDebuggerEvents();
     779    m_doneProcessingDebuggerEvents = true;
    634780}
    635781
     
    643789    m_pauseOnStepOut = true;
    644790    setSteppingMode(SteppingModeEnabled);
    645     notifyDoneProcessingDebuggerEvents();
     791    m_doneProcessingDebuggerEvents = true;
    646792}
    647793
     
    702848    bool didPauseForStep = pauseNow;
    703849
    704     Breakpoint breakpoint;
    705850    TextPosition position = DebuggerCallFrame::positionForCallFrame(vm, m_currentCallFrame);
    706     bool didHitBreakpoint = hasBreakpoint(sourceID, position, &breakpoint);
    707     pauseNow |= didHitBreakpoint;
     851
     852    auto breakpoint = didHitBreakpoint(globalObject, sourceID, position);
     853    if (breakpoint)
     854        pauseNow = true;
     855
     856    // Special breakpoints are only given one opportunity to pause.
     857    auto specialBreakpoint = WTFMove(m_specialBreakpoint);
     858    if (specialBreakpoint && specialBreakpoint->shouldPause(*this, globalObject))
     859        pauseNow = true;
     860
    708861    m_lastExecutedLine = position.m_line.zeroBasedInt();
    709862    if (!pauseNow)
     
    717870    TemporaryPausedState pausedState(*this);
    718871
    719     if (didHitBreakpoint) {
    720         handleBreakpointHit(globalObject, breakpoint);
     872    if (breakpoint || specialBreakpoint) {
    721873        // Note that the actions can potentially stop the debugger, so we need to check that
    722874        // we still have a current call frame when we get back.
    723         if (!m_currentCallFrame)
    724             return;
    725 
    726         if (breakpoint.autoContinue) {
     875
     876        bool autoContinue = false;
     877
     878        if (breakpoint) {
     879            evaluateBreakpointActions(*breakpoint, globalObject);
     880
     881            if (!m_currentCallFrame)
     882                return;
     883
     884            if (breakpoint->isAutoContinue())
     885                autoContinue = true;
     886        }
     887
     888        if (specialBreakpoint) {
     889            evaluateBreakpointActions(*specialBreakpoint, globalObject);
     890
     891            if (!m_currentCallFrame)
     892                return;
     893
     894            if (specialBreakpoint->isAutoContinue())
     895                autoContinue = true;
     896        }
     897
     898        if (autoContinue) {
    727899            if (!didPauseForStep)
    728900                return;
    729             didHitBreakpoint = false;
    730         } else
    731             m_pausingBreakpointID = breakpoint.id;
     901
     902            breakpoint = nullptr;
     903            specialBreakpoint = nullptr;
     904        } else if (breakpoint)
     905            m_pausingBreakpointID = breakpoint->id();
    732906    }
    733907
    734908    if (blackboxTypeIterator != m_blackboxedScripts.end() && blackboxTypeIterator->value == BlackboxType::Deferred) {
    735909        m_afterBlackboxedScript = true;
    736         setPauseOnNextStatement(true);
     910        schedulePauseAtNextOpportunity();
    737911        return;
    738912    }
     
    742916        if (afterBlackboxedScript)
    743917            reason = PausedAfterBlackboxedScript;
    744         else if (didHitBreakpoint)
     918        else if (breakpoint)
    745919            reason = PausedForBreakpoint;
    746920        PauseReasonDeclaration rauseReasonDeclaration(*this, reason);
     
    752926    m_pausingBreakpointID = noBreakpointID;
    753927
    754     if (!m_pauseAtNextOpportunity && !m_pauseOnCallFrame) {
     928    if (!m_pauseAtNextOpportunity && !m_pauseOnCallFrame && !m_specialBreakpoint) {
    755929        setSteppingMode(SteppingModeDisabled);
    756930        m_currentCallFrame = nullptr;
    757931    }
     932}
     933
     934void Debugger::handlePause(JSGlobalObject* globalObject, ReasonForPause)
     935{
     936    dispatchFunctionToObservers([&] (Observer& observer) {
     937        ASSERT(isPaused());
     938        observer.didPause(globalObject, currentDebuggerCallFrame(), exceptionOrCaughtValue(globalObject));
     939    });
     940
     941    didPause(globalObject);
     942
     943    m_doneProcessingDebuggerEvents = false;
     944    runEventLoopWhilePaused();
     945
     946    didContinue(globalObject);
     947
     948    dispatchFunctionToObservers([&] (Observer& observer) {
     949        observer.didContinue();
     950    });
     951}
     952
     953JSC::JSValue Debugger::exceptionOrCaughtValue(JSC::JSGlobalObject* globalObject)
     954{
     955    if (reasonForPause() == PausedForException)
     956        return currentException();
     957
     958    for (RefPtr<DebuggerCallFrame> frame = &currentDebuggerCallFrame(); frame; frame = frame->callerFrame()) {
     959        DebuggerScope& scope = *frame->scope();
     960        if (scope.isCatchScope())
     961            return scope.caughtValue(globalObject);
     962    }
     963
     964    return { };
    758965}
    759966
     
    8081015    }
    8091016
    810     // Only pause at the next expression with step-in, step-next, and step-out.
    811     bool shouldAttemptPause = m_pauseAtNextOpportunity || m_pauseOnStepNext || m_pauseOnStepOut;
     1017    // Only pause at the next expression for step-in, step-next, step-out, or special breakpoints.
     1018    bool shouldAttemptPause = m_pauseAtNextOpportunity || m_pauseOnStepNext || m_pauseOnStepOut || m_specialBreakpoint;
    8121019
    8131020    PauseReasonDeclaration reason(*this, PausedAtExpression);
     
    9181125    m_pauseOnStepOut = false;
    9191126    m_afterBlackboxedScript = false;
     1127    m_specialBreakpoint = nullptr;
    9201128}
    9211129
     
    9341142}
    9351143
     1144void Debugger::willRunMicrotask()
     1145{
     1146    dispatchFunctionToObservers([&] (Observer& observer) {
     1147        observer.willRunMicrotask();
     1148    });
     1149}
     1150
     1151void Debugger::didRunMicrotask()
     1152{
     1153    dispatchFunctionToObservers([&] (Observer& observer) {
     1154        observer.didRunMicrotask();
     1155    });
     1156}
     1157
    9361158DebuggerCallFrame& Debugger::currentDebuggerCallFrame()
    9371159{
Note: See TracChangeset for help on using the changeset viewer.