Bug 1482070 - Move ConsoleServiceListener to its own file. r=Honza
authorAlexandre Poirot <poirot.alex@gmail.com>
Tue, 14 Aug 2018 08:05:30 -0700
changeset 432395 71e21bc8aa29936516dc0f2574a6bcad803ac035
parent 432394 c6c757dc5f0b12d4f7eb506aefd13ab69e1b0649
child 432396 985b16e1735621130527b12af5adb3505496e048
push id106726
push userapoirot@mozilla.com
push dateMon, 20 Aug 2018 16:07:22 +0000
treeherdermozilla-inbound@68bff34cbddd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersHonza
bugs1482070
milestone63.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 1482070 - Move ConsoleServiceListener to its own file. r=Honza Summary: Depends On D3597 Reviewers: Honza! Tags: #secure-revision Bug #: 1482070 Differential Revision: https://phabricator.services.mozilla.com/D3598 MozReview-Commit-ID: 7jdh33iKfgU
devtools/server/actors/webconsole.js
devtools/server/actors/webconsole/listeners.js
devtools/server/actors/webconsole/listeners/console-service.js
devtools/server/actors/webconsole/listeners/moz.build
devtools/shared/webconsole/test/test_cached_messages.html
--- a/devtools/server/actors/webconsole.js
+++ b/devtools/server/actors/webconsole.js
@@ -38,17 +38,17 @@ loader.lazyRequireGetter(this, "EventEmi
 
 // Overwrite implemented listeners for workers so that we don't attempt
 // to load an unsupported module.
 if (isWorker) {
   loader.lazyRequireGetter(this, "ConsoleAPIListener", "devtools/server/actors/webconsole/worker-listeners", true);
   loader.lazyRequireGetter(this, "ConsoleServiceListener", "devtools/server/actors/webconsole/worker-listeners", true);
 } else {
   loader.lazyRequireGetter(this, "ConsoleAPIListener", "devtools/server/actors/webconsole/listeners", true);
-  loader.lazyRequireGetter(this, "ConsoleServiceListener", "devtools/server/actors/webconsole/listeners", true);
+  loader.lazyRequireGetter(this, "ConsoleServiceListener", "devtools/server/actors/webconsole/listeners/console-service", true);
   loader.lazyRequireGetter(this, "ConsoleReflowListener", "devtools/server/actors/webconsole/listeners", true);
   loader.lazyRequireGetter(this, "ContentProcessListener", "devtools/server/actors/webconsole/listeners", true);
   loader.lazyRequireGetter(this, "DocumentEventsListener", "devtools/server/actors/webconsole/listeners", true);
 }
 
 function isObject(value) {
   return Object(value) === value;
 }
--- a/devtools/server/actors/webconsole/listeners.js
+++ b/devtools/server/actors/webconsole/listeners.js
@@ -10,176 +10,16 @@ const Services = require("Services");
 const ChromeUtils = require("ChromeUtils");
 const {CONSOLE_WORKER_IDS, WebConsoleUtils} = require("devtools/server/actors/webconsole/utils");
 
 loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
 
 // Process script used to forward console calls from content processes to parent process
 const CONTENT_PROCESS_SCRIPT = "resource://devtools/server/actors/webconsole/content-process-forward.js";
 
-// The page errors listener
-
-/**
- * The nsIConsoleService listener. This is used to send all of the console
- * messages (JavaScript, CSS and more) to the remote Web Console instance.
- *
- * @constructor
- * @param nsIDOMWindow [window]
- *        Optional - the window object for which we are created. This is used
- *        for filtering out messages that belong to other windows.
- * @param object listener
- *        The listener object must have one method:
- *        - onConsoleServiceMessage(). This method is invoked with one argument,
- *        the nsIConsoleMessage, whenever a relevant message is received.
- */
-function ConsoleServiceListener(window, listener) {
-  this.window = window;
-  this.listener = listener;
-}
-exports.ConsoleServiceListener = ConsoleServiceListener;
-
-ConsoleServiceListener.prototype =
-{
-  QueryInterface: ChromeUtils.generateQI([Ci.nsIConsoleListener]),
-
-  /**
-   * The content window for which we listen to page errors.
-   * @type nsIDOMWindow
-   */
-  window: null,
-
-  /**
-   * The listener object which is notified of messages from the console service.
-   * @type object
-   */
-  listener: null,
-
-  /**
-   * Initialize the nsIConsoleService listener.
-   */
-  init: function() {
-    Services.console.registerListener(this);
-  },
-
-  /**
-   * The nsIConsoleService observer. This method takes all the script error
-   * messages belonging to the current window and sends them to the remote Web
-   * Console instance.
-   *
-   * @param nsIConsoleMessage message
-   *        The message object coming from the nsIConsoleService.
-   */
-  observe: function(message) {
-    if (!this.listener) {
-      return;
-    }
-
-    if (this.window) {
-      if (!(message instanceof Ci.nsIScriptError) ||
-          !message.outerWindowID ||
-          !this.isCategoryAllowed(message.category)) {
-        return;
-      }
-
-      const errorWindow = Services.wm.getOuterWindowWithId(message.outerWindowID);
-      if (!errorWindow || !isWindowIncluded(this.window, errorWindow)) {
-        return;
-      }
-    }
-
-    this.listener.onConsoleServiceMessage(message);
-  },
-
-  /**
-   * Check if the given message category is allowed to be tracked or not.
-   * We ignore chrome-originating errors as we only care about content.
-   *
-   * @param string category
-   *        The message category you want to check.
-   * @return boolean
-   *         True if the category is allowed to be logged, false otherwise.
-   */
-  isCategoryAllowed: function(category) {
-    if (!category) {
-      return false;
-    }
-
-    switch (category) {
-      case "XPConnect JavaScript":
-      case "component javascript":
-      case "chrome javascript":
-      case "chrome registration":
-      case "XBL":
-      case "XBL Prototype Handler":
-      case "XBL Content Sink":
-      case "xbl javascript":
-        return false;
-    }
-
-    return true;
-  },
-
-  /**
-   * Get the cached page errors for the current inner window and its (i)frames.
-   *
-   * @param boolean [includePrivate=false]
-   *        Tells if you want to also retrieve messages coming from private
-   *        windows. Defaults to false.
-   * @return array
-   *         The array of cached messages. Each element is an nsIScriptError or
-   *         an nsIConsoleMessage
-   */
-  getCachedMessages: function(includePrivate = false) {
-    const errors = Services.console.getMessageArray() || [];
-
-    // if !this.window, we're in a browser console. Still need to filter
-    // private messages.
-    if (!this.window) {
-      return errors.filter((error) => {
-        if (error instanceof Ci.nsIScriptError) {
-          if (!includePrivate && error.isFromPrivateWindow) {
-            return false;
-          }
-        }
-
-        return true;
-      });
-    }
-
-    const ids = WebConsoleUtils.getInnerWindowIDsForFrames(this.window);
-
-    return errors.filter((error) => {
-      if (error instanceof Ci.nsIScriptError) {
-        if (!includePrivate && error.isFromPrivateWindow) {
-          return false;
-        }
-        if (ids &&
-            (!ids.includes(error.innerWindowID) ||
-             !this.isCategoryAllowed(error.category))) {
-          return false;
-        }
-      } else if (ids && ids[0]) {
-        // If this is not an nsIScriptError and we need to do window-based
-        // filtering we skip this message.
-        return false;
-      }
-
-      return true;
-    });
-  },
-
-  /**
-   * Remove the nsIConsoleService listener.
-   */
-  destroy: function() {
-    Services.console.unregisterListener(this);
-    this.listener = this.window = null;
-  },
-};
-
 // The window.console API observer
 
 /**
  * The window.console API observer. This allows the window.console API messages
  * to be sent to the remote Web Console instance.
  *
  * @constructor
  * @param nsIDOMWindow window
copy from devtools/server/actors/webconsole/listeners.js
copy to devtools/server/actors/webconsole/listeners/console-service.js
--- a/devtools/server/actors/webconsole/listeners.js
+++ b/devtools/server/actors/webconsole/listeners/console-service.js
@@ -1,24 +1,19 @@
 /* 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";
 
-const {Cc, Ci, components} = require("chrome");
+const {Ci} = require("chrome");
 const {isWindowIncluded} = require("devtools/shared/layout/utils");
 const Services = require("Services");
 const ChromeUtils = require("ChromeUtils");
-const {CONSOLE_WORKER_IDS, WebConsoleUtils} = require("devtools/server/actors/webconsole/utils");
-
-loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
-
-// Process script used to forward console calls from content processes to parent process
-const CONTENT_PROCESS_SCRIPT = "resource://devtools/server/actors/webconsole/content-process-forward.js";
+const {WebConsoleUtils} = require("devtools/server/actors/webconsole/utils");
 
 // The page errors listener
 
 /**
  * The nsIConsoleService listener. This is used to send all of the console
  * messages (JavaScript, CSS and more) to the remote Web Console instance.
  *
  * @constructor
@@ -169,399 +164,8 @@ ConsoleServiceListener.prototype =
   /**
    * Remove the nsIConsoleService listener.
    */
   destroy: function() {
     Services.console.unregisterListener(this);
     this.listener = this.window = null;
   },
 };
-
-// The window.console API observer
-
-/**
- * The window.console API observer. This allows the window.console API messages
- * to be sent to the remote Web Console instance.
- *
- * @constructor
- * @param nsIDOMWindow window
- *        Optional - the window object for which we are created. This is used
- *        for filtering out messages that belong to other windows.
- * @param object owner
- *        The owner object must have the following methods:
- *        - onConsoleAPICall(). This method is invoked with one argument, the
- *        Console API message that comes from the observer service, whenever
- *        a relevant console API call is received.
- * @param object filteringOptions
- *        Optional - The filteringOptions that this listener should listen to:
- *        - addonId: filter console messages based on the addonId.
- */
-function ConsoleAPIListener(window, owner, {addonId} = {}) {
-  this.window = window;
-  this.owner = owner;
-  this.addonId = addonId;
-}
-exports.ConsoleAPIListener = ConsoleAPIListener;
-
-ConsoleAPIListener.prototype =
-{
-  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),
-
-  /**
-   * The content window for which we listen to window.console API calls.
-   * @type nsIDOMWindow
-   */
-  window: null,
-
-  /**
-   * The owner object which is notified of window.console API calls. It must
-   * have a onConsoleAPICall method which is invoked with one argument: the
-   * console API call object that comes from the observer service.
-   *
-   * @type object
-   * @see WebConsoleActor
-   */
-  owner: null,
-
-  /**
-   * The addonId that we listen for. If not null then only messages from this
-   * console will be returned.
-   */
-  addonId: null,
-
-  /**
-   * Initialize the window.console API observer.
-   */
-  init: function() {
-    // Note that the observer is process-wide. We will filter the messages as
-    // needed, see CAL_observe().
-    Services.obs.addObserver(this, "console-api-log-event");
-  },
-
-  /**
-   * The console API message observer. When messages are received from the
-   * observer service we forward them to the remote Web Console instance.
-   *
-   * @param object message
-   *        The message object receives from the observer service.
-   * @param string topic
-   *        The message topic received from the observer service.
-   */
-  observe: function(message, topic) {
-    if (!this.owner) {
-      return;
-    }
-
-    // Here, wrappedJSObject is not a security wrapper but a property defined
-    // by the XPCOM component which allows us to unwrap the XPCOM interface and
-    // access the underlying JSObject.
-    const apiMessage = message.wrappedJSObject;
-
-    if (!this.isMessageRelevant(apiMessage)) {
-      return;
-    }
-
-    this.owner.onConsoleAPICall(apiMessage);
-  },
-
-  /**
-   * Given a message, return true if this window should show it and false
-   * if it should be ignored.
-   *
-   * @param message
-   *        The message from the Storage Service
-   * @return bool
-   *         Do we care about this message?
-   */
-  isMessageRelevant: function(message) {
-    const workerType = WebConsoleUtils.getWorkerType(message);
-
-    if (this.window && workerType === "ServiceWorker") {
-      // For messages from Service Workers, message.ID is the
-      // scope, which can be used to determine whether it's controlling
-      // a window.
-      const scope = message.ID;
-
-      if (!this.window.shouldReportForServiceWorkerScope(scope)) {
-        return false;
-      }
-    }
-
-    if (this.window && !workerType) {
-      const msgWindow = Services.wm.getCurrentInnerWindowWithId(message.innerID);
-      if (!msgWindow || !isWindowIncluded(this.window, msgWindow)) {
-        // Not the same window!
-        return false;
-      }
-    }
-
-    if (this.addonId) {
-      // ConsoleAPI.jsm messages contains a consoleID, (and it is currently
-      // used in Addon SDK add-ons), the standard 'console' object
-      // (which is used in regular webpages and in WebExtensions pages)
-      // contains the originAttributes of the source document principal.
-
-      // Filtering based on the originAttributes used by
-      // the Console API object.
-      if (message.addonId == this.addonId) {
-        return true;
-      }
-
-      // Filtering based on the old-style consoleID property used by
-      // the legacy Console JSM module.
-      if (message.consoleID && message.consoleID == `addon/${this.addonId}`) {
-        return true;
-      }
-
-      return false;
-    }
-
-    return true;
-  },
-
-  /**
-   * Get the cached messages for the current inner window and its (i)frames.
-   *
-   * @param boolean [includePrivate=false]
-   *        Tells if you want to also retrieve messages coming from private
-   *        windows. Defaults to false.
-   * @return array
-   *         The array of cached messages.
-   */
-  getCachedMessages: function(includePrivate = false) {
-    let messages = [];
-    const ConsoleAPIStorage = Cc["@mozilla.org/consoleAPI-storage;1"]
-                              .getService(Ci.nsIConsoleAPIStorage);
-
-    // if !this.window, we're in a browser console. Retrieve all events
-    // for filtering based on privacy.
-    if (!this.window) {
-      messages = ConsoleAPIStorage.getEvents();
-    } else {
-      const ids = WebConsoleUtils.getInnerWindowIDsForFrames(this.window);
-      ids.forEach((id) => {
-        messages = messages.concat(ConsoleAPIStorage.getEvents(id));
-      });
-    }
-
-    CONSOLE_WORKER_IDS.forEach((id) => {
-      messages = messages.concat(ConsoleAPIStorage.getEvents(id));
-    });
-
-    messages = messages.filter(msg => {
-      return this.isMessageRelevant(msg);
-    });
-
-    if (includePrivate) {
-      return messages;
-    }
-
-    return messages.filter((m) => !m.private);
-  },
-
-  /**
-   * Destroy the console API listener.
-   */
-  destroy: function() {
-    Services.obs.removeObserver(this, "console-api-log-event");
-    this.window = this.owner = null;
-  },
-};
-
-/**
- * A ReflowObserver that listens for reflow events from the page.
- * Implements nsIReflowObserver.
- *
- * @constructor
- * @param object window
- *        The window for which we need to track reflow.
- * @param object owner
- *        The listener owner which needs to implement:
- *        - onReflowActivity(reflowInfo)
- */
-
-function ConsoleReflowListener(window, listener) {
-  this.docshell = window.docShell;
-  this.listener = listener;
-  this.docshell.addWeakReflowObserver(this);
-}
-
-exports.ConsoleReflowListener = ConsoleReflowListener;
-
-ConsoleReflowListener.prototype =
-{
-  QueryInterface: ChromeUtils.generateQI([Ci.nsIReflowObserver,
-                                          Ci.nsISupportsWeakReference]),
-  docshell: null,
-  listener: null,
-
-  /**
-   * Forward reflow event to listener.
-   *
-   * @param DOMHighResTimeStamp start
-   * @param DOMHighResTimeStamp end
-   * @param boolean interruptible
-   */
-  sendReflow: function(start, end, interruptible) {
-    const frame = components.stack.caller.caller;
-
-    let filename = frame ? frame.filename : null;
-
-    if (filename) {
-      // Because filename could be of the form "xxx.js -> xxx.js -> xxx.js",
-      // we only take the last part.
-      filename = filename.split(" ").pop();
-    }
-
-    this.listener.onReflowActivity({
-      interruptible: interruptible,
-      start: start,
-      end: end,
-      sourceURL: filename,
-      sourceLine: frame ? frame.lineNumber : null,
-      functionName: frame ? frame.name : null
-    });
-  },
-
-  /**
-   * On uninterruptible reflow
-   *
-   * @param DOMHighResTimeStamp start
-   * @param DOMHighResTimeStamp end
-   */
-  reflow: function(start, end) {
-    this.sendReflow(start, end, false);
-  },
-
-  /**
-   * On interruptible reflow
-   *
-   * @param DOMHighResTimeStamp start
-   * @param DOMHighResTimeStamp end
-   */
-  reflowInterruptible: function(start, end) {
-    this.sendReflow(start, end, true);
-  },
-
-  /**
-   * Unregister listener.
-   */
-  destroy: function() {
-    this.docshell.removeWeakReflowObserver(this);
-    this.listener = this.docshell = null;
-  },
-};
-
-/**
- * Forward console message calls from content processes to the parent process.
- * Used by Browser console and toolbox to see messages from all processes.
- *
- * @constructor
- * @param object owner
- *        The listener owner which needs to implement:
- *        - onConsoleAPICall(message)
- */
-function ContentProcessListener(listener) {
-  this.listener = listener;
-
-  Services.ppmm.addMessageListener("Console:Log", this);
-  Services.ppmm.loadProcessScript(CONTENT_PROCESS_SCRIPT, true);
-}
-
-exports.ContentProcessListener = ContentProcessListener;
-
-ContentProcessListener.prototype = {
-  receiveMessage(message) {
-    const logMsg = message.data;
-    logMsg.wrappedJSObject = logMsg;
-    this.listener.onConsoleAPICall(logMsg);
-  },
-
-  destroy() {
-    // Tell the content processes to stop listening and forwarding messages
-    Services.ppmm.broadcastAsyncMessage("DevTools:StopForwardingContentProcessMessage");
-
-    Services.ppmm.removeMessageListener("Console:Log", this);
-    Services.ppmm.removeDelayedProcessScript(CONTENT_PROCESS_SCRIPT);
-
-    this.listener = null;
-  }
-};
-
-/**
- * Forward `DOMContentLoaded` and `load` events with precise timing
- * of when events happened according to window.performance numbers.
- *
- * @constructor
- * @param object console
- *        The web console actor.
- */
-function DocumentEventsListener(console) {
-  this.console = console;
-
-  this.onWindowReady = this.onWindowReady.bind(this);
-  this.onContentLoaded = this.onContentLoaded.bind(this);
-  this.onLoad = this.onLoad.bind(this);
-  this.listen();
-}
-
-exports.DocumentEventsListener = DocumentEventsListener;
-
-DocumentEventsListener.prototype = {
-  listen() {
-    EventEmitter.on(this.console.parentActor, "window-ready", this.onWindowReady);
-    this.onWindowReady({ window: this.console.window, isTopLevel: true });
-  },
-
-  onWindowReady({ window, isTopLevel }) {
-    // Ignore iframes
-    if (!isTopLevel) {
-      return;
-    }
-
-    const { readyState } = window.document;
-    if (readyState != "interactive" && readyState != "complete") {
-      window.addEventListener("DOMContentLoaded", this.onContentLoaded, { once: true });
-    } else {
-      this.onContentLoaded({ target: window.document });
-    }
-    if (readyState != "complete") {
-      window.addEventListener("load", this.onLoad, { once: true });
-    } else {
-      this.onLoad({ target: window.document });
-    }
-  },
-
-  onContentLoaded(event) {
-    const window = event.target.defaultView;
-    const packet = {
-      from: this.console.actorID,
-      type: "documentEvent",
-      name: "dom-interactive",
-      // milliseconds since the UNIX epoch, when the parser finished its work
-      // on the main document, that is when its Document.readyState changes to
-      // 'interactive' and the corresponding readystatechange event is thrown
-      time: window.performance.timing.domInteractive
-    };
-    this.console.conn.send(packet);
-  },
-
-  onLoad(event) {
-    const window = event.target.defaultView;
-    const packet = {
-      from: this.console.actorID,
-      type: "documentEvent",
-      name: "dom-complete",
-      // milliseconds since the UNIX epoch, when the parser finished its work
-      // on the main document, that is when its Document.readyState changes to
-      // 'complete' and the corresponding readystatechange event is thrown
-      time: window.performance.timing.domComplete
-    };
-    this.console.conn.send(packet);
-  },
-
-  destroy() {
-    EventEmitter.off(this.console.parentActor, "window-ready", this.onWindowReady);
-
-    this.listener = null;
-  }
-};
-
--- a/devtools/server/actors/webconsole/listeners/moz.build
+++ b/devtools/server/actors/webconsole/listeners/moz.build
@@ -1,9 +1,10 @@
 # -*- 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(
     'console-progress.js',
+    'console-service.js',
 )
--- a/devtools/shared/webconsole/test/test_cached_messages.html
+++ b/devtools/shared/webconsole/test/test_cached_messages.html
@@ -92,17 +92,19 @@ function doConsoleCalls() {
       arguments: ["foobarBaz-warn", { type: "object", actor: /[a-z]/ }],
     },
   ];
 }
 </script>
 
 <script class="testbody" type="text/javascript">
 SimpleTest.waitForExplicitFinish();
-var {ConsoleServiceListener, ConsoleAPIListener} =
+var {ConsoleServiceListener} =
+  require("devtools/server/actors/webconsole/listeners/console-service");
+var {ConsoleAPIListener} =
   require("devtools/server/actors/webconsole/listeners");
 
 let consoleAPIListener, consoleServiceListener;
 let consoleAPICalls = 0;
 let pageErrors = 0;
 
 let handlers = {
   onConsoleAPICall: function onConsoleAPICall(message) {