Bug 1561707 - Support excluding listeners from the event collector list. r=gl
authorLogan Smyth <loganfsmyth@gmail.com>
Thu, 27 Jun 2019 06:23:54 +0000
changeset 540467 7dd6e190f8af367c05c4048a2e53c90e55574ab5
parent 540466 405a1aaa16c5b874f6544930e25b2c0f6c4134b6
child 540468 61a027216c5e5d7aef93272072fdda02e6578809
push id11529
push userarchaeopteryx@coole-files.de
push dateThu, 04 Jul 2019 15:22:33 +0000
treeherdermozilla-beta@ebb510a784b8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgl
bugs1561707
milestone69.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1561707 - Support excluding listeners from the event collector list. r=gl Differential Revision: https://phabricator.services.mozilla.com/D36076
devtools/server/actors/inspector/constants.js
devtools/server/actors/inspector/event-collector.js
devtools/server/actors/inspector/moz.build
devtools/server/actors/inspector/walker.js
new file mode 100644
--- /dev/null
+++ b/devtools/server/actors/inspector/constants.js
@@ -0,0 +1,17 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+/**
+ * Any event listener flagged with this symbol will not be considered when
+ * the EventCollector class enumerates listeners for nodes. For example:
+ *
+ *   const someListener = () => {};
+ *   someListener[EXCLUDED_LISTENER] = true;
+ *   eventListenerService.addSystemEventListener(node, "event", someListener);
+ */
+const EXCLUDED_LISTENER = Symbol("event-collector-excluded-listener");
+
+exports.EXCLUDED_LISTENER = EXCLUDED_LISTENER;
--- a/devtools/server/actors/inspector/event-collector.js
+++ b/devtools/server/actors/inspector/event-collector.js
@@ -12,16 +12,19 @@ const Services = require("Services");
 const {
   isAfterPseudoElement,
   isBeforePseudoElement,
   isMarkerPseudoElement,
   isNativeAnonymous,
 } = require("devtools/shared/layout/utils");
 const Debugger = require("Debugger");
 const ReplayInspector = require("devtools/server/actors/replay/inspector");
+const {
+  EXCLUDED_LISTENER,
+} = require("devtools/server/actors/inspector/constants");
 
 // eslint-disable-next-line
 const JQUERY_LIVE_REGEX = /return typeof \w+.*.event\.triggered[\s\S]*\.event\.(dispatch|handle).*arguments/;
 
 const REACT_EVENT_NAMES = [
   "onAbort",
   "onAnimationEnd",
   "onAnimationIteration",
@@ -248,27 +251,34 @@ class MainEventCollector {
    *       on C++ rather than JavaScript.
    *
    * @param  {DOMNode} node
    *         The node for which we want to get unfiltered event listeners.
    * @return {Array}
    *         An array of unfiltered event listeners or an empty array
    */
   getDOMListeners(node) {
+    let listeners;
     if (typeof node.nodeName !== "undefined" && node.nodeName.toLowerCase() === "html") {
       const winListeners =
         Services.els.getListenerInfoFor(node.ownerGlobal) || [];
       const docElementListeners =
         Services.els.getListenerInfoFor(node) || [];
       const docListeners =
         Services.els.getListenerInfoFor(node.parentNode) || [];
 
-      return [...winListeners, ...docElementListeners, ...docListeners];
+      listeners = [...winListeners, ...docElementListeners, ...docListeners];
+    } else {
+      listeners = Services.els.getListenerInfoFor(node) || [];
     }
-    return Services.els.getListenerInfoFor(node) || [];
+
+    return listeners.filter(listener => {
+      const obj = this.unwrap(listener.listenerObject);
+      return !obj || !obj[EXCLUDED_LISTENER];
+    });
   }
 
   getJQuery(node) {
     if (Cu.isDeadWrapper(node)) {
       return null;
     }
 
     const global = this.unwrap(node.ownerGlobal);
--- a/devtools/server/actors/inspector/moz.build
+++ b/devtools/server/actors/inspector/moz.build
@@ -1,15 +1,16 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DevToolsModules(
+  'constants.js',
   'css-logic.js',
   'custom-element-watcher.js',
   'document-walker.js',
   'event-collector.js',
   'inspector.js',
   'node.js',
   'utils.js',
   'walker.js',
--- a/devtools/server/actors/inspector/walker.js
+++ b/devtools/server/actors/inspector/walker.js
@@ -7,16 +7,19 @@
 const {Cc, Ci, Cu} = require("chrome");
 
 const Services = require("Services");
 const protocol = require("devtools/shared/protocol");
 const {walkerSpec} = require("devtools/shared/specs/inspector");
 const {LongStringActor} = require("devtools/server/actors/string");
 const InspectorUtils = require("InspectorUtils");
 const ReplayInspector = require("devtools/server/actors/replay/inspector");
+const {
+  EXCLUDED_LISTENER,
+} = require("devtools/server/actors/inspector/constants");
 
 loader.lazyRequireGetter(this, "getFrameElement", "devtools/shared/layout/utils", true);
 loader.lazyRequireGetter(this, "isAfterPseudoElement", "devtools/shared/layout/utils", true);
 loader.lazyRequireGetter(this, "isAnonymous", "devtools/shared/layout/utils", true);
 loader.lazyRequireGetter(this, "isBeforePseudoElement", "devtools/shared/layout/utils", true);
 loader.lazyRequireGetter(this, "isDirectShadowHostChild", "devtools/shared/layout/utils", true);
 loader.lazyRequireGetter(this, "isMarkerPseudoElement", "devtools/shared/layout/utils", true);
 loader.lazyRequireGetter(this, "isNativeAnonymous", "devtools/shared/layout/utils", true);
@@ -155,19 +158,24 @@ var WalkerActor = protocol.ActorClassWit
     this._orphaned = new Set();
 
     // The client can tell the walker that it is interested in a node
     // even when it is orphaned with the `retainNode` method.  This
     // list contains orphaned nodes that were so retained.
     this._retainedOrphans = new Set();
 
     this.onNodeInserted = this.onNodeInserted.bind(this);
+    this.onNodeInserted[EXCLUDED_LISTENER] = true;
     this.onNodeRemoved = this.onNodeRemoved.bind(this);
+    this.onNodeRemoved[EXCLUDED_LISTENER] = true;
     this.onAttributeModified = this.onAttributeModified.bind(this);
+    this.onAttributeModified[EXCLUDED_LISTENER] = true;
     this.onNodeRemovedFromDocument = this.onNodeRemovedFromDocument.bind(this);
+    this.onNodeRemovedFromDocument[EXCLUDED_LISTENER] = true;
+
     this.onMutations = this.onMutations.bind(this);
     this.onSlotchange = this.onSlotchange.bind(this);
     this.onShadowrootattached = this.onShadowrootattached.bind(this);
     this.onFrameLoad = this.onFrameLoad.bind(this);
     this.onFrameUnload = this.onFrameUnload.bind(this);
     this.onCustomElementDefined = this.onCustomElementDefined.bind(this);
     this._throttledEmitNewMutations = throttle(this._emitNewMutations.bind(this),
       MUTATIONS_THROTTLING_DELAY);