Changeset 243244 in webkit


Ignore:
Timestamp:
Mar 20, 2019 2:55:05 PM (5 years ago)
Author:
Devin Rousso
Message:

Web Inspector: DOM: include window as part of any event listener chain
https://bugs.webkit.org/show_bug.cgi?id=195730
<rdar://problem/48916872>

Reviewed by Timothy Hatcher.

Source/JavaScriptCore:

  • inspector/protocol/DOM.json:

Modify DOM.getEventListenersForNode to not save the handler object, as that was never
used by the frontend. Add an onWindow optional property to DOM.EventListener that is set
when the event listener was retrieved from the window object.

Source/WebCore:

Test: inspector/dom/getEventListenersForNode.html

  • inspector/agents/InspectorDOMAgent.h:

(WebCore::EventListenerInfo::EventListenerInfo): Deleted.

  • inspector/agents/InspectorDOMAgent.cpp:

(WebCore::InspectorDOMAgent::getEventListenersForNode):
(WebCore::InspectorDOMAgent::buildObjectForEventListener):
(WebCore::InspectorDOMAgent::getEventListeners): Deleted.

Source/WebInspectorUI:

Allow non-nodes (e.g. window) to be listed as the target of an event listener.
Add support for the same concept when showing breakpoint details after pausing on a specific
event listener in the Debugger/Sources navigation sidebar.

  • UserInterface/Views/DOMNodeDetailsSidebarPanel.js:

(WI.DOMNodeDetailsSidebarPanel.prototype.initialLayout):
(WI.DOMNodeDetailsSidebarPanel.prototype._refreshEventListeners.generateGroupsByEvent):
(WI.DOMNodeDetailsSidebarPanel.prototype._refreshEventListeners.generateGroupsByTarget): Added.
(WI.DOMNodeDetailsSidebarPanel.prototype._refreshEventListeners.eventListenersCallback):
(WI.DOMNodeDetailsSidebarPanel.prototype._refreshEventListeners):
(WI.DOMNodeDetailsSidebarPanel.prototype._refreshEventListeners.generateGroupsByNode): Deleted.

  • UserInterface/Views/EventListenerSectionGroup.js:

(WI.EventListenerSectionGroup.prototype._targetTextOrLink): Added.
(WI.EventListenerSectionGroup.prototype._nodeTextOrLink): Deleted.

  • UserInterface/Views/DebuggerSidebarPanel.js:

(WI.DebuggerSidebarPanel.prototype._addBreakpoint):
(WI.DebuggerSidebarPanel.prototype._breakpointTreeOutlineDeleteTreeElement):
(WI.DebuggerSidebarPanel.prototype._treeSelectionDidChange):
(WI.DebuggerSidebarPanel.prototype._updatePauseReasonSection):

  • UserInterface/Views/DebuggerSidebarPanel.css:

(.sidebar > .panel.navigation.debugger > .content > .breakpoints .tree-outline .item.event-target-window .icon): Added.

  • UserInterface/Views/SourcesNavigationSidebarPanel.js:

(WI.SourcesNavigationSidebarPanel):
(WI.SourcesNavigationSidebarPanel.prototype._addBreakpoint):
(WI.SourcesNavigationSidebarPanel.prototype._updatePauseReasonSection):
(WI.SourcesNavigationSidebarPanel.prototype._handleTreeSelectionDidChange):

  • UserInterface/Views/SourcesNavigationSidebarPanel.css:

(.sidebar > .panel.navigation.sources > .content > .breakpoints .tree-outline .item.event-target-window .icon): Added.

  • Localizations/en.lproj/localizedStrings.js:

LayoutTests:

  • inspector/dom/getEventListenersForNode.html:
  • inspector/dom/getEventListenersForNode-expected.txt:
  • inspector/dom/setEventListenerDisabled.html:
  • inspector/dom/event-listener-add-remove.html:
Location:
trunk
Files:
18 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r243242 r243244  
     12019-03-20  Devin Rousso  <drousso@apple.com>
     2
     3        Web Inspector: DOM: include window as part of any event listener chain
     4        https://bugs.webkit.org/show_bug.cgi?id=195730
     5        <rdar://problem/48916872>
     6
     7        Reviewed by Timothy Hatcher.
     8
     9        * inspector/dom/getEventListenersForNode.html:
     10        * inspector/dom/getEventListenersForNode-expected.txt:
     11        * inspector/dom/setEventListenerDisabled.html:
     12        * inspector/dom/event-listener-add-remove.html:
     13
    1142019-03-20  Devin Rousso  <drousso@apple.com>
    215
  • trunk/LayoutTests/inspector/dom/event-listener-add-remove.html

    r236766 r243244  
    5454
    5555    function logListeners(expectedCount) {
    56         return DOMAgent.getEventListenersForNode(node.id).then((payload) => {
    57             InspectorTest.expectEqual(payload.listeners.length, expectedCount, `There should be ${expectedCount} event listeners.`);
    58 
    59             for (let eventListener of payload.listeners)
     56        return DOMAgent.getEventListenersForNode(node.id).then(({listeners}) => {
     57            listeners = listeners.filter((listener) => listener.nodeId === node.id);
     58
     59            InspectorTest.expectEqual(listeners.length, expectedCount, `There should be ${expectedCount} event listeners.`);
     60
     61            for (let eventListener of listeners)
    6062                InspectorTest.log(`  - "${eventListener.type}"`);
    6163        });
     
    6870        description: "Test that the document has no event listeners.",
    6971        test(resolve, reject) {
    70             DOMAgent.getEventListenersForNode(node.id).then((payload) => {
    71                 InspectorTest.expectEqual(payload.listeners.length, 0, "There should be no event listeners.");
    72 
    73                 for (let eventListener of payload.listeners)
     72            DOMAgent.getEventListenersForNode(node.id).then(({listeners}) => {
     73                listeners = listeners.filter((listener) => listener.nodeId === node.id);
     74
     75                InspectorTest.expectEqual(listeners.length, 0, "There should be no event listeners.");
     76
     77                for (let eventListener of listeners)
    7478                    InspectorTest.log(eventListener.type);
    7579            }).then(resolve, reject);
  • trunk/LayoutTests/inspector/dom/getEventListenersForNode-expected.txt

    r241110 r243244  
    55-- Running test case: DOM.getEventListenersForNode.Basic
    66Event: A
    7 Node: body
     7Target: body
    88Capture: true
    99Attribute: false
    1010Handler Name: bodyA
    11 PASS: The Event Listener has a source location.
     11The Event Listener has a source location.
    1212
    1313Event: B
    14 Node: body
     14Target: body
    1515Capture: true
    1616Attribute: false
    17 PASS: The Event Listener has a source location.
     17The Event Listener has a source location.
    1818
    1919Event: E
    20 Node: div#x
     20Target: div#x
    2121Capture: false
    2222Attribute: false
    2323Handler Name: ObjectEventHandler
    24 PASS: The Event Listener has a source location.
     24The Event Listener has a source location.
    2525
    2626Event: D
    27 Node: div#x
     27Target: div#x
    2828Capture: false
    2929Attribute: false
    3030Handler Name: handleEvent
    31 PASS: The Event Listener has a source location.
     31The Event Listener has a source location.
    3232
    3333Event: C
    34 Node: div#x
     34Target: div#x
    3535Capture: false
    3636Attribute: false
    37 PASS: The Event Listener has a source location.
     37The Event Listener has a source location.
    3838
    3939Event: B
    40 Node: div#x
     40Target: div#x
    4141Capture: false
    4242Attribute: false
    4343Handler Name: xB
    4444Once: true
    45 PASS: The Event Listener has a source location.
     45The Event Listener has a source location.
    4646
    4747Event: A
    48 Node: div#x
     48Target: div#x
    4949Capture: false
    5050Attribute: false
    5151Handler Name: xA
    52 PASS: The Event Listener has a source location.
     52The Event Listener has a source location.
    5353
    5454Event: click
    55 Node: div#x
     55Target: div#x
    5656Capture: false
    5757Attribute: true
    5858Handler Name: onclick
    59 PASS: The Event Listener has a source location.
     59The Event Listener has a source location.
    6060
    6161Event: B
    62 Node: #document
     62Target: #document
    6363Capture: false
    6464Attribute: false
    6565Passive: true
    66 PASS: The Event Listener has a source location.
     66The Event Listener has a source location.
    6767
    6868Event: A
    69 Node: #document
     69Target: #document
    7070Capture: false
    7171Attribute: false
    7272Handler Name: documentA
    7373Passive: true
    74 PASS: The Event Listener has a source location.
     74The Event Listener has a source location.
     75
     76Event: load
     77Target: window
     78Capture: false
     79Attribute: true
     80Handler Name: onload
     81The Event Listener has a source location.
     82
     83Event: error
     84Target: window
     85Capture: false
     86Attribute: true
    7587
    7688
  • trunk/LayoutTests/inspector/dom/getEventListenersForNode.html

    r241110 r243244  
    2020
    2121                for (let eventListener of eventListeners) {
    22                     let node = WI.domManager.nodeForId(eventListener.nodeId);
    23 
    24 
    2522                    InspectorTest.log(`Event: ${eventListener.type}`);
    26                     InspectorTest.log(`Node: ${node.displayName}`);
     23                    if (eventListener.nodeId) {
     24                        let node = WI.domManager.nodeForId(eventListener.nodeId);
     25                        InspectorTest.log(`Target: ${node.displayName}`);
     26                    }
     27                    if (eventListener.onWindow)
     28                        InspectorTest.log("Target: window");
    2729                    InspectorTest.log(`Capture: ${eventListener.useCapture}`);
    2830                    InspectorTest.log(`Attribute: ${eventListener.isAttribute}`);
     
    3436                    if (eventListener.once)
    3537                        InspectorTest.log(`Once: ${eventListener.once}`);
    36 
    37                     InspectorTest.expectThat(eventListener.location, "The Event Listener has a source location.");
     38                    if (eventListener.location)
     39                        InspectorTest.log("The Event Listener has a source location.");
    3840
    3941                    InspectorTest.log("");
  • trunk/LayoutTests/inspector/dom/setEventListenerDisabled.html

    r236766 r243244  
    1717    function logListener() {
    1818        return DOMAgent.getEventListenersForNode(clickEventListener.nodeId).then(({listeners}) => {
     19            listeners = listeners.filter((listener) => listener.nodeId === clickEventListener.nodeId);
    1920            InspectorTest.assert(listeners.length === 1, "There should only be one event listener.");
    2021            InspectorTest.assert(listeners[0].type === "click", `There event listener should be for "click".`);
  • trunk/Source/JavaScriptCore/ChangeLog

    r243243 r243244  
     12019-03-20  Devin Rousso  <drousso@apple.com>
     2
     3        Web Inspector: DOM: include window as part of any event listener chain
     4        https://bugs.webkit.org/show_bug.cgi?id=195730
     5        <rdar://problem/48916872>
     6
     7        Reviewed by Timothy Hatcher.
     8
     9        * inspector/protocol/DOM.json:
     10        Modify `DOM.getEventListenersForNode` to not save the handler object, as that was never
     11        used by the frontend. Add an `onWindow` optional property to `DOM.EventListener` that is set
     12        when the event listener was retrieved from the `window` object.
     13
    1142019-03-20  Devin Rousso  <drousso@apple.com>
    215
  • trunk/Source/JavaScriptCore/inspector/protocol/DOM.json

    r243207 r243244  
    8888                { "name": "useCapture", "type": "boolean", "description": "<code>EventListener</code>'s useCapture." },
    8989                { "name": "isAttribute", "type": "boolean", "description": "<code>EventListener</code>'s isAttribute." },
    90                 { "name": "nodeId", "$ref": "NodeId", "description": "Target <code>DOMNode</code> id." },
     90                { "name": "nodeId", "$ref": "NodeId", "optional": true, "description": "The target <code>DOMNode</code> id if the event listener is for a node." },
     91                { "name": "onWindow", "type": "boolean", "optional": true, "description": "True if the event listener was added to the window." },
    9192                { "name": "location", "$ref": "Debugger.Location", "optional": true, "description": "Handler code location." },
    9293                { "name": "handlerName", "type": "string", "optional": true, "description": "Event handler function name." },
    93                 { "name": "handlerObject", "$ref": "Runtime.RemoteObject", "optional": true, "description": "Event handler function value." },
    9494                { "name": "passive", "type": "boolean", "optional": true, "description": "<code>EventListener</code>'s passive." },
    9595                { "name": "once", "type": "boolean", "optional": true, "description": "<code>EventListener</code>'s once." },
     
    290290            "description": "Returns event listeners relevant to the node.",
    291291            "parameters": [
    292                 { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to get listeners for." },
    293                 { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name for handler value. Handler value is not returned without this parameter specified." }
     292                { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to get listeners for." }
    294293            ],
    295294            "returns": [
  • trunk/Source/WebCore/ChangeLog

    r243243 r243244  
     12019-03-20  Devin Rousso  <drousso@apple.com>
     2
     3        Web Inspector: DOM: include window as part of any event listener chain
     4        https://bugs.webkit.org/show_bug.cgi?id=195730
     5        <rdar://problem/48916872>
     6
     7        Reviewed by Timothy Hatcher.
     8
     9        Test: inspector/dom/getEventListenersForNode.html
     10
     11        * inspector/agents/InspectorDOMAgent.h:
     12        (WebCore::EventListenerInfo::EventListenerInfo): Deleted.
     13        * inspector/agents/InspectorDOMAgent.cpp:
     14        (WebCore::InspectorDOMAgent::getEventListenersForNode):
     15        (WebCore::InspectorDOMAgent::buildObjectForEventListener):
     16        (WebCore::InspectorDOMAgent::getEventListeners): Deleted.
     17
    1182019-03-20  Devin Rousso  <drousso@apple.com>
    219
  • trunk/Source/WebCore/inspector/agents/InspectorDOMAgent.cpp

    r243207 r243244  
    877877}
    878878
    879 void InspectorDOMAgent::getEventListenersForNode(ErrorString& errorString, int nodeId, const String* objectGroup, RefPtr<JSON::ArrayOf<Inspector::Protocol::DOM::EventListener>>& listenersArray)
     879void InspectorDOMAgent::getEventListenersForNode(ErrorString& errorString, int nodeId, RefPtr<JSON::ArrayOf<Inspector::Protocol::DOM::EventListener>>& listenersArray)
    880880{
    881881    listenersArray = JSON::ArrayOf<Inspector::Protocol::DOM::EventListener>::create();
    882     Node* node = assertNode(errorString, nodeId);
     882
     883    auto* node = assertNode(errorString, nodeId);
    883884    if (!node)
    884885        return;
     886
     887    Vector<RefPtr<EventTarget>> ancestors;
     888    ancestors.append(node);
     889    for (auto* ancestor = node->parentOrShadowHostNode(); ancestor; ancestor = ancestor->parentOrShadowHostNode())
     890        ancestors.append(ancestor);
     891    if (auto* window = node->document().domWindow())
     892        ancestors.append(window);
     893
     894    struct EventListenerInfo {
     895        RefPtr<EventTarget> eventTarget;
     896        const AtomicString eventType;
     897        const EventListenerVector eventListeners;
     898    };
     899
    885900    Vector<EventListenerInfo> eventInformation;
    886     getEventListeners(node, eventInformation, true);
     901    for (size_t i = ancestors.size(); i; --i) {
     902        auto& ancestor = ancestors[i - 1];
     903        for (auto& eventType : ancestor->eventTypes()) {
     904            EventListenerVector filteredListeners;
     905            for (auto& listener : ancestor->eventListeners(eventType)) {
     906                if (listener->callback().type() == EventListener::JSEventListenerType)
     907                    filteredListeners.append(listener);
     908            }
     909            if (!filteredListeners.isEmpty())
     910                eventInformation.append({ ancestor, eventType, WTFMove(filteredListeners) });
     911        }
     912    }
    887913
    888914    auto addListener = [&] (RegisteredEventListener& listener, const EventListenerInfo& info) {
     
    892918
    893919        for (auto& inspectorEventListener : m_eventListenerEntries.values()) {
    894             if (inspectorEventListener.matches(*info.node, info.eventType, listener.callback(), listener.useCapture())) {
     920            if (inspectorEventListener.matches(*info.eventTarget, info.eventType, listener.callback(), listener.useCapture())) {
    895921                identifier = inspectorEventListener.identifier;
    896922                disabled = inspectorEventListener.disabled;
     
    901927
    902928        if (!identifier) {
    903             InspectorEventListener inspectorEventListener(m_lastEventListenerId++, *info.node, info.eventType, listener.callback(), listener.useCapture());
     929            InspectorEventListener inspectorEventListener(m_lastEventListenerId++, *info.eventTarget, info.eventType, listener.callback(), listener.useCapture());
    904930
    905931            identifier = inspectorEventListener.identifier;
     
    910936        }
    911937
    912         listenersArray->addItem(buildObjectForEventListener(listener, identifier, info.eventType, info.node, objectGroup, disabled, hasBreakpoint));
     938        listenersArray->addItem(buildObjectForEventListener(listener, identifier, *info.eventTarget, info.eventType, disabled, hasBreakpoint));
    913939    };
    914940
     
    916942    size_t eventInformationLength = eventInformation.size();
    917943    for (auto& info : eventInformation) {
    918         for (auto& listener : info.eventListenerVector) {
     944        for (auto& listener : info.eventListeners) {
    919945            if (listener->useCapture())
    920946                addListener(*listener, info);
     
    925951    for (size_t i = eventInformationLength; i; --i) {
    926952        const EventListenerInfo& info = eventInformation[i - 1];
    927         for (auto& listener : info.eventListenerVector) {
     953        for (auto& listener : info.eventListeners) {
    928954            if (!listener->useCapture())
    929955                addListener(*listener, info);
    930         }
    931     }
    932 }
    933 
    934 void InspectorDOMAgent::getEventListeners(Node* node, Vector<EventListenerInfo>& eventInformation, bool includeAncestors)
    935 {
    936     // The Node's Ancestors including self.
    937     Vector<Node*> ancestors;
    938     // Push this node as the firs element.
    939     ancestors.append(node);
    940     if (includeAncestors) {
    941         for (ContainerNode* ancestor = node->parentOrShadowHostNode(); ancestor; ancestor = ancestor->parentOrShadowHostNode())
    942             ancestors.append(ancestor);
    943     }
    944 
    945     // Nodes and their Listeners for the concerned event types (order is top to bottom)
    946     for (size_t i = ancestors.size(); i; --i) {
    947         Node* ancestor = ancestors[i - 1];
    948         EventTargetData* d = ancestor->eventTargetData();
    949         if (!d)
    950             continue;
    951         // Get the list of event types this Node is concerned with
    952         for (auto& type : d->eventListenerMap.eventTypes()) {
    953             auto& listeners = ancestor->eventListeners(type);
    954             EventListenerVector filteredListeners;
    955             filteredListeners.reserveInitialCapacity(listeners.size());
    956             for (auto& listener : listeners) {
    957                 if (listener->callback().type() == EventListener::JSEventListenerType)
    958                     filteredListeners.uncheckedAppend(listener);
    959             }
    960             if (!filteredListeners.isEmpty())
    961                 eventInformation.append(EventListenerInfo(ancestor, type, WTFMove(filteredListeners)));
    962956        }
    963957    }
     
    16611655}
    16621656
    1663 Ref<Inspector::Protocol::DOM::EventListener> InspectorDOMAgent::buildObjectForEventListener(const RegisteredEventListener& registeredEventListener, int identifier, const AtomicString& eventType, Node* node, const String* objectGroupId, bool disabled, bool hasBreakpoint)
     1657Ref<Inspector::Protocol::DOM::EventListener> InspectorDOMAgent::buildObjectForEventListener(const RegisteredEventListener& registeredEventListener, int identifier, EventTarget& eventTarget, const AtomicString& eventType, bool disabled, bool hasBreakpoint)
    16641658{
    16651659    Ref<EventListener> eventListener = registeredEventListener.callback();
    16661660
    1667     JSC::ExecState* exec = nullptr;
    1668     JSC::JSObject* handlerObject = nullptr;
    1669     JSC::JSFunction* handlerFunction = nullptr;
    16701661    String handlerName;
    16711662    int lineNumber = 0;
     
    16751666        auto& scriptListener = downcast<JSEventListener>(eventListener.get());
    16761667
     1668        Document* document = nullptr;
     1669        if (auto* scriptExecutionContext = eventTarget.scriptExecutionContext()) {
     1670            if (is<Document>(scriptExecutionContext))
     1671                document = downcast<Document>(scriptExecutionContext);
     1672        } else if (is<Node>(eventTarget))
     1673            document = &downcast<Node>(eventTarget).document();
     1674
     1675        JSC::JSObject* handlerObject = nullptr;
     1676        JSC::ExecState* exec = nullptr;
     1677
    16771678        JSC::JSLockHolder lock(scriptListener.isolatedWorld().vm());
    16781679
    1679         exec = execStateFromNode(scriptListener.isolatedWorld(), &node->document());
    1680         handlerObject = scriptListener.jsFunction(node->document());
     1680        if (document) {
     1681            handlerObject = scriptListener.jsFunction(*document);
     1682            exec = execStateFromNode(scriptListener.isolatedWorld(), document);
     1683        }
     1684
    16811685        if (handlerObject && exec) {
    1682             handlerFunction = JSC::jsDynamicCast<JSC::JSFunction*>(exec->vm(), handlerObject);
     1686            JSC::JSFunction* handlerFunction = JSC::jsDynamicCast<JSC::JSFunction*>(exec->vm(), handlerObject);
    16831687
    16841688            if (!handlerFunction) {
     
    17171721        .setUseCapture(registeredEventListener.useCapture())
    17181722        .setIsAttribute(eventListener->isAttribute())
    1719         .setNodeId(pushNodePathToFrontend(node))
    17201723        .release();
    1721     if (objectGroupId && handlerObject && exec) {
    1722         InjectedScript injectedScript = m_injectedScriptManager.injectedScriptFor(exec);
    1723         if (!injectedScript.hasNoValue())
    1724             value->setHandlerObject(injectedScript.wrapObject(handlerObject, *objectGroupId));
    1725     }
     1724    if (is<Node>(eventTarget))
     1725        value->setNodeId(pushNodePathToFrontend(&downcast<Node>(eventTarget)));
     1726    else if (is<DOMWindow>(eventTarget))
     1727        value->setOnWindow(true);
    17261728    if (!scriptID.isNull()) {
    17271729        auto location = Inspector::Protocol::Debugger::Location::create()
  • trunk/Source/WebCore/inspector/agents/InspectorDOMAgent.h

    r243207 r243244  
    7878typedef String ErrorString;
    7979
    80 struct EventListenerInfo {
    81     EventListenerInfo(Node* node, const AtomicString& eventType, EventListenerVector&& eventListenerVector)
    82         : node(node)
    83         , eventType(eventType)
    84         , eventListenerVector(WTFMove(eventListenerVector))
    85     {
    86     }
    87 
    88     Node* node;
    89     const AtomicString eventType;
    90     const EventListenerVector eventListenerVector;
    91 };
    92 
    9380class InspectorDOMAgent final : public InspectorAgentBase, public Inspector::DOMBackendDispatcherHandler {
    9481    WTF_MAKE_NONCOPYABLE(InspectorDOMAgent);
     
    130117    void getDataBindingsForNode(ErrorString&, int nodeId, RefPtr<JSON::ArrayOf<Inspector::Protocol::DOM::DataBinding>>& dataArray) override;
    131118    void getAssociatedDataForNode(ErrorString&, int nodeId, Optional<String>& associatedData) override;
    132     void getEventListenersForNode(ErrorString&, int nodeId, const WTF::String* objectGroup, RefPtr<JSON::ArrayOf<Inspector::Protocol::DOM::EventListener>>& listenersArray) override;
     119    void getEventListenersForNode(ErrorString&, int nodeId, RefPtr<JSON::ArrayOf<Inspector::Protocol::DOM::EventListener>>& listenersArray) override;
    133120    void setEventListenerDisabled(ErrorString&, int eventListenerId, bool disabled) override;
    134121    void setBreakpointForEventListener(ErrorString&, int eventListenerId) override;
     
    157144    void setInspectedNode(ErrorString&, int nodeId) override;
    158145
    159     void getEventListeners(Node*, Vector<EventListenerInfo>& listenersArray, bool includeAncestors);
    160 
    161 
    162146    // InspectorInstrumentation
    163147    int identifierForNode(Node&);
     
    249233    Ref<JSON::ArrayOf<Inspector::Protocol::DOM::Node>> buildArrayForContainerChildren(Node* container, int depth, NodeToIdMap* nodesMap);
    250234    RefPtr<JSON::ArrayOf<Inspector::Protocol::DOM::Node>> buildArrayForPseudoElements(const Element&, NodeToIdMap* nodesMap);
    251     Ref<Inspector::Protocol::DOM::EventListener> buildObjectForEventListener(const RegisteredEventListener&, int identifier, const AtomicString& eventType, Node*, const String* objectGroupId, bool disabled, bool hasBreakpoint);
     235    Ref<Inspector::Protocol::DOM::EventListener> buildObjectForEventListener(const RegisteredEventListener&, int identifier, EventTarget&, const AtomicString& eventType, bool disabled, bool hasBreakpoint);
    252236    RefPtr<Inspector::Protocol::DOM::AccessibilityProperties> buildObjectForAccessibilityProperties(Node*);
    253237    void processAccessibilityChildren(AccessibilityObject&, JSON::ArrayOf<int>&);
  • trunk/Source/WebInspectorUI/ChangeLog

    r243242 r243244  
     12019-03-20  Devin Rousso  <drousso@apple.com>
     2
     3        Web Inspector: DOM: include window as part of any event listener chain
     4        https://bugs.webkit.org/show_bug.cgi?id=195730
     5        <rdar://problem/48916872>
     6
     7        Reviewed by Timothy Hatcher.
     8
     9        Allow non-nodes (e.g. `window`) to be listed as the target of an event listener.
     10        Add support for the same concept when showing breakpoint details after pausing on a specific
     11        event listener in the Debugger/Sources navigation sidebar.
     12
     13        * UserInterface/Views/DOMNodeDetailsSidebarPanel.js:
     14        (WI.DOMNodeDetailsSidebarPanel.prototype.initialLayout):
     15        (WI.DOMNodeDetailsSidebarPanel.prototype._refreshEventListeners.generateGroupsByEvent):
     16        (WI.DOMNodeDetailsSidebarPanel.prototype._refreshEventListeners.generateGroupsByTarget): Added.
     17        (WI.DOMNodeDetailsSidebarPanel.prototype._refreshEventListeners.eventListenersCallback):
     18        (WI.DOMNodeDetailsSidebarPanel.prototype._refreshEventListeners):
     19        (WI.DOMNodeDetailsSidebarPanel.prototype._refreshEventListeners.generateGroupsByNode): Deleted.
     20
     21        * UserInterface/Views/EventListenerSectionGroup.js:
     22        (WI.EventListenerSectionGroup.prototype._targetTextOrLink): Added.
     23        (WI.EventListenerSectionGroup.prototype._nodeTextOrLink): Deleted.
     24
     25        * UserInterface/Views/DebuggerSidebarPanel.js:
     26        (WI.DebuggerSidebarPanel.prototype._addBreakpoint):
     27        (WI.DebuggerSidebarPanel.prototype._breakpointTreeOutlineDeleteTreeElement):
     28        (WI.DebuggerSidebarPanel.prototype._treeSelectionDidChange):
     29        (WI.DebuggerSidebarPanel.prototype._updatePauseReasonSection):
     30        * UserInterface/Views/DebuggerSidebarPanel.css:
     31        (.sidebar > .panel.navigation.debugger > .content > .breakpoints .tree-outline .item.event-target-window .icon): Added.
     32
     33        * UserInterface/Views/SourcesNavigationSidebarPanel.js:
     34        (WI.SourcesNavigationSidebarPanel):
     35        (WI.SourcesNavigationSidebarPanel.prototype._addBreakpoint):
     36        (WI.SourcesNavigationSidebarPanel.prototype._updatePauseReasonSection):
     37        (WI.SourcesNavigationSidebarPanel.prototype._handleTreeSelectionDidChange):
     38        * UserInterface/Views/SourcesNavigationSidebarPanel.css:
     39        (.sidebar > .panel.navigation.sources > .content > .breakpoints .tree-outline .item.event-target-window .icon): Added.
     40
     41        * Localizations/en.lproj/localizedStrings.js:
     42
    1432019-03-20  Devin Rousso  <drousso@apple.com>
    244
  • trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js

    r243220 r243244  
    516516localizedStrings["Group Media Requests"] = "Group Media Requests";
    517517localizedStrings["Group by Event"] = "Group by Event";
    518 localizedStrings["Group by Node"] = "Group by Node";
    519518localizedStrings["Group by Path"] = "Group by Path";
     519localizedStrings["Group by Target"] = "Group by Target";
    520520localizedStrings["Group by Type"] = "Group by Type";
    521521localizedStrings["Grouping Method"] = "Grouping Method";
     
    10291029localizedStrings["Tag"] = "Tag";
    10301030localizedStrings["Take snapshot"] = "Take snapshot";
     1031localizedStrings["Target"] = "Target";
    10311032localizedStrings["Template Content"] = "Template Content";
    10321033localizedStrings["Text"] = "Text";
  • trunk/Source/WebInspectorUI/UserInterface/Views/DOMNodeDetailsSidebarPanel.js

    r242820 r243244  
    9191
    9292        createOption(WI.UIString("Group by Event"), WI.DOMNodeDetailsSidebarPanel.EventListenerGroupingMethod.Event);
    93         createOption(WI.UIString("Group by Node"), WI.DOMNodeDetailsSidebarPanel.EventListenerGroupingMethod.Node);
     93        createOption(WI.UIString("Group by Target"), WI.DOMNodeDetailsSidebarPanel.EventListenerGroupingMethod.Target);
    9494
    9595        eventListenersGroupMethodSelectElement.value = this._eventListenerGroupingMethodSetting.value;
     
    342342            return;
    343343
     344        const windowTargetIdentifier = Symbol("window");
     345
    344346        function createEventListenerSection(title, eventListeners, options = {}) {
    345347            let groups = eventListeners.map((eventListener) => new WI.EventListenerSectionGroup(eventListener, options));
     
    355357            let eventListenerTypes = new Map;
    356358            for (let eventListener of eventListeners) {
    357                 eventListener.node = WI.domManager.nodeForId(eventListener.nodeId);
     359                console.assert(eventListener.nodeId || eventListener.onWindow);
     360                if (eventListener.nodeId)
     361                    eventListener.node = WI.domManager.nodeForId(eventListener.nodeId);
    358362
    359363                let eventListenersForType = eventListenerTypes.get(eventListener.type);
    360364                if (!eventListenersForType)
    361365                    eventListenerTypes.set(eventListener.type, eventListenersForType = []);
    362 
    363366                eventListenersForType.push(eventListener);
    364367            }
     
    374377        }
    375378
    376         function generateGroupsByNode(eventListeners) {
    377             let eventListenerNodes = new Map;
     379        function generateGroupsByTarget(eventListeners) {
     380            let eventListenerTargets = new Map;
    378381            for (let eventListener of eventListeners) {
    379                 eventListener.node = WI.domManager.nodeForId(eventListener.nodeId);
    380 
    381                 let eventListenersForNode = eventListenerNodes.get(eventListener.node);
    382                 if (!eventListenersForNode)
    383                     eventListenerNodes.set(eventListener.node, eventListenersForNode = []);
    384 
    385                 eventListenersForNode.push(eventListener);
     382                console.assert(eventListener.nodeId || eventListener.onWindow);
     383                if (eventListener.nodeId)
     384                    eventListener.node = WI.domManager.nodeForId(eventListener.nodeId);
     385
     386                let target = eventListener.onWindow ? windowTargetIdentifier : eventListener.node;
     387                let eventListenersForTarget = eventListenerTargets.get(target);
     388                if (!eventListenersForTarget)
     389                    eventListenerTargets.set(target, eventListenersForTarget = []);
     390                eventListenersForTarget.push(eventListener);
    386391            }
    387392
    388393            let rows = [];
     394
     395            function generateSectionForTarget(target) {
     396                let eventListenersForTarget = eventListenerTargets.get(target);
     397                if (!eventListenersForTarget)
     398                    return;
     399
     400                eventListenersForTarget.sort((a, b) => a.type.toLowerCase().extendedLocaleCompare(b.type.toLowerCase()));
     401
     402                let title = target === windowTargetIdentifier ? WI.unlocalizedString("window") : target.displayName;
     403
     404                let section = createEventListenerSection(title, eventListenersForTarget, {hideTarget: true});
     405                if (target instanceof WI.DOMNode)
     406                    WI.bindInteractionsForNodeToElement(target, section.titleElement, {ignoreClick: true});
     407                rows.push(section);
     408            }
    389409
    390410            let currentNode = domNode;
    391411            do {
    392                 let eventListenersForNode = eventListenerNodes.get(currentNode);
    393                 if (!eventListenersForNode)
    394                     continue;
    395 
    396                 eventListenersForNode.sort((a, b) => a.type.toLowerCase().extendedLocaleCompare(b.type.toLowerCase()));
    397 
    398                 let section = createEventListenerSection(currentNode.displayName, eventListenersForNode, {hideNode: true});
    399                 WI.bindInteractionsForNodeToElement(currentNode, section.titleElement, {ignoreClick: true});
    400                 rows.push(section);
     412                generateSectionForTarget(currentNode);
    401413            } while (currentNode = currentNode.parentNode);
     414
     415            generateSectionForTarget(windowTargetIdentifier);
    402416
    403417            return rows;
     
    425439                break;
    426440
    427             case WI.DOMNodeDetailsSidebarPanel.EventListenerGroupingMethod.Node:
    428                 this._eventListenersSectionGroup.rows = generateGroupsByNode.call(this, eventListeners);
     441            case WI.DOMNodeDetailsSidebarPanel.EventListenerGroupingMethod.Target:
     442                this._eventListenersSectionGroup.rows = generateGroupsByTarget.call(this, eventListeners);
    429443                break;
    430444            }
     
    951965WI.DOMNodeDetailsSidebarPanel.EventListenerGroupingMethod = {
    952966    Event: "event",
    953     Node: "node",
     967    Target: "target",
    954968};
  • trunk/Source/WebInspectorUI/UserInterface/Views/DebuggerSidebarPanel.css

    r242768 r243244  
    104104}
    105105
     106.sidebar > .panel.navigation.debugger > .content > .breakpoints .tree-outline .item.event-target-window .icon {
     107    content: url(../Images/TypeObject.svg);
     108}
     109
    106110@media (prefers-color-scheme: dark) {
    107111    .sidebar > .panel.navigation.debugger .warning-banner {
  • trunk/Source/WebInspectorUI/UserInterface/Views/DebuggerSidebarPanel.js

    r242937 r243244  
    495495            constructor = WI.EventBreakpointTreeElement;
    496496
    497             if (breakpoint.eventListener)
    498                 parentTreeElement = getDOMNodeTreeElement(breakpoint.eventListener.node);
     497            if (breakpoint.eventListener) {
     498                let eventTargetTreeElement = null;
     499                if (breakpoint.eventListener.onWindow) {
     500                    if (!DebuggerSidebarPanel.__windowEventTargetRepresentedObject)
     501                        DebuggerSidebarPanel.__windowEventTargetRepresentedObject = {__window: true};
     502
     503                    eventTargetTreeElement = this._breakpointsContentTreeOutline.findTreeElement(DebuggerSidebarPanel.__windowEventTargetRepresentedObject);
     504                    if (!eventTargetTreeElement) {
     505                        const subtitle = null;
     506                        eventTargetTreeElement = new WI.GeneralTreeElement(["event-target-window"], WI.unlocalizedString("window"), subtitle, DebuggerSidebarPanel.__windowEventTargetRepresentedObject);
     507                        this._addTreeElement(eventTargetTreeElement, parentTreeElement);
     508                    }
     509                } else if (breakpoint.eventListener.node)
     510                    eventTargetTreeElement = getDOMNodeTreeElement(breakpoint.eventListener.node);
     511                if (eventTargetTreeElement)
     512                    parentTreeElement = eventTargetTreeElement;
     513            }
    499514        } else if (breakpoint instanceof WI.URLBreakpoint) {
    500515            constructor = WI.URLBreakpointTreeElement;
     
    900915    {
    901916        console.assert(treeElement.selected);
     917
     918        if (treeElement.representedObject === DebuggerSidebarPanel.__windowEventTargetRepresentedObject) {
     919            let eventBreakpointsOnWindow = WI.domManager.eventListenerBreakpoints.filter((eventBreakpoint) => eventBreakpoint.eventListener.onWindow);
     920            for (let eventBreakpoint of eventBreakpointsOnWindow)
     921                WI.domManager.removeBreakpointForEventListener(eventBreakpoint.eventListener);
     922            return true;
     923        }
     924
    902925        console.assert(treeElement instanceof WI.ResourceTreeElement || treeElement instanceof WI.ScriptTreeElement);
    903926        if (!(treeElement instanceof WI.ResourceTreeElement) && !(treeElement instanceof WI.ScriptTreeElement))
     
    954977            || treeElement instanceof WI.EventBreakpointTreeElement
    955978            || treeElement instanceof WI.URLBreakpointTreeElement)
     979            return;
     980
     981        if (treeElement.representedObject === DebuggerSidebarPanel.__windowEventTargetRepresentedObject)
    956982            return;
    957983
     
    12321258                console.assert(eventListener.eventListenerId === pauseData.eventListenerId);
    12331259
    1234                 let ownerElementRow = new WI.DetailsSectionSimpleRow(WI.UIString("Element"), WI.linkifyNodeReference(eventListener.node));
    1235                 rows.push(ownerElementRow);
     1260                let value = null;
     1261                if (eventListener.onWindow)
     1262                    value = WI.unlocalizedString("window");
     1263                else if (eventListener.node)
     1264                    value = WI.linkifyNodeReference(eventListener.node);
     1265                if (value)
     1266                    rows.push(new WI.DetailsSectionSimpleRow(WI.UIString("Target"), value));
    12361267            }
    12371268
  • trunk/Source/WebInspectorUI/UserInterface/Views/EventListenerSectionGroup.js

    r241110 r243244  
    3535        if (!options.hideType)
    3636            rows.push(new WI.DetailsSectionSimpleRow(WI.UIString("Event"), this._eventListener.type));
    37         if (!options.hideNode)
    38             rows.push(new WI.DetailsSectionSimpleRow(WI.UIString("Node"), this._nodeTextOrLink()));
     37        if (!options.hideTarget)
     38            rows.push(new WI.DetailsSectionSimpleRow(WI.UIString("Target"), this._targetTextOrLink()));
    3939        rows.push(new WI.DetailsSectionSimpleRow(WI.UIString("Function"), this._functionTextOrLink()));
    4040
     
    6666    // Private
    6767
    68     _nodeTextOrLink()
     68    _targetTextOrLink()
    6969    {
    70         var node = this._eventListener.node;
    71         console.assert(node);
    72         if (!node)
    73             return "";
     70        if (this._eventListener.onWindow)
     71            return WI.unlocalizedString("window");
    7472
    75         if (node.nodeType() === Node.DOCUMENT_NODE)
    76             return "document";
     73        let node = this._eventListener.node;
     74        if (node)
     75            return WI.linkifyNodeReference(node);
    7776
    78         return WI.linkifyNodeReference(node);
     77        console.assert();
     78        return "";
    7979    }
    8080
  • trunk/Source/WebInspectorUI/UserInterface/Views/SourcesNavigationSidebarPanel.css

    r243225 r243244  
    142142}
    143143
     144.sidebar > .panel.navigation.sources > .content > .breakpoints .tree-outline .item.event-target-window .icon {
     145    content: url(../Images/TypeObject.svg);
     146}
     147
    144148@media (prefers-dark-interface) {
    145149    .sidebar > .panel.navigation.sources > .content > .warning-banner {
  • trunk/Source/WebInspectorUI/UserInterface/Views/SourcesNavigationSidebarPanel.js

    r243225 r243244  
    143143        this._breakpointsTreeOutline.ondelete = (treeElement) => {
    144144            console.assert(treeElement.selected);
     145
     146            if (treeElement.representedObject === SourcesNavigationSidebarPanel.__windowEventTargetRepresentedObject) {
     147                let eventBreakpointsOnWindow = WI.domManager.eventListenerBreakpoints.filter((eventBreakpoint) => eventBreakpoint.eventListener.onWindow);
     148                for (let eventBreakpoint of eventBreakpointsOnWindow)
     149                    WI.domManager.removeBreakpointForEventListener(eventBreakpoint.eventListener);
     150                return true;
     151            }
     152
    145153            console.assert(treeElement instanceof WI.ResourceTreeElement || treeElement instanceof WI.ScriptTreeElement);
    146154            if (!(treeElement instanceof WI.ResourceTreeElement) && !(treeElement instanceof WI.ScriptTreeElement))
     
    902910            constructor = WI.EventBreakpointTreeElement;
    903911
    904             if (breakpoint.eventListener)
    905                 parentTreeElement = getDOMNodeTreeElement(breakpoint.eventListener.node);
     912            if (breakpoint.eventListener) {
     913                let eventTargetTreeElement = null;
     914                if (breakpoint.eventListener.onWindow) {
     915                    if (!SourcesNavigationSidebarPanel.__windowEventTargetRepresentedObject)
     916                        SourcesNavigationSidebarPanel.__windowEventTargetRepresentedObject = {__window: true};
     917
     918                    eventTargetTreeElement = this._breakpointsTreeOutline.findTreeElement(SourcesNavigationSidebarPanel.__windowEventTargetRepresentedObject);
     919                    if (!eventTargetTreeElement) {
     920                        const subtitle = null;
     921                        eventTargetTreeElement = new WI.GeneralTreeElement(["event-target-window"], WI.unlocalizedString("window"), subtitle, SourcesNavigationSidebarPanel.__windowEventTargetRepresentedObject);
     922                        this._insertDebuggerTreeElement(eventTargetTreeElement, parentTreeElement);
     923                    }
     924                } else if (breakpoint.eventListener.node)
     925                    eventTargetTreeElement = getDOMNodeTreeElement(breakpoint.eventListener.node);
     926                if (eventTargetTreeElement)
     927                    parentTreeElement = eventTargetTreeElement;
     928            }
    906929        } else if (breakpoint instanceof WI.URLBreakpoint) {
    907930            constructor = WI.URLBreakpointTreeElement;
     
    12561279                console.assert(eventListener.eventListenerId === pauseData.eventListenerId);
    12571280
    1258                 let ownerElementRow = new WI.DetailsSectionSimpleRow(WI.UIString("Element"), WI.linkifyNodeReference(eventListener.node));
    1259                 rows.push(ownerElementRow);
     1281                let value = null;
     1282                if (eventListener.onWindow)
     1283                    value = WI.unlocalizedString("window");
     1284                else if (eventListener.node)
     1285                    value = WI.linkifyNodeReference(eventListener.node);
     1286                if (value)
     1287                    rows.push(new WI.DetailsSectionSimpleRow(WI.UIString("Target"), value));
    12601288            }
    12611289
     
    13871415            || treeElement instanceof WI.EventBreakpointTreeElement
    13881416            || treeElement instanceof WI.URLBreakpointTreeElement)
     1417            return;
     1418
     1419        if (treeElement.representedObject === SourcesNavigationSidebarPanel.__windowEventTargetRepresentedObject)
    13891420            return;
    13901421
Note: See TracChangeset for help on using the changeset viewer.