WebKit Bugzilla
Attachment 347172 Details for
Bug 183118
: Web Inspector: support breakpoints for arbitrary event names
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch
bug-183118-20180815095353.patch (text/plain), 62.69 KB, created by
Devin Rousso
on 2018-08-15 09:53:56 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Devin Rousso
Created:
2018-08-15 09:53:56 PDT
Size:
62.69 KB
patch
obsolete
>diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index b33909817c0fae14e6617f52be30c3e1a20a13cb..c607e191ed8eac6c9f0ef095fa11d2e170133f7c 100644 >--- a/Source/WebCore/ChangeLog >+++ b/Source/WebCore/ChangeLog >@@ -1,3 +1,15 @@ >+2018-08-15 Devin Rousso <drousso@apple.com> >+ >+ Web Inspector: support breakpoints for arbitrary event names >+ https://bugs.webkit.org/show_bug.cgi?id=183118 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Test: inspector/dom-debugger/event-listener-breakpoints.html >+ >+ * inspector/agents/InspectorDOMDebuggerAgent.cpp: >+ (WebCore::InspectorDOMDebuggerAgent::pauseOnNativeEventIfNeeded): >+ > 2018-08-14 Fujii Hironori <Hironori.Fujii@sony.com> > > Unreviewed, rolling out r234874 and r234876. >diff --git a/Source/WebInspectorUI/ChangeLog b/Source/WebInspectorUI/ChangeLog >index b1e1d78832becb6259b00ca6480a6bacfc68d5a9..ef77d809d6946327ff6e2e5108fe0cf091ebbcf9 100644 >--- a/Source/WebInspectorUI/ChangeLog >+++ b/Source/WebInspectorUI/ChangeLog >@@ -1,3 +1,97 @@ >+2018-08-15 Devin Rousso <drousso@apple.com> >+ >+ Web Inspector: support breakpoints for arbitrary event names >+ https://bugs.webkit.org/show_bug.cgi?id=183118 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Create UI for setting breakpoints on event names. Ties into existing DOMDebugger commands, >+ specifically `setEventListenerBreakpoint` and `removeEventListenerBreakpoint`, that will >+ pause execution if any DOM event is fired that matches any previously registered breakpoints. >+ >+ Event listener breakpoints are distinguished by name, and they currently apply globally, >+ meaning that only one breakpoint per event name can be registered. >+ >+ Event listener breakpoints are created in the Debugger tab in a new "Event Listener Breakpoints" >+ section in the Navigation sidebar. A new type of popover, EventListenerBreakpointPopover, is >+ used, but right now all it contains is a basic text input for the event name. Similarly, a >+ new TreeElement subclass, EventListenerBreakpointTreeElement, is used when showing the list >+ of event listener breakpoints, but all it shows now is the event name. >+ >+ The majority of the logic in this patch was derived from XHR breakpoints. >+ >+ * Localizations/en.lproj/localizedStrings.js: >+ * UserInterface/Main.html: >+ * UserInterface/Test.html: >+ * UserInterface/Images/EventBreakpoint.svg: Added. >+ >+ * UserInterface/Models/EventListenerBreakpoint.js: Added. >+ (WI.EventListenerBreakpoint): >+ (WI.EventListenerBreakpoint.prototype.get eventName): >+ (WI.EventListenerBreakpoint.prototype.get disabled): >+ (WI.EventListenerBreakpoint.prototype.set disabled): >+ (WI.EventListenerBreakpoint.prototype.get serializableInfo): >+ (WI.EventListenerBreakpoint.prototype.saveIdentityToCookie): >+ >+ * UserInterface/Controllers/DOMDebuggerManager.js: >+ (WI.DOMDebuggerManager): >+ (WI.DOMDebuggerManager.prototype.get eventListenerBreakpoints): >+ (WI.DOMDebuggerManager.prototype.eventListenerBreakpointForEventName): >+ (WI.DOMDebuggerManager.prototype.addEventListenerBreakpoint): >+ (WI.DOMDebuggerManager.prototype.removeEventListenerBreakpoint): >+ (WI.DOMDebuggerManager.prototype._speculativelyResolveBreakpoints): >+ (WI.DOMDebuggerManager.prototype._updateEventListenerBreakpoint.breakpointUpdated): >+ (WI.DOMDebuggerManager.prototype._updateEventListenerBreakpoint): >+ (WI.DOMDebuggerManager.prototype._resolveEventListenerBreakpoint): >+ (WI.DOMDebuggerManager.prototype._saveEventListenerBreakpoints): >+ (WI.DOMDebuggerManager.prototype._eventListenerBreakpointDisabledStateDidChange): >+ >+ * UserInterface/Controllers/DebuggerManager.js: >+ (WI.DebuggerManager.prototype._pauseReasonFromPayload): >+ >+ * UserInterface/Controllers/EventListenerBreakpointTreeController.js: Added. >+ (WI.EventListenerBreakpointTreeController): >+ (WI.EventListenerBreakpointTreeController.prototype.revealAndSelect): >+ (WI.EventListenerBreakpointTreeController.prototype._eventListenerBreakpointAdded): >+ (WI.EventListenerBreakpointTreeController.prototype._eventListenerBreakpointRemoved): >+ (WI.EventListenerBreakpointTreeController.prototype._addTreeElement): >+ >+ * UserInterface/Views/DebuggerSidebarPanel.js: >+ (WI.DebuggerSidebarPanel): >+ (WI.DebuggerSidebarPanel.prototype._updatePauseReasonSection): >+ (WI.DebuggerSidebarPanel.prototype._eventListenerBreakpointAddedOrRemoved): >+ (WI.DebuggerSidebarPanel.prototype._addEventListenerBreakpointButtonClicked): >+ (WI.DebuggerSidebarPanel.prototype.willDismissPopover): >+ >+ * UserInterface/Views/EventListenerBreakpointTreeElement.js: Added. >+ (WI.EventListenerBreakpointTreeElement): >+ (WI.EventListenerBreakpointTreeElement.prototype.onattach): >+ (WI.EventListenerBreakpointTreeElement.prototype.ondetach): >+ (WI.EventListenerBreakpointTreeElement.prototype.ondelete): >+ (WI.EventListenerBreakpointTreeElement.prototype.onenter): >+ (WI.EventListenerBreakpointTreeElement.prototype.onspace): >+ (WI.EventListenerBreakpointTreeElement.prototype.populateContextMenu): >+ (WI.EventListenerBreakpointTreeElement.prototype._statusImageElementClicked): >+ (WI.EventListenerBreakpointTreeElement.prototype._statusImageElementFocused): >+ (WI.EventListenerBreakpointTreeElement.prototype._statusImageElementMouseDown): >+ (WI.EventListenerBreakpointTreeElement.prototype._toggleBreakpoint): >+ (WI.EventListenerBreakpointTreeElement.prototype._updateStatus): >+ * UserInterface/Views/EventListenerBreakpointTreeElement.css: Added. >+ (.breakpoint.event-listener:not(.breakpoint-paused-icon) .icon): >+ >+ * UserInterface/Views/EventListenerBreakpointPopover.js: Added. >+ (WI.EventListenerBreakpointPopover): >+ (WI.EventListenerBreakpointPopover.prototype.get result): >+ (WI.EventListenerBreakpointPopover.prototype.get value): >+ (WI.EventListenerBreakpointPopover.prototype.show): >+ (WI.EventListenerBreakpointPopover.prototype._presentOverTargetElement): >+ * UserInterface/Views/EventListenerBreakpointPopover.css: Added. >+ (.popover .event-listener-breakpoint-content): >+ (.popover .event-listener-breakpoint-content > input): >+ >+ * UserInterface/Views/NavigationSidebarPanel.js: >+ (WI.NavigationSidebarPanel.prototype._isTreeElementWithoutRepresentedObject): >+ > 2018-08-13 Matt Baker <mattbaker@apple.com> > > Web Inspector: Table should handle row selection instead of the table delegate >diff --git a/Source/WebCore/inspector/agents/InspectorDOMDebuggerAgent.cpp b/Source/WebCore/inspector/agents/InspectorDOMDebuggerAgent.cpp >index fad32f8ce511a801319525a1d1e7296d44d0d9e6..36f8aecc43ecfbb4e99a3545d1296bb8bd039c96 100644 >--- a/Source/WebCore/inspector/agents/InspectorDOMDebuggerAgent.cpp >+++ b/Source/WebCore/inspector/agents/InspectorDOMDebuggerAgent.cpp >@@ -371,7 +371,7 @@ void InspectorDOMDebuggerAgent::pauseOnNativeEventIfNeeded(bool isDOMEvent, cons > return; > > Ref<JSON::Object> eventData = JSON::Object::create(); >- eventData->setString("eventName"_s, fullEventName); >+ eventData->setString("eventName"_s, eventName); > > if (synchronous) > m_debuggerAgent->breakProgram(Inspector::DebuggerFrontendDispatcher::Reason::EventListener, WTFMove(eventData)); >diff --git a/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js b/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js >index 77e742002f491ccb5c0cf05d44f6abc210820e5a..ed8cc4cd8e03d58d7728c5f36c1778cdd14bea8a 100644 >--- a/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js >+++ b/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js >@@ -72,6 +72,7 @@ localizedStrings["Add"] = "Add"; > localizedStrings["Add %s Rule"] = "Add %s Rule"; > localizedStrings["Add Action"] = "Add Action"; > localizedStrings["Add Breakpoint"] = "Add Breakpoint"; >+localizedStrings["Add Event Listener Breakpoint"] = "Add Event Listener Breakpoint"; > localizedStrings["Add New"] = "Add New"; > localizedStrings["Add New Class"] = "Add New Class"; > localizedStrings["Add New Probe Expression"] = "Add New Probe Expression"; >@@ -144,6 +145,7 @@ localizedStrings["Bottom"] = "Bottom"; > localizedStrings["Boundary"] = "Boundary"; > localizedStrings["Box Model"] = "Box Model"; > localizedStrings["Box Shadow"] = "Box Shadow"; >+localizedStrings["Break on events with name:"] = "Break on events with name:"; > localizedStrings["Break on request with URL:"] = "Break on request with URL:"; > localizedStrings["Break onâ¦"] = "Break onâ¦"; > localizedStrings["Breakdown"] = "Breakdown"; >@@ -396,6 +398,7 @@ localizedStrings["Eval Code"] = "Eval Code"; > localizedStrings["Evaluate JavaScript"] = "Evaluate JavaScript"; > localizedStrings["Event"] = "Event"; > localizedStrings["Event Dispatched"] = "Event Dispatched"; >+localizedStrings["Event Listener Breakpoints"] = "Event Listener Breakpoints"; > localizedStrings["Event Listeners"] = "Event Listeners"; > localizedStrings["Events"] = "Events"; > localizedStrings["Exception with thrown value: %s"] = "Exception with thrown value: %s"; >@@ -1093,6 +1096,7 @@ localizedStrings["times before stopping"] = "times before stopping"; > localizedStrings["toggle"] = "toggle"; > localizedStrings["unsupported version."] = "unsupported version."; > localizedStrings["value"] = "value"; >+localizedStrings["â%sâ Event Fired"] = "â%sâ Event Fired"; > localizedStrings["â%sâ Profile Recorded"] = "â%sâ Profile Recorded"; > localizedStrings["â%sâ is invalid."] = "â%sâ is invalid."; > localizedStrings["â%sâ threw an error."] = "â%sâ threw an error."; >diff --git a/Source/WebInspectorUI/UserInterface/Controllers/DOMDebuggerManager.js b/Source/WebInspectorUI/UserInterface/Controllers/DOMDebuggerManager.js >index 000a3dd9a92c0117bb590823d687ead6b438a053..6ca3413e74242af257120d2cdcb05ea6283ec880 100644 >--- a/Source/WebInspectorUI/UserInterface/Controllers/DOMDebuggerManager.js >+++ b/Source/WebInspectorUI/UserInterface/Controllers/DOMDebuggerManager.js >@@ -33,6 +33,9 @@ WI.DOMDebuggerManager = class DOMDebuggerManager extends WI.Object > this._domBreakpointURLMap = new Map; > this._domBreakpointFrameIdentifierMap = new Map; > >+ this._eventListenerBreakpointSetting = new WI.Setting("event-listener-breakpoint", []); >+ this._eventListenerBreakpoints = []; >+ > this._xhrBreakpointsSetting = new WI.Setting("xhr-breakpoints", []); > this._xhrBreakpoints = []; > this._allRequestsBreakpointEnabledSetting = new WI.Setting("break-on-all-requests", false); >@@ -40,6 +43,7 @@ WI.DOMDebuggerManager = class DOMDebuggerManager extends WI.Object > this._allRequestsBreakpoint = new WI.XHRBreakpoint(null, null, !this._allRequestsBreakpointEnabledSetting.value); > > WI.DOMBreakpoint.addEventListener(WI.DOMBreakpoint.Event.DisabledStateDidChange, this._domBreakpointDisabledStateDidChange, this); >+ WI.EventListenerBreakpoint.addEventListener(WI.EventListenerBreakpoint.Event.DisabledStateDidChange, this._eventListenerBreakpointDisabledStateDidChange, this); > WI.XHRBreakpoint.addEventListener(WI.XHRBreakpoint.Event.DisabledStateDidChange, this._xhrBreakpointDisabledStateDidChange, this); > > WI.domTreeManager.addEventListener(WI.DOMTreeManager.Event.NodeRemoved, this._nodeRemoved, this); >@@ -58,6 +62,11 @@ WI.DOMDebuggerManager = class DOMDebuggerManager extends WI.Object > this.addDOMBreakpoint(breakpoint); > } > >+ for (let cookie of this._eventListenerBreakpointSetting.value) { >+ let breakpoint = new WI.EventListenerBreakpoint(cookie.eventName, cookie.disabled); >+ this.addEventListenerBreakpoint(breakpoint); >+ } >+ > for (let cookie of this._xhrBreakpointsSetting.value) { > let breakpoint = new WI.XHRBreakpoint(cookie.type, cookie.url, cookie.disabled); > this.addXHRBreakpoint(breakpoint); >@@ -102,6 +111,8 @@ WI.DOMDebuggerManager = class DOMDebuggerManager extends WI.Object > return resolvedBreakpoints; > } > >+ get eventListenerBreakpoints() { return this._eventListenerBreakpoints; } >+ > get xhrBreakpoints() { return this._xhrBreakpoints; } > > isBreakpointRemovable(breakpoint) >@@ -184,6 +195,51 @@ WI.DOMDebuggerManager = class DOMDebuggerManager extends WI.Object > this._saveDOMBreakpoints(); > } > >+ eventListenerBreakpointForEventName(eventName) >+ { >+ return this._eventListenerBreakpoints.find((breakpoint) => breakpoint.eventName === eventName) || null; >+ } >+ >+ addEventListenerBreakpoint(breakpoint) >+ { >+ console.assert(breakpoint instanceof WI.EventListenerBreakpoint); >+ if (!breakpoint) >+ return; >+ >+ if (this._eventListenerBreakpoints.some((item) => item.eventName === breakpoint.eventName)) >+ return; >+ >+ this._eventListenerBreakpoints.push(breakpoint); >+ >+ this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.EventListenerBreakpointAdded, {breakpoint}); >+ >+ this._resolveEventListenerBreakpoint(breakpoint); >+ this._saveEventListenerBreakpoints(); >+ } >+ >+ removeEventListenerBreakpoint(breakpoint) >+ { >+ console.assert(breakpoint instanceof WI.EventListenerBreakpoint); >+ if (!breakpoint) >+ return; >+ >+ if (!this._eventListenerBreakpoints.includes(breakpoint)) >+ return; >+ >+ this._eventListenerBreakpoints.remove(breakpoint); >+ >+ this._saveEventListenerBreakpoints(); >+ this.dispatchEventToListeners(WI.DOMDebuggerManager.Event.EventListenerBreakpointRemoved, {breakpoint}); >+ >+ if (breakpoint.disabled) >+ return; >+ >+ DOMDebuggerAgent.removeEventListenerBreakpoint(breakpoint.eventName, (error) => { >+ if (error) >+ console.error(error); >+ }); >+ } >+ > xhrBreakpointForURL(url) > { > return this._xhrBreakpoints.find((breakpoint) => breakpoint.url === url) || null; >@@ -300,6 +356,9 @@ WI.DOMDebuggerManager = class DOMDebuggerManager extends WI.Object > } > } > >+ for (let breakpoint of this._eventListenerBreakpoints) >+ this._resolveEventListenerBreakpoint(breakpoint); >+ > for (let breakpoint of this._xhrBreakpoints) > this._resolveXHRBreakpoint(breakpoint); > } >@@ -347,6 +406,23 @@ WI.DOMDebuggerManager = class DOMDebuggerManager extends WI.Object > DOMDebuggerAgent.setDOMBreakpoint(nodeIdentifier, breakpoint.type, breakpointUpdated); > } > >+ _updateEventListenerBreakpoint(breakpoint, callback) >+ { >+ function breakpointUpdated(error) >+ { >+ if (error) >+ console.error(error); >+ >+ if (callback) >+ callback(error); >+ } >+ >+ if (breakpoint.disabled) >+ DOMDebuggerAgent.removeEventListenerBreakpoint(breakpoint.eventName, breakpointUpdated); >+ else >+ DOMDebuggerAgent.setEventListenerBreakpoint(breakpoint.eventName, breakpointUpdated); >+ } >+ > _updateXHRBreakpoint(breakpoint, callback) > { > function breakpointUpdated(error) >@@ -366,6 +442,16 @@ WI.DOMDebuggerManager = class DOMDebuggerManager extends WI.Object > } > } > >+ _resolveEventListenerBreakpoint(breakpoint) >+ { >+ if (breakpoint.disabled) >+ return; >+ >+ this._updateEventListenerBreakpoint(breakpoint, () => { >+ breakpoint.dispatchEventToListeners(WI.EventListenerBreakpoint.Event.ResolvedStateDidChange); >+ }); >+ } >+ > _resolveXHRBreakpoint(breakpoint) > { > if (breakpoint.disabled) >@@ -388,6 +474,14 @@ WI.DOMDebuggerManager = class DOMDebuggerManager extends WI.Object > this._domBreakpointsSetting.value = breakpointsToSave.map((breakpoint) => breakpoint.serializableInfo); > } > >+ _saveEventListenerBreakpoints() >+ { >+ if (this._restoringBreakpoints) >+ return; >+ >+ this._eventListenerBreakpointSetting.value = this._eventListenerBreakpoints.map((breakpoint) => breakpoint.serializableInfo); >+ } >+ > _saveXHRBreakpoints() > { > if (this._restoringBreakpoints) >@@ -403,6 +497,13 @@ WI.DOMDebuggerManager = class DOMDebuggerManager extends WI.Object > this._saveDOMBreakpoints(); > } > >+ _eventListenerBreakpointDisabledStateDidChange(event) >+ { >+ let breakpoint = event.target; >+ this._updateEventListenerBreakpoint(breakpoint); >+ this._saveEventListenerBreakpoints(); >+ } >+ > _xhrBreakpointDisabledStateDidChange(event) > { > let breakpoint = event.target; >@@ -488,6 +589,8 @@ WI.DOMDebuggerManager = class DOMDebuggerManager extends WI.Object > WI.DOMDebuggerManager.Event = { > DOMBreakpointAdded: "dom-debugger-manager-dom-breakpoint-added", > DOMBreakpointRemoved: "dom-debugger-manager-dom-breakpoint-removed", >+ EventListenerBreakpointAdded: "dom-debugger-manager-event-listener-breakpoint-added", >+ EventListenerBreakpointRemoved: "dom-debugger-manager-event-listener-breakpoint-removed", > XHRBreakpointAdded: "dom-debugger-manager-xhr-breakpoint-added", > XHRBreakpointRemoved: "dom-debugger-manager-xhr-breakpoint-removed", > }; >diff --git a/Source/WebInspectorUI/UserInterface/Controllers/DebuggerManager.js b/Source/WebInspectorUI/UserInterface/Controllers/DebuggerManager.js >index 6502748a4af1a4f227333fc6a3226a90c3c5b91d..92595ab7e5b0650357e0a4b3da8a25288440ac66 100644 >--- a/Source/WebInspectorUI/UserInterface/Controllers/DebuggerManager.js >+++ b/Source/WebInspectorUI/UserInterface/Controllers/DebuggerManager.js >@@ -828,6 +828,8 @@ WI.DebuggerManager = class DebuggerManager extends WI.Object > return WI.DebuggerManager.PauseReason.DOM; > case DebuggerAgent.PausedReason.DebuggerStatement: > return WI.DebuggerManager.PauseReason.DebuggerStatement; >+ case DebuggerAgent.PausedReason.EventListener: >+ return WI.DebuggerManager.PauseReason.EventListener; > case DebuggerAgent.PausedReason.Exception: > return WI.DebuggerManager.PauseReason.Exception; > case DebuggerAgent.PausedReason.PauseOnNextStatement: >@@ -1236,6 +1238,7 @@ WI.DebuggerManager.PauseReason = { > CSPViolation: "CSP-violation", > DebuggerStatement: "debugger-statement", > DOM: "DOM", >+ EventListener: "EventListener", > Exception: "exception", > PauseOnNextStatement: "pause-on-next-statement", > XHR: "xhr", >diff --git a/Source/WebInspectorUI/UserInterface/Controllers/EventListenerBreakpointTreeController.js b/Source/WebInspectorUI/UserInterface/Controllers/EventListenerBreakpointTreeController.js >new file mode 100644 >index 0000000000000000000000000000000000000000..054a2da4165ffd76c75dc8cecb52985202f4f782 >--- /dev/null >+++ b/Source/WebInspectorUI/UserInterface/Controllers/EventListenerBreakpointTreeController.js >@@ -0,0 +1,76 @@ >+/* >+ * Copyright (C) 2018 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' >+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, >+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS >+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR >+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF >+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS >+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN >+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) >+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF >+ * THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+WI.EventListenerBreakpointTreeController = class EventListenerBreakpointTreeController >+{ >+ constructor(treeOutline) >+ { >+ this._treeOutline = treeOutline; >+ >+ WI.domDebuggerManager.addEventListener(WI.DOMDebuggerManager.Event.EventListenerBreakpointAdded, this._eventListenerBreakpointAdded, this); >+ WI.domDebuggerManager.addEventListener(WI.DOMDebuggerManager.Event.EventListenerBreakpointRemoved, this._eventListenerBreakpointRemoved, this); >+ >+ for (let breakpoint of WI.domDebuggerManager.eventListenerBreakpoints) >+ this._addTreeElement(breakpoint); >+ } >+ >+ // Public >+ >+ revealAndSelect(breakpoint) >+ { >+ let treeElement = this._treeOutline.findTreeElement(breakpoint); >+ if (!treeElement) >+ return; >+ >+ treeElement.revealAndSelect(); >+ } >+ >+ // Private >+ >+ _eventListenerBreakpointAdded(event) >+ { >+ this._addTreeElement(event.data.breakpoint); >+ } >+ >+ _eventListenerBreakpointRemoved(event) >+ { >+ let breakpoint = event.data.breakpoint; >+ let treeElement = this._treeOutline.findTreeElement(breakpoint); >+ if (!treeElement) >+ return; >+ >+ this._treeOutline.removeChild(treeElement); >+ } >+ >+ _addTreeElement(breakpoint) >+ { >+ let treeElement = this._treeOutline.findTreeElement(breakpoint); >+ console.assert(!treeElement); >+ if (treeElement) >+ return; >+ >+ this._treeOutline.appendChild(new WI.EventListenerBreakpointTreeElement(breakpoint)); >+ } >+}; >diff --git a/Source/WebInspectorUI/UserInterface/Images/EventBreakpoint.svg b/Source/WebInspectorUI/UserInterface/Images/EventBreakpoint.svg >new file mode 100644 >index 0000000000000000000000000000000000000000..15dacd648fad15184e7ee1e44a7a7b03c61f748c >--- /dev/null >+++ b/Source/WebInspectorUI/UserInterface/Images/EventBreakpoint.svg >@@ -0,0 +1,8 @@ >+<?xml version="1.0" encoding="utf-8"?> >+<!-- Copyright © 2018 Apple Inc. All rights reserved. --> >+<svg xmlns="http://www.w3.org/2000/svg" id="root" version="1.1" viewBox="0 0 16 16"> >+ <path fill="rgb(148, 183, 219)" d="M 13 1 L 3 1 C 1.898438 1 1 1.898438 1 3 L 1 13 C 1 14.101562 1.898438 15 3 15 L 13 15 C 14.101562 15 15 14.101562 15 13 L 15 3 C 15 1.898438 14.101562 1 13 1 Z"/> >+ <path fill="rgb(106, 136, 170)" d="M 13 1 L 3 1 C 1.898438 1 1 1.898438 1 3 L 1 13 C 1 14.101562 1.898438 15 3 15 L 13 15 C 14.101562 15 15 14.101562 15 13 L 15 3 C 15 1.898438 14.101562 1 13 1 M 13 2 C 13.550781 2 14 2.449219 14 3 L 14 13 C 14 13.550781 13.550781 14 13 14 L 3 14 C 2.449219 14 2 13.550781 2 13 L 2 3 C 2 2.449219 2.449219 2 3 2 L 13 2"/> >+ <path fill="rgb(113, 146, 184)" d="M 5.503906 12.742188 C 4.949219 12.742188 4.503906 12.292969 4.503906 11.742188 L 4.503906 3.792969 C 4.503906 3.242188 4.949219 2.792969 5.503906 2.792969 L 10.472656 2.792969 C 11.023438 2.792969 11.472656 3.242188 11.472656 3.792969 L 11.472656 4.882812 C 11.472656 5.4375 11.023438 5.882812 10.472656 5.882812 C 10.472656 5.882812 9.132812 5.882812 8.15625 5.882812 C 8.15625 5.992188 8.15625 5.996094 8.15625 6.101562 C 8.957031 6.101562 9.875 6.101562 9.875 6.101562 C 10.425781 6.101562 10.875 6.550781 10.875 7.101562 L 10.875 8.164062 C 10.875 8.71875 10.425781 9.164062 9.875 9.164062 C 9.875 9.164062 8.957031 9.164062 8.15625 9.164062 C 8.15625 9.382812 8.15625 9.398438 8.15625 9.613281 C 9.191406 9.613281 10.722656 9.613281 10.722656 9.613281 C 11.277344 9.613281 11.722656 10.0625 11.722656 10.613281 L 11.722656 11.742188 C 11.722656 12.292969 11.277344 12.742188 10.722656 12.742188 L 5.503906 12.742188"/> >+ <path fill="white" d="M 5.503906 11.742188 L 5.503906 3.792969 L 10.46875 3.792969 L 10.46875 4.882812 L 7.15625 4.882812 L 7.15625 7.101562 L 9.875 7.101562 L 9.875 8.164062 L 7.15625 8.164062 L 7.15625 10.613281 L 10.722656 10.613281 L 10.722656 11.742188 Z"/> >+</svg> >diff --git a/Source/WebInspectorUI/UserInterface/Main.html b/Source/WebInspectorUI/UserInterface/Main.html >index 010fc949ac13b04692684c7ba21bcf4b28d1d709..0d7f3a33dc3e16b10d7f93fef0d141b4ff9b5ab7 100644 >--- a/Source/WebInspectorUI/UserInterface/Main.html >+++ b/Source/WebInspectorUI/UserInterface/Main.html >@@ -83,6 +83,8 @@ > <link rel="stylesheet" href="Views/DetailsSection.css"> > <link rel="stylesheet" href="Views/DividerNavigationItem.css"> > <link rel="stylesheet" href="Views/Editing.css"> >+ <link rel="stylesheet" href="Views/EventListenerBreakpointPopover.css"> >+ <link rel="stylesheet" href="Views/EventListenerBreakpointTreeElement.css"> > <link rel="stylesheet" href="Views/EventListenerSectionGroup.css"> > <link rel="stylesheet" href="Views/ErrorObjectView.css"> > <link rel="stylesheet" href="Views/FilterBar.css"> >@@ -376,6 +378,7 @@ > <script src="Models/DebuggerDashboard.js"></script> > <script src="Models/DebuggerData.js"></script> > <script src="Models/DefaultDashboard.js"></script> >+ <script src="Models/EventListenerBreakpoint.js"></script> > <script src="Models/ExecutionContext.js"></script> > <script src="Models/ExecutionContextList.js"></script> > <script src="Models/FPSInstrument.js"></script> >@@ -622,6 +625,8 @@ > <script src="Views/EditableDataGridNode.js"></script> > <script src="Views/EditingSupport.js"></script> > <script src="Views/ErrorObjectView.js"></script> >+ <script src="Views/EventListenerBreakpointPopover.js"></script> >+ <script src="Views/EventListenerBreakpointTreeElement.js"></script> > <script src="Views/EventListenerSectionGroup.js"></script> > <script src="Views/FilterBar.js"></script> > <script src="Views/FilterBarButton.js"></script> >@@ -843,6 +848,7 @@ > <script src="Controllers/DebuggerManager.js"></script> > <script src="Controllers/DOMBreakpointTreeController.js"></script> > <script src="Controllers/DragToAdjustController.js"></script> >+ <script src="Controllers/EventListenerBreakpointTreeController.js"></script> > <script src="Controllers/Formatter.js"></script> > <script src="Controllers/FormatterSourceMap.js"></script> > <script src="Controllers/FrameResourceManager.js"></script> >diff --git a/Source/WebInspectorUI/UserInterface/Models/EventListenerBreakpoint.js b/Source/WebInspectorUI/UserInterface/Models/EventListenerBreakpoint.js >new file mode 100644 >index 0000000000000000000000000000000000000000..016f75c6853ed4de0d3c0aa937daa910ce838680 >--- /dev/null >+++ b/Source/WebInspectorUI/UserInterface/Models/EventListenerBreakpoint.js >@@ -0,0 +1,79 @@ >+/* >+ * Copyright (C) 2018 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' >+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, >+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS >+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR >+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF >+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS >+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN >+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) >+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF >+ * THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+WI.EventListenerBreakpoint = class EventListenerBreakpoint extends WI.Object >+{ >+ constructor(eventName, disabled) >+ { >+ super(); >+ >+ console.assert(typeof eventName === "string"); >+ >+ this._eventName = eventName; >+ this._disabled = disabled || false; >+ } >+ >+ // Public >+ >+ get eventName() { return this._eventName; } >+ >+ get disabled() >+ { >+ return this._disabled; >+ } >+ >+ set disabled(disabled) >+ { >+ if (this._disabled === disabled) >+ return; >+ >+ this._disabled = disabled; >+ >+ this.dispatchEventToListeners(WI.EventListenerBreakpoint.Event.DisabledStateDidChange); >+ } >+ >+ get serializableInfo() >+ { >+ let info = { >+ eventName: this._eventName, >+ }; >+ if (this._disabled) >+ info.disabled = true; >+ >+ return info; >+ } >+ >+ saveIdentityToCookie(cookie) >+ { >+ cookie[WI.EventListenerBreakpoint.EventNameCookieKey] = this._eventName; >+ } >+}; >+ >+WI.EventListenerBreakpoint.EventNameCookieKey = "event-listener-breakpoint-event-name"; >+ >+WI.EventListenerBreakpoint.Event = { >+ DisabledStateDidChange: "event-listener-breakpoint-disabled-state-did-change", >+ ResolvedStateDidChange: "event-listener-breakpoint-resolved-state-did-change", >+}; >diff --git a/Source/WebInspectorUI/UserInterface/Test.html b/Source/WebInspectorUI/UserInterface/Test.html >index 0defd44cb48689f2dc4c770237a316e3520c71bc..9a4166861d0dacb89f67ef7abaa6e75e3a2bf770 100644 >--- a/Source/WebInspectorUI/UserInterface/Test.html >+++ b/Source/WebInspectorUI/UserInterface/Test.html >@@ -133,6 +133,7 @@ > <script src="Models/DOMStorageObject.js"></script> > <script src="Models/DOMTree.js"></script> > <script src="Models/DebuggerData.js"></script> >+ <script src="Models/EventListenerBreakpoint.js"></script> > <script src="Models/ExecutionContext.js"></script> > <script src="Models/ExecutionContextList.js"></script> > <script src="Models/FPSInstrument.js"></script> >diff --git a/Source/WebInspectorUI/UserInterface/Views/DebuggerSidebarPanel.js b/Source/WebInspectorUI/UserInterface/Views/DebuggerSidebarPanel.js >index 5a5362582d137ff07904763bbc524bcf046009cf..aa17ff4992d7a240fdd5ce716c32bcab4e814557 100644 >--- a/Source/WebInspectorUI/UserInterface/Views/DebuggerSidebarPanel.js >+++ b/Source/WebInspectorUI/UserInterface/Views/DebuggerSidebarPanel.js >@@ -176,22 +176,44 @@ WI.DebuggerSidebarPanel = class DebuggerSidebarPanel extends WI.NavigationSideba > this._domBreakpointsSection = new WI.DetailsSection("dom-breakpoints", WI.UIString("DOM Breakpoints"), [domBreakpointsGroup], null, defaultCollapsed); > this.contentView.element.appendChild(this._domBreakpointsSection.element); > >+ this._eventListenerBreakpointsContentTreeOutline = this.createContentTreeOutline(true); >+ this._eventListenerBreakpointsContentTreeOutline.addEventListener(WI.TreeOutline.Event.ElementAdded, this._eventListenerBreakpointAddedOrRemoved, this); >+ this._eventListenerBreakpointsContentTreeOutline.addEventListener(WI.TreeOutline.Event.ElementRemoved, this._eventListenerBreakpointAddedOrRemoved, this); >+ >+ this._eventListenerBreakpointsRow = new WI.DetailsSectionRow(WI.UIString("No Breakpoints")); >+ this._eventListenerBreakpointsRow.element.appendChild(this._eventListenerBreakpointsContentTreeOutline.element); >+ this._eventListenerBreakpointsRow.showEmptyMessage(); >+ >+ let eventListenerBreakpointNavigationBar = new WI.NavigationBar; >+ let eventListenerBreakpointNavigationBarWrapper = document.createElement("div"); >+ eventListenerBreakpointNavigationBarWrapper.appendChild(eventListenerBreakpointNavigationBar.element); >+ >+ let addEventListenerBreakpointButton = new WI.ButtonNavigationItem("add-event-listener-breakpoint", WI.UIString("Add Event Listener Breakpoint"), "Images/Plus13.svg", 13, 13); >+ addEventListenerBreakpointButton.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._addEventListenerBreakpointButtonClicked, this); >+ eventListenerBreakpointNavigationBar.addNavigationItem(addEventListenerBreakpointButton); >+ >+ let eventListenerBreakpointsGroup = new WI.DetailsSectionGroup([this._eventListenerBreakpointsRow]); >+ this._eventListenerBreakpointsSection = new WI.DetailsSection("event-listener-breakpoints", WI.UIString("Event Listener Breakpoints"), [eventListenerBreakpointsGroup], eventListenerBreakpointNavigationBarWrapper, defaultCollapsed); >+ this.contentView.element.appendChild(this._eventListenerBreakpointsSection.element); >+ >+ this._eventListenerBreakpointTreeController = new WI.EventListenerBreakpointTreeController(this._eventListenerBreakpointsContentTreeOutline); >+ > this._xhrBreakpointsContentTreeOutline = this.createContentTreeOutline(true); > this._xhrBreakpointTreeController = new WI.XHRBreakpointTreeController(this._xhrBreakpointsContentTreeOutline); > > this._xhrBreakpointsRow = new WI.DetailsSectionRow; > this._xhrBreakpointsRow.element.appendChild(this._xhrBreakpointsContentTreeOutline.element); > >- let navigationBar = new WI.NavigationBar; >- let navigationBarWrapper = document.createElement("div"); >- navigationBarWrapper.appendChild(navigationBar.element); >+ let xhrBreakpointNavigationBar = new WI.NavigationBar; >+ let xhrBreakpointNavigationBarWrapper = document.createElement("div"); >+ xhrBreakpointNavigationBarWrapper.appendChild(xhrBreakpointNavigationBar.element); > > let addXHRBreakpointButton = new WI.ButtonNavigationItem("add-xhr-breakpoint", WI.UIString("Add XHR Breakpoint"), "Images/Plus13.svg", 13, 13); > addXHRBreakpointButton.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._addXHRBreakpointButtonClicked, this); >- navigationBar.addNavigationItem(addXHRBreakpointButton); >+ xhrBreakpointNavigationBar.addNavigationItem(addXHRBreakpointButton); > > let xhrBreakpointsGroup = new WI.DetailsSectionGroup([this._xhrBreakpointsRow]); >- let xhrBreakpointsSection = new WI.DetailsSection("xhr-breakpoints", WI.UIString("XHR Breakpoints"), [xhrBreakpointsGroup], navigationBarWrapper, defaultCollapsed); >+ let xhrBreakpointsSection = new WI.DetailsSection("xhr-breakpoints", WI.UIString("XHR Breakpoints"), [xhrBreakpointsGroup], xhrBreakpointNavigationBarWrapper, defaultCollapsed); > this.contentView.element.appendChild(xhrBreakpointsSection.element); > } > >@@ -1032,6 +1054,24 @@ WI.DebuggerSidebarPanel = class DebuggerSidebarPanel extends WI.NavigationSideba > } > break; > >+ case WI.DebuggerManager.PauseReason.EventListener: >+ console.assert(pauseData, "Expected data with an event listener, but found none."); >+ if (pauseData) { >+ let eventListenerBreakpoint = WI.domDebuggerManager.eventListenerBreakpointForEventName(pauseData.eventName); >+ console.assert(eventListenerBreakpoint, "Expected Event Listener breakpoint for event name.", pauseData.eventName); >+ >+ this._pauseReasonTreeOutline = this.createContentTreeOutline(true); >+ >+ let eventListenerBreakpointTreeElement = new WI.EventListenerBreakpointTreeElement(eventListenerBreakpoint, WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName, WI.UIString("â%sâ Event Fired").format(pauseData.eventName)); >+ this._pauseReasonTreeOutline.appendChild(eventListenerBreakpointTreeElement); >+ >+ let eventListenerBreakpointRow = new WI.DetailsSectionRow; >+ eventListenerBreakpointRow.element.appendChild(this._pauseReasonTreeOutline.element); >+ >+ this._pauseReasonGroup.rows = [eventListenerBreakpointRow]; >+ } >+ return true; >+ > case WI.DebuggerManager.PauseReason.Exception: > console.assert(pauseData, "Expected data with an exception, but found none."); > if (pauseData) { >@@ -1175,6 +1215,28 @@ WI.DebuggerSidebarPanel = class DebuggerSidebarPanel extends WI.NavigationSideba > this._domBreakpointsSection.collapsed = false; > } > >+ _eventListenerBreakpointAddedOrRemoved(event) >+ { >+ if (!this._eventListenerBreakpointsContentTreeOutline.children.length) { >+ this._eventListenerBreakpointsRow.showEmptyMessage(); >+ return; >+ } >+ >+ if (this._eventListenerBreakpointsContentTreeOutline.element.parent) >+ return; >+ >+ this._eventListenerBreakpointsRow.hideEmptyMessage(); >+ this._eventListenerBreakpointsRow.element.append(this._eventListenerBreakpointsContentTreeOutline.element); >+ >+ this._eventListenerBreakpointsSection.collapsed = false; >+ } >+ >+ _addEventListenerBreakpointButtonClicked(event) >+ { >+ let popover = new WI.EventListenerBreakpointPopover(this); >+ popover.show(event.target.element, [WI.RectEdge.MAX_Y, WI.RectEdge.MIN_Y, WI.RectEdge.MAX_X]); >+ } >+ > _addXHRBreakpointButtonClicked(event) > { > let popover = new WI.XHRBreakpointPopover(this); >@@ -1188,11 +1250,19 @@ WI.DebuggerSidebarPanel = class DebuggerSidebarPanel extends WI.NavigationSideba > if (popover.result !== WI.InputPopover.Result.Committed) > return; > >- let url = popover.value; >- if (!url) >+ if (popover instanceof WI.EventListenerBreakpointPopover) { >+ let eventName = popover.value; >+ if (eventName) >+ WI.domDebuggerManager.addEventListenerBreakpoint(new WI.EventListenerBreakpoint(eventName)); > return; >+ } > >- WI.domDebuggerManager.addXHRBreakpoint(new WI.XHRBreakpoint(popover.type, url)); >+ if (popover instanceof WI.XHRBreakpointPopover) { >+ let url = popover.value; >+ if (url) >+ WI.domDebuggerManager.addXHRBreakpoint(new WI.XHRBreakpoint(popover.type, url)); >+ return; >+ } > } > }; > >diff --git a/Source/WebInspectorUI/UserInterface/Views/EventListenerBreakpointPopover.css b/Source/WebInspectorUI/UserInterface/Views/EventListenerBreakpointPopover.css >new file mode 100644 >index 0000000000000000000000000000000000000000..7080bb8f80604a71993cc6c90cfec78110218ea4 >--- /dev/null >+++ b/Source/WebInspectorUI/UserInterface/Views/EventListenerBreakpointPopover.css >@@ -0,0 +1,36 @@ >+/* >+ * Copyright (C) 2018 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' >+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, >+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS >+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR >+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF >+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS >+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN >+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) >+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF >+ * THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+.popover .event-listener-breakpoint-content { >+ margin: 2px; >+ padding: 5px; >+} >+ >+.popover .event-listener-breakpoint-content > input { >+ width: 100%; >+ margin-top: 4px; >+ padding: 4px 0 2px 0; >+ outline: none; >+} >diff --git a/Source/WebInspectorUI/UserInterface/Views/EventListenerBreakpointPopover.js b/Source/WebInspectorUI/UserInterface/Views/EventListenerBreakpointPopover.js >new file mode 100644 >index 0000000000000000000000000000000000000000..64cbb567961cfec79533d5da192b9dc6ebbc98f2 >--- /dev/null >+++ b/Source/WebInspectorUI/UserInterface/Views/EventListenerBreakpointPopover.js >@@ -0,0 +1,88 @@ >+/* >+ * Copyright (C) 2018 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' >+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, >+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS >+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR >+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF >+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS >+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN >+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) >+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF >+ * THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+WI.EventListenerBreakpointPopover = class EventListenerBreakpointPopover extends WI.Popover >+{ >+ constructor(delegate) >+ { >+ super(delegate); >+ >+ this._result = WI.InputPopover.Result.None; >+ this._value = null; >+ >+ this._codeMirror = null; >+ this._targetElement = null; >+ this._preferredEdges = null; >+ >+ this.windowResizeHandler = this._presentOverTargetElement.bind(this); >+ } >+ >+ // Public >+ >+ get result() { return this._result; } >+ get value() { return this._value; } >+ >+ show(targetElement, preferredEdges) >+ { >+ this._targetElement = targetElement; >+ this._preferredEdges = preferredEdges; >+ >+ let contentElement = document.createElement("div"); >+ contentElement.classList.add("event-listener-breakpoint-content"); >+ >+ let label = contentElement.appendChild(document.createElement("div")); >+ label.classList.add("label"); >+ label.textContent = WI.UIString("Break on events with name:"); >+ >+ this._inputElement = contentElement.appendChild(document.createElement("input")); >+ this._inputElement.placeholder = "click"; >+ this._inputElement.addEventListener("keydown", (event) => { >+ if (!isEnterKey(event)) >+ return; >+ >+ this._result = WI.InputPopover.Result.Committed; >+ this._value = event.target.value.trim(); >+ >+ this.dismiss(); >+ }); >+ >+ this.content = contentElement; >+ >+ this._presentOverTargetElement(); >+ } >+ >+ // Private >+ >+ _presentOverTargetElement() >+ { >+ if (!this._targetElement) >+ return; >+ >+ let targetFrame = WI.Rect.rectFromClientRect(this._targetElement.getBoundingClientRect()); >+ this.present(targetFrame, this._preferredEdges); >+ >+ this._inputElement.select(); >+ } >+}; >diff --git a/Source/WebInspectorUI/UserInterface/Views/EventListenerBreakpointTreeElement.css b/Source/WebInspectorUI/UserInterface/Views/EventListenerBreakpointTreeElement.css >new file mode 100644 >index 0000000000000000000000000000000000000000..7d74252a6a4c693fac00594716db820f9c80ea3d >--- /dev/null >+++ b/Source/WebInspectorUI/UserInterface/Views/EventListenerBreakpointTreeElement.css >@@ -0,0 +1,28 @@ >+/* >+ * Copyright (C) 2018 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' >+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, >+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS >+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR >+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF >+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS >+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN >+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) >+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF >+ * THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+.breakpoint.event-listener:not(.breakpoint-paused-icon) .icon { >+ content: url(../Images/EventBreakpoint.svg); >+} >diff --git a/Source/WebInspectorUI/UserInterface/Views/EventListenerBreakpointTreeElement.js b/Source/WebInspectorUI/UserInterface/Views/EventListenerBreakpointTreeElement.js >new file mode 100644 >index 0000000000000000000000000000000000000000..fecc8db41ec6704d964ddf83573ef648680edbf1 >--- /dev/null >+++ b/Source/WebInspectorUI/UserInterface/Views/EventListenerBreakpointTreeElement.js >@@ -0,0 +1,139 @@ >+/* >+ * Copyright (C) 2018 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' >+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, >+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS >+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR >+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF >+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS >+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN >+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) >+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF >+ * THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+WI.EventListenerBreakpointTreeElement = class EventListenerBreakpointTreeElement extends WI.GeneralTreeElement >+{ >+ constructor(breakpoint, className, title) >+ { >+ console.assert(breakpoint instanceof WI.EventListenerBreakpoint); >+ >+ let classNames = ["breakpoint", "event-listener"]; >+ if (className) >+ classNames.push(className); >+ >+ if (!title) >+ title = breakpoint.eventName; >+ >+ const subtitle = null; >+ super(classNames, title, subtitle, breakpoint); >+ >+ this._statusImageElement = document.createElement("img"); >+ this._statusImageElement.classList.add("status-image", "resolved"); >+ this.status = this._statusImageElement; >+ >+ breakpoint.addEventListener(WI.EventListenerBreakpoint.Event.DisabledStateDidChange, this._updateStatus, this); >+ >+ this._updateStatus(); >+ } >+ >+ // Protected >+ >+ onattach() >+ { >+ super.onattach(); >+ >+ this._boundStatusImageElementClicked = this._statusImageElementClicked.bind(this); >+ this._boundStatusImageElementFocused = this._statusImageElementFocused.bind(this); >+ this._boundStatusImageElementMouseDown = this._statusImageElementMouseDown.bind(this); >+ >+ this._statusImageElement.addEventListener("click", this._boundStatusImageElementClicked); >+ this._statusImageElement.addEventListener("focus", this._boundStatusImageElementFocused); >+ this._statusImageElement.addEventListener("mousedown", this._boundStatusImageElementMouseDown); >+ } >+ >+ ondetach() >+ { >+ super.ondetach(); >+ >+ this._statusImageElement.removeEventListener("click", this._boundStatusImageElementClicked); >+ this._statusImageElement.removeEventListener("focus", this._boundStatusImageElementFocused); >+ this._statusImageElement.removeEventListener("mousedown", this._boundStatusImageElementMouseDown); >+ >+ this._boundStatusImageElementClicked = null; >+ this._boundStatusImageElementFocused = null; >+ this._boundStatusImageElementMouseDown = null; >+ } >+ >+ ondelete() >+ { >+ WI.domDebuggerManager.removeEventListenerBreakpoint(this.representedObject); >+ return true; >+ } >+ >+ onenter() >+ { >+ this._toggleBreakpoint(); >+ return true; >+ } >+ >+ onspace() >+ { >+ this._toggleBreakpoint(); >+ return true; >+ } >+ >+ populateContextMenu(contextMenu, event) >+ { >+ let breakpoint = this.representedObject; >+ let label = breakpoint.disabled ? WI.UIString("Enable Breakpoint") : WI.UIString("Disable Breakpoint"); >+ contextMenu.appendItem(label, this._toggleBreakpoint.bind(this)); >+ >+ if (WI.domDebuggerManager.isBreakpointRemovable(breakpoint)) { >+ contextMenu.appendSeparator(); >+ contextMenu.appendItem(WI.UIString("Delete Breakpoint"), () => { >+ WI.domDebuggerManager.removeEventListenerBreakpoint(breakpoint); >+ }); >+ } >+ } >+ >+ // Private >+ >+ _statusImageElementClicked(event) >+ { >+ this._toggleBreakpoint(); >+ } >+ >+ _statusImageElementFocused(event) >+ { >+ // Prevent tree outline focus. >+ event.stopPropagation(); >+ } >+ >+ _statusImageElementMouseDown(event) >+ { >+ // Prevent tree element selection. >+ event.stopPropagation(); >+ } >+ >+ _toggleBreakpoint() >+ { >+ this.representedObject.disabled = !this.representedObject.disabled; >+ } >+ >+ _updateStatus() >+ { >+ this._statusImageElement.classList.toggle("disabled", this.representedObject.disabled); >+ } >+}; >diff --git a/Source/WebInspectorUI/UserInterface/Views/NavigationSidebarPanel.js b/Source/WebInspectorUI/UserInterface/Views/NavigationSidebarPanel.js >index 6574034aaf55ea51f854ec5d8164f2af45051652..27e317e30a820e84a7f2af8eef27729aa14087f3 100644 >--- a/Source/WebInspectorUI/UserInterface/Views/NavigationSidebarPanel.js >+++ b/Source/WebInspectorUI/UserInterface/Views/NavigationSidebarPanel.js >@@ -633,6 +633,7 @@ WI.NavigationSidebarPanel = class NavigationSidebarPanel extends WI.SidebarPanel > || treeElement instanceof WI.ThreadTreeElement > || treeElement instanceof WI.IdleTreeElement > || treeElement instanceof WI.DOMBreakpointTreeElement >+ || treeElement instanceof WI.EventListenerBreakpointTreeElement > || treeElement instanceof WI.XHRBreakpointTreeElement > || treeElement instanceof WI.CSSStyleSheetTreeElement > || typeof treeElement.representedObject === "string" >diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog >index 822ac2e3e736117474b3b6fd6d10b5e0fd2281e5..0c5b3b33cfaa14b79f6ad3462f760eee56cb6725 100644 >--- a/LayoutTests/ChangeLog >+++ b/LayoutTests/ChangeLog >@@ -1,3 +1,13 @@ >+2018-08-15 Devin Rousso <drousso@apple.com> >+ >+ Web Inspector: support breakpoints for arbitrary event names >+ https://bugs.webkit.org/show_bug.cgi?id=183118 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * inspector/dom-debugger/event-listener-breakpoints-expected.txt: Added. >+ * inspector/dom-debugger/event-listener-breakpoints.html: Added. >+ > 2018-08-14 Zalan Bujtas <zalan@apple.com> > > [LFC][Floating] Add support for negative clearance. >diff --git a/LayoutTests/inspector/dom-debugger/event-listener-breakpoints-expected.txt b/LayoutTests/inspector/dom-debugger/event-listener-breakpoints-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..ad02250cc83203e4a028c2778793a9bd2ae39b6a >--- /dev/null >+++ b/LayoutTests/inspector/dom-debugger/event-listener-breakpoints-expected.txt >@@ -0,0 +1,68 @@ >+Tests for Event Listener breakpoints. >+ >+ >+== Running test suite: DOMDebugger.EventListener >+-- Running test case: DOMDebugger.EventListener.AddBreakpoint "click" >+Adding "click" Event Breakpoint... >+Firing "click" on body... >+PASS: Should pause before event handler is run. >+CALL STACK: >+0: [F] handleBody_click >+1: [F] bodyFire_click >+2: [P] Global Code >+-- Running test teardown. >+ >+-- Running test case: DOMDebugger.EventListener.AddDisabledBreakpoint "click" >+Adding "click" Event Breakpoint... >+Disabling "click" Event Breakpoint... >+Firing "click" on body... >+PASS: Should not pause for disabled breakpoint. >+-- Running test teardown. >+ >+-- Running test case: DOMDebugger.EventListener.RemoveBreakpoint "click" >+Adding "click" Event Breakpoint... >+Removing "click" Event Breakpoint... >+Firing "click" on body... >+PASS: Should not pause for removed breakpoint. >+-- Running test teardown. >+ >+-- Running test case: DOMDebugger.EventListener.RemoveDisabledBreakpoint "click" >+Adding "click" Event Breakpoint... >+Disabling "click" Event Breakpoint... >+Removing "click" Event Breakpoint... >+Firing "click" on body... >+PASS: Should not pause for removed disabled breakpoint. >+-- Running test teardown. >+ >+-- Running test case: DOMDebugger.EventListener.AddBreakpoint "custom" >+Adding "custom" Event Breakpoint... >+Firing "custom" on body... >+PASS: Should pause before event handler is run. >+CALL STACK: >+0: [F] handleBody_custom >+1: [F] bodyFire_custom >+2: [P] Global Code >+-- Running test teardown. >+ >+-- Running test case: DOMDebugger.EventListener.AddDisabledBreakpoint "custom" >+Adding "custom" Event Breakpoint... >+Disabling "custom" Event Breakpoint... >+Firing "custom" on body... >+PASS: Should not pause for disabled breakpoint. >+-- Running test teardown. >+ >+-- Running test case: DOMDebugger.EventListener.RemoveBreakpoint "custom" >+Adding "custom" Event Breakpoint... >+Removing "custom" Event Breakpoint... >+Firing "custom" on body... >+PASS: Should not pause for removed breakpoint. >+-- Running test teardown. >+ >+-- Running test case: DOMDebugger.EventListener.RemoveDisabledBreakpoint "custom" >+Adding "custom" Event Breakpoint... >+Disabling "custom" Event Breakpoint... >+Removing "custom" Event Breakpoint... >+Firing "custom" on body... >+PASS: Should not pause for removed disabled breakpoint. >+-- Running test teardown. >+ >diff --git a/LayoutTests/inspector/dom-debugger/event-listener-breakpoints.html b/LayoutTests/inspector/dom-debugger/event-listener-breakpoints.html >new file mode 100644 >index 0000000000000000000000000000000000000000..686ae4900a5856fd10aad4365a035a1c5678be43 >--- /dev/null >+++ b/LayoutTests/inspector/dom-debugger/event-listener-breakpoints.html >@@ -0,0 +1,207 @@ >+<!doctype html> >+<html> >+<head> >+<script src="../../http/tests/inspector/resources/inspector-test.js"></script> >+<script src="../debugger/resources/log-active-stack-trace.js"></script> >+<script> >+function handleBody_click(event) { >+ TestPage.dispatchEventToFrontend("TestPageBody-click"); >+} >+ >+function handleBody_custom(event) { >+ TestPage.dispatchEventToFrontend("TestPageBody-custom"); >+} >+ >+function bodyFire_click() { >+ document.body.click(); >+} >+ >+function bodyFire_custom() { >+ document.body.dispatchEvent(new Event("custom")); >+} >+ >+function test() { >+ let suite = InspectorTest.createAsyncSuite("DOMDebugger.EventListener"); >+ >+ function teardown(resolve, reject) { >+ let breakpoints = WI.domDebuggerManager.eventListenerBreakpoints; >+ for (let breakpoint of breakpoints) >+ WI.domDebuggerManager.removeEventListenerBreakpoint(breakpoint); >+ >+ resolve(); >+ } >+ >+ function awaitBodyEvent(eventName) { >+ return function() { >+ InspectorTest.log(`Firing "${eventName}" on body...`); >+ >+ return new Promise((resolve, reject) => { >+ InspectorTest.evaluateInPage(`bodyFire_${eventName}()`, (error) => { >+ if (error) >+ reject(error); >+ else >+ resolve(); >+ }); >+ }); >+ }; >+ } >+ >+ function failOnPause(resolve, eventName, message) { >+ let paused = false; >+ >+ let listener = WI.debuggerManager.singleFireEventListener(WI.DebuggerManager.Event.Paused, (event) => { >+ paused = true; >+ >+ let targetData = WI.debuggerManager.dataForTarget(WI.debuggerManager.activeCallFrame.target); >+ InspectorTest.assert(targetData.pauseReason === WI.DebuggerManager.PauseReason.EventListener, "Pause reason should be EventListener."); >+ InspectorTest.assert(targetData.pauseData.eventName === eventName, `Pause data eventName should be "${eventName}".`); >+ >+ InspectorTest.fail(message); >+ logActiveStackTrace(); >+ >+ WI.debuggerManager.resume(); >+ }); >+ >+ InspectorTest.singleFireEventListener("TestPageBody-" + eventName, (event) => { >+ if (!paused) { >+ WI.debuggerManager.removeEventListener(WI.DebuggerManager.Event.Paused, listener); >+ >+ InspectorTest.pass(message); >+ } >+ >+ resolve(); >+ }); >+ } >+ >+ function addBreakpoint(eventName) { >+ InspectorTest.log(`Adding "${eventName}" Event Breakpoint...`); >+ >+ return new Promise((resolve, reject) => { >+ let breakpoint = new WI.EventListenerBreakpoint(eventName); >+ >+ WI.domDebuggerManager.awaitEvent(WI.DOMDebuggerManager.Event.EventListenerBreakpointAdded) >+ .then((event) => { >+ InspectorTest.assert(event.data.breakpoint.eventName === eventName, "Breakpoint should be for expected event name."); >+ InspectorTest.assert(!breakpoint.disabled, "Breakpoint should not be disabled initially."); >+ resolve(breakpoint); >+ }); >+ >+ WI.domDebuggerManager.addEventListenerBreakpoint(breakpoint); >+ }); >+ } >+ >+ function removeBreakpoint(breakpoint) { >+ InspectorTest.log(`Removing "${breakpoint.eventName}" Event Breakpoint...`); >+ >+ return new Promise((resolve, reject) => { >+ WI.domDebuggerManager.awaitEvent(WI.DOMDebuggerManager.Event.EventListenerBreakpointRemoved) >+ .then((event) => { >+ InspectorTest.assert(event.data.breakpoint === breakpoint, "Removed Breakpoint should be expected object."); >+ InspectorTest.assert(!WI.domDebuggerManager.eventListenerBreakpoints.includes(breakpoint), "Breakpoint should not be in the list of breakpoints."); >+ resolve(breakpoint); >+ }); >+ >+ WI.domDebuggerManager.removeEventListenerBreakpoint(breakpoint); >+ }); >+ } >+ >+ function disableBreakpoint(breakpoint) { >+ InspectorTest.log(`Disabling "${breakpoint.eventName}" Event Breakpoint...`); >+ >+ breakpoint.disabled = true; >+ return breakpoint; >+ } >+ >+ function addTestCasesForEventName(eventName) { >+ suite.addTestCase({ >+ name: `DOMDebugger.EventListener.AddBreakpoint "${eventName}"`, >+ description: "Check that the debugger pauses for enabled breakpoints.", >+ teardown, >+ test(resolve, reject) { >+ let paused = false; >+ >+ let listener = WI.debuggerManager.singleFireEventListener(WI.DebuggerManager.Event.Paused, (event) => { >+ paused = true; >+ >+ InspectorTest.pass("Should pause before event handler is run."); >+ logActiveStackTrace(); >+ >+ WI.debuggerManager.resume(); >+ }); >+ >+ InspectorTest.singleFireEventListener(`TestPageBody-${eventName}`, (event) => { >+ if (!paused) { >+ WI.debuggerManager.removeEventListener(WI.DebuggerManager.Event.Paused, listener); >+ >+ InspectorTest.fail("Should pause before event handler is run."); >+ } >+ >+ resolve(); >+ }); >+ >+ addBreakpoint(eventName) >+ .then(awaitBodyEvent(eventName)) >+ .catch(reject); >+ } >+ }); >+ >+ suite.addTestCase({ >+ name: `DOMDebugger.EventListener.AddDisabledBreakpoint "${eventName}"`, >+ description: "Check that debugger does the not pause for disabled breakpoints.", >+ teardown, >+ test(resolve, reject) { >+ failOnPause(resolve, eventName, "Should not pause for disabled breakpoint."); >+ >+ addBreakpoint(eventName) >+ .then(disableBreakpoint) >+ .then(awaitBodyEvent(eventName)) >+ .catch(reject); >+ } >+ }); >+ >+ suite.addTestCase({ >+ name: `DOMDebugger.EventListener.RemoveBreakpoint "${eventName}"`, >+ description: "Check that debugger does not pause for removed breakpoint.", >+ teardown, >+ test(resolve, reject) { >+ failOnPause(resolve, eventName, "Should not pause for removed breakpoint."); >+ >+ addBreakpoint(eventName) >+ .then(removeBreakpoint) >+ .then(awaitBodyEvent(eventName)) >+ .catch(reject); >+ } >+ }); >+ >+ suite.addTestCase({ >+ name: `DOMDebugger.EventListener.RemoveDisabledBreakpoint "${eventName}"`, >+ description: "Check that a disabled breakpoint can be removed.", >+ teardown, >+ test(resolve, reject) { >+ failOnPause(resolve, eventName, "Should not pause for removed disabled breakpoint."); >+ >+ addBreakpoint(eventName) >+ .then(disableBreakpoint) >+ .then(removeBreakpoint) >+ .then(awaitBodyEvent(eventName)) >+ .catch(reject); >+ } >+ }); >+ >+ } >+ >+ addTestCasesForEventName("click"); >+ addTestCasesForEventName("custom"); >+ >+ suite.runTestCasesAndFinish(); >+} >+</script> >+</head> >+<body onload="runTest()"> >+ <p>Tests for Event Listener breakpoints.</p> >+ <script> >+ document.body.addEventListener("click", handleBody_click); >+ document.body.addEventListener("custom", handleBody_custom); >+ </script> >+</body> >+</html>
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 183118
:
334593
|
334594
|
334596
|
334597
|
334603
|
334633
|
335486
|
335762
|
346100
|
346101
|
346363
|
346367
|
346368
|
346369
|
346384
|
347172
|
347331