Bug 1482070 - Move ConsoleAPIListener to its own file. r=Honza
authorAlexandre Poirot <poirot.alex@gmail.com>
Tue, 14 Aug 2018 08:07:28 -0700
changeset 432396 985b16e1735621130527b12af5adb3505496e048
parent 432395 71e21bc8aa29936516dc0f2574a6bcad803ac035
child 432397 e6cb4956e53f6b9bc7c2e70ff7a5d3f44065c66d
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 ConsoleAPIListener to its own file. r=Honza Summary: Depends On D3598 Reviewers: Honza! Tags: #secure-revision Bug #: 1482070 Differential Revision: https://phabricator.services.mozilla.com/D3599 MozReview-Commit-ID: k3KVzlmXOR
devtools/server/actors/addon/console.js
devtools/server/actors/webconsole.js
devtools/server/actors/webconsole/listeners.js
devtools/server/actors/webconsole/listeners/console-api.js
devtools/server/actors/webconsole/listeners/moz.build
devtools/shared/tests/unit/test_console_filtering.js
devtools/shared/tests/unit/test_eventemitter_basic.js
devtools/shared/tests/unit/test_eventemitter_static.js
devtools/shared/webconsole/test/test_cached_messages.html
--- a/devtools/server/actors/addon/console.js
+++ b/devtools/server/actors/addon/console.js
@@ -1,15 +1,15 @@
 /* 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";
 
-var { ConsoleAPIListener } = require("devtools/server/actors/webconsole/listeners");
+var { ConsoleAPIListener } = require("devtools/server/actors/webconsole/listeners/console-api");
 var { update } = require("devtools/shared/DevToolsUtils");
 
 loader.lazyRequireGetter(this, "WebConsoleActor", "devtools/server/actors/webconsole", true);
 
 const { extend } = require("devtools/shared/extend");
 const { ActorClassWithSpec, Actor } = require("devtools/shared/protocol");
 const { addonConsoleSpec } = require("devtools/shared/specs/addon/console");
 
--- a/devtools/server/actors/webconsole.js
+++ b/devtools/server/actors/webconsole.js
@@ -37,17 +37,17 @@ loader.lazyRequireGetter(this, "Environm
 loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
 
 // 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, "ConsoleAPIListener", "devtools/server/actors/webconsole/listeners/console-api", 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,206 +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 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.
copy from devtools/server/actors/webconsole/listeners.js
copy to devtools/server/actors/webconsole/listeners/console-api.js
--- a/devtools/server/actors/webconsole/listeners.js
+++ b/devtools/server/actors/webconsole/listeners/console-api.js
@@ -1,25 +1,20 @@
 /* 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 {Cc, 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";
-
 // 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
@@ -200,208 +195,8 @@ ConsoleAPIListener.prototype =
   /**
    * 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,10 +1,11 @@
 # -*- 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-api.js',
     'console-progress.js',
     'console-service.js',
 )
--- a/devtools/shared/tests/unit/test_console_filtering.js
+++ b/devtools/shared/tests/unit/test_console_filtering.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const { console, ConsoleAPI } = require("resource://gre/modules/Console.jsm");
-const { ConsoleAPIListener } = require("devtools/server/actors/webconsole/listeners");
+const { ConsoleAPIListener } = require("devtools/server/actors/webconsole/listeners/console-api");
 
 var seenMessages = 0;
 var seenTypes = 0;
 
 var callback = {
   onConsoleAPICall: function(message) {
     if (message.consoleID && message.consoleID == "addon/foo") {
       Assert.equal(message.level, "warn");
--- a/devtools/shared/tests/unit/test_eventemitter_basic.js
+++ b/devtools/shared/tests/unit/test_eventemitter_basic.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const {
   ConsoleAPIListener
-} = require("devtools/server/actors/webconsole/listeners");
+} = require("devtools/server/actors/webconsole/listeners/console-api");
 const EventEmitter = require("devtools/shared/event-emitter");
 const hasMethod = (target, method) =>
   method in target && typeof target[method] === "function";
 
 /**
  * Each method of this object is a test; tests can be synchronous or asynchronous:
  *
  * 1. Plain functions are synchronous tests.
--- a/devtools/shared/tests/unit/test_eventemitter_static.js
+++ b/devtools/shared/tests/unit/test_eventemitter_static.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const {
   ConsoleAPIListener
-} = require("devtools/server/actors/webconsole/listeners");
+} = require("devtools/server/actors/webconsole/listeners/console-api");
 const { on, once, off, emit, count, handler } = require("devtools/shared/event-emitter");
 
 const pass = (message) => ok(true, message);
 const fail = (message) => ok(false, message);
 
 /**
  * Each method of this object is a test; tests can be synchronous or asynchronous:
  *
--- a/devtools/shared/webconsole/test/test_cached_messages.html
+++ b/devtools/shared/webconsole/test/test_cached_messages.html
@@ -95,17 +95,17 @@ function doConsoleCalls() {
 }
 </script>
 
 <script class="testbody" type="text/javascript">
 SimpleTest.waitForExplicitFinish();
 var {ConsoleServiceListener} =
   require("devtools/server/actors/webconsole/listeners/console-service");
 var {ConsoleAPIListener} =
-  require("devtools/server/actors/webconsole/listeners");
+  require("devtools/server/actors/webconsole/listeners/console-api");
 
 let consoleAPIListener, consoleServiceListener;
 let consoleAPICalls = 0;
 let pageErrors = 0;
 
 let handlers = {
   onConsoleAPICall: function onConsoleAPICall(message) {
     for (let msg of expectedConsoleCalls) {