Bug 1522253: Convert RemotePageManager to ES6 classes. r=Gijs
authorDave Townsend <dtownsend@oxymoronical.com>
Tue, 12 Feb 2019 12:08:07 +0000
changeset 458765 dcc0c69e319e
parent 458764 0ca63f8a449c
child 458766 e8f1e89edf7f
push id35548
push useropoprus@mozilla.com
push dateWed, 13 Feb 2019 09:48:26 +0000
treeherdermozilla-central@93e37c529818 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersGijs
bugs1522253
milestone67.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 1522253: Convert RemotePageManager to ES6 classes. r=Gijs Differential Revision: https://phabricator.services.mozilla.com/D19411
toolkit/components/remotepagemanager/MessagePort.jsm
toolkit/components/remotepagemanager/RemotePageManagerChild.jsm
toolkit/components/remotepagemanager/RemotePageManagerParent.jsm
--- a/toolkit/components/remotepagemanager/MessagePort.jsm
+++ b/toolkit/components/remotepagemanager/MessagePort.jsm
@@ -63,171 +63,164 @@ let RPMAccessManager = {
     }
 
     // otherwise deny access
     Cu.reportError("RPMAccessManager does not allow access to Feature: " + aFeature + " for: " + uri);
     return false;
   },
 };
 
-function MessageListener() {
-  this.listeners = new Map();
-}
+class MessageListener {
+  constructor() {
+    this.listeners = new Map();
+  }
 
-MessageListener.prototype = {
   keys() {
     return this.listeners.keys();
-  },
+  }
 
   has(name) {
     return this.listeners.has(name);
-  },
+  }
 
   callListeners(message) {
     let listeners = this.listeners.get(message.name);
     if (!listeners) {
       return;
     }
 
     for (let listener of listeners.values()) {
       try {
         listener(message);
       } catch (e) {
         Cu.reportError(e);
       }
     }
-  },
+  }
 
   addMessageListener(name, callback) {
     if (!this.listeners.has(name))
       this.listeners.set(name, new Set([callback]));
     else
       this.listeners.get(name).add(callback);
-  },
+  }
 
   removeMessageListener(name, callback) {
     if (!this.listeners.has(name))
       return;
 
     this.listeners.get(name).delete(callback);
-  },
-};
+  }
+}
 
 /*
  * A message port sits on each side of the process boundary for every remote
  * page. Each has a port ID that is unique to the message manager it talks
  * through.
  *
  * We roughly implement the same contract as nsIMessageSender and
  * nsIMessageListenerManager
  */
-function MessagePort(messageManager, portID) {
-  this.messageManager = messageManager;
-  this.portID = portID;
-  this.destroyed = false;
-  this.listener = new MessageListener();
+class MessagePort {
+  constructor(messageManager, portID) {
+    this.messageManager = messageManager;
+    this.portID = portID;
+    this.destroyed = false;
+    this.listener = new MessageListener();
 
-  this.message = this.message.bind(this);
-  this.messageManager.addMessageListener("RemotePage:Message", this.message);
-}
-
-MessagePort.prototype = {
-  messageManager: null,
-  portID: null,
-  destroyed: null,
-  listener: null,
-  _browser: null,
-  remotePort: null,
+    this.message = this.message.bind(this);
+    this.messageManager.addMessageListener("RemotePage:Message", this.message);
+  }
 
   // Called when the message manager used to connect to the other process has
   // changed, i.e. when a tab is detached.
   swapMessageManager(messageManager) {
     this.messageManager.removeMessageListener("RemotePage:Message", this.message);
 
     this.messageManager = messageManager;
 
     this.messageManager.addMessageListener("RemotePage:Message", this.message);
-  },
+  }
 
   /* Adds a listener for messages. Many callbacks can be registered for the
    * same message if necessary. An attempt to register the same callback for the
    * same message twice will be ignored. When called the callback is passed an
    * object with these properties:
    *   target: This message port
    *   name:   The message name
    *   data:   Any data sent with the message
    */
   addMessageListener(name, callback) {
     if (this.destroyed) {
       throw new Error("Message port has been destroyed");
     }
 
     this.listener.addMessageListener(name, callback);
-  },
+  }
 
   /*
    * Removes a listener for messages.
    */
   removeMessageListener(name, callback) {
     if (this.destroyed) {
       throw new Error("Message port has been destroyed");
     }
 
     this.listener.removeMessageListener(name, callback);
-  },
+  }
 
   // Sends a message asynchronously to the other process
   sendAsyncMessage(name, data = null) {
     if (this.destroyed) {
       throw new Error("Message port has been destroyed");
     }
 
     this.messageManager.sendAsyncMessage("RemotePage:Message", {
       portID: this.portID,
       name,
       data,
     });
-  },
+  }
 
   // Called to destroy this port
   destroy() {
     try {
       // This can fail in the child process if the tab has already been closed
       this.messageManager.removeMessageListener("RemotePage:Message", this.message);
     } catch (e) { }
     this.messageManager = null;
     this.destroyed = true;
     this.portID = null;
     this.listener = null;
-  },
+  }
 
   getBoolPref(aPref) {
     let principal = this.window.document.nodePrincipal;
     if (!RPMAccessManager.checkAllowAccess(principal, "getBoolPref", aPref)) {
       throw new Error("RPMAccessManager does not allow access to getBoolPref");
     }
     return Services.prefs.getBoolPref(aPref);
-  },
+  }
 
   setBoolPref(aPref, aVal) {
     return new this.window.Promise(function(resolve) {
       AsyncPrefs.set(aPref, aVal).then(function() {
         resolve();
       });
     });
-  },
+  }
 
   getFormatURLPref(aFormatURL) {
     let principal = this.window.document.nodePrincipal;
     if (!RPMAccessManager.checkAllowAccess(principal, "getFormatURLPref", aFormatURL)) {
       throw new Error("RPMAccessManager does not allow access to getFormatURLPref");
     }
     return Services.urlFormatter.formatURLPref(aFormatURL);
-  },
+  }
 
   isWindowPrivate() {
     let principal = this.window.document.nodePrincipal;
     if (!RPMAccessManager.checkAllowAccess(principal, "isWindowPrivate", "yes")) {
       throw new Error("RPMAccessManager does not allow access to isWindowPrivate");
     }
     return PrivateBrowsingUtils.isContentWindowPrivate(this.window);
-  },
-};
+  }
+}
--- a/toolkit/components/remotepagemanager/RemotePageManagerChild.jsm
+++ b/toolkit/components/remotepagemanager/RemotePageManagerChild.jsm
@@ -5,86 +5,86 @@
 "use strict";
 
 var EXPORTED_SYMBOLS = ["ChildMessagePort"];
 
 const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
 const {MessagePort} = ChromeUtils.import("resource://gre/modules/remotepagemanager/MessagePort.jsm");
 
 // The content side of a message port
-function ChildMessagePort(contentFrame, window) {
-  let portID = Services.appinfo.processID + ":" + ChildMessagePort.prototype.nextPortID++;
-  MessagePort.call(this, contentFrame, portID);
+class ChildMessagePort extends MessagePort {
+  constructor(contentFrame, window) {
+    let portID = Services.appinfo.processID + ":" + ChildMessagePort.nextPortID++;
+    super(contentFrame, portID);
+
+    this.window = window;
 
-  this.window = window;
+    // Add functionality to the content page
+    Cu.exportFunction(this.sendAsyncMessage.bind(this), window, {
+      defineAs: "RPMSendAsyncMessage",
+    });
+    Cu.exportFunction(this.addMessageListener.bind(this), window, {
+      defineAs: "RPMAddMessageListener",
+      allowCallbacks: true,
+    });
+    Cu.exportFunction(this.removeMessageListener.bind(this), window, {
+      defineAs: "RPMRemoveMessageListener",
+      allowCallbacks: true,
+    });
+    Cu.exportFunction(this.getBoolPref.bind(this), window, {
+      defineAs: "RPMGetBoolPref",
+    });
+    Cu.exportFunction(this.setBoolPref.bind(this), window, {
+      defineAs: "RPMSetBoolPref",
+    });
+    Cu.exportFunction(this.getFormatURLPref.bind(this), window, {
+      defineAs: "RPMGetFormatURLPref",
+    });
+    Cu.exportFunction(this.isWindowPrivate.bind(this), window, {
+      defineAs: "RPMIsWindowPrivate",
+    });
 
-  // Add functionality to the content page
-  Cu.exportFunction(this.sendAsyncMessage.bind(this), window, {
-    defineAs: "RPMSendAsyncMessage",
-  });
-  Cu.exportFunction(this.addMessageListener.bind(this), window, {
-    defineAs: "RPMAddMessageListener",
-    allowCallbacks: true,
-  });
-  Cu.exportFunction(this.removeMessageListener.bind(this), window, {
-    defineAs: "RPMRemoveMessageListener",
-    allowCallbacks: true,
-  });
-  Cu.exportFunction(this.getBoolPref.bind(this), window, {
-    defineAs: "RPMGetBoolPref",
-  });
-  Cu.exportFunction(this.setBoolPref.bind(this), window, {
-    defineAs: "RPMSetBoolPref",
-  });
-  Cu.exportFunction(this.getFormatURLPref.bind(this), window, {
-    defineAs: "RPMGetFormatURLPref",
-  });
-  Cu.exportFunction(this.isWindowPrivate.bind(this), window, {
-    defineAs: "RPMIsWindowPrivate",
-  });
+    // Send a message for load events
+    let loadListener = () => {
+      this.sendAsyncMessage("RemotePage:Load");
+      window.removeEventListener("load", loadListener);
+    };
+    window.addEventListener("load", loadListener);
+
+    // Destroy the port when the window is unloaded
+    window.addEventListener("unload", () => {
+      try {
+        this.sendAsyncMessage("RemotePage:Unload");
+      } catch (e) {
+        // If the tab has been closed the frame message manager has already been
+        // destroyed
+      }
+      this.destroy();
+    });
 
-  // Send a message for load events
-  let loadListener = () => {
-    this.sendAsyncMessage("RemotePage:Load");
-    window.removeEventListener("load", loadListener);
-  };
-  window.addEventListener("load", loadListener);
+    // Tell the main process to set up its side of the message pipe.
+    this.messageManager.sendAsyncMessage("RemotePage:InitPort", {
+      portID,
+      url: window.document.documentURI.replace(/[\#|\?].*$/, ""),
+    });
+  }
 
-  // Destroy the port when the window is unloaded
-  window.addEventListener("unload", () => {
-    try {
-      this.sendAsyncMessage("RemotePage:Unload");
-    } catch (e) {
-      // If the tab has been closed the frame message manager has already been
-      // destroyed
+  // Called when a message is received from the message manager. This could
+  // have come from any port in the message manager so verify the port ID.
+  message({ data: messagedata }) {
+    if (this.destroyed || (messagedata.portID != this.portID)) {
+      return;
     }
-    this.destroy();
-  });
 
-  // Tell the main process to set up its side of the message pipe.
-  this.messageManager.sendAsyncMessage("RemotePage:InitPort", {
-    portID,
-    url: window.document.documentURI.replace(/[\#|\?].*$/, ""),
-  });
+    let message = {
+      name: messagedata.name,
+      data: messagedata.data,
+    };
+    this.listener.callListeners(Cu.cloneInto(message, this.window));
+  }
+
+  destroy() {
+    this.window = null;
+    super.destroy.call(this);
+  }
 }
 
-ChildMessagePort.prototype = Object.create(MessagePort.prototype);
-
-ChildMessagePort.prototype.nextPortID = 0;
-
-// Called when a message is received from the message manager. This could
-// have come from any port in the message manager so verify the port ID.
-ChildMessagePort.prototype.message = function({ data: messagedata }) {
-  if (this.destroyed || (messagedata.portID != this.portID)) {
-    return;
-  }
-
-  let message = {
-    name: messagedata.name,
-    data: messagedata.data,
-  };
-  this.listener.callListeners(Cu.cloneInto(message, this.window));
-};
-
-ChildMessagePort.prototype.destroy = function() {
-  this.window = null;
-  MessagePort.prototype.destroy.call(this);
-};
+ChildMessagePort.nextPortID = 0;
--- a/toolkit/components/remotepagemanager/RemotePageManagerParent.jsm
+++ b/toolkit/components/remotepagemanager/RemotePageManagerParent.jsm
@@ -23,135 +23,130 @@ const {Services} = ChromeUtils.import("r
 const {MessageListener, MessagePort} = ChromeUtils.import("resource://gre/modules/remotepagemanager/MessagePort.jsm");
 
 /**
  * Creates a RemotePages object which listens for new remote pages of some
  * particular URLs. A "RemotePage:Init" message will be dispatched to this
  * object for every page loaded. Message listeners added to this object receive
  * messages from all loaded pages from the requested urls.
  */
-var RemotePages = function(urls) {
-  this.urls = Array.isArray(urls) ? urls : [urls];
-  this.messagePorts = new Set();
-  this.listener = new MessageListener();
-  this.destroyed = false;
-
-  this.portCreated = this.portCreated.bind(this);
-  this.portMessageReceived = this.portMessageReceived.bind(this);
+class RemotePages {
+  constructor(urls) {
+    this.urls = Array.isArray(urls) ? urls : [urls];
+    this.messagePorts = new Set();
+    this.listener = new MessageListener();
+    this.destroyed = false;
 
-  for (const url of this.urls) {
-    RemotePageManager.addRemotePageListener(url, this.portCreated);
-  }
-};
+    this.portCreated = this.portCreated.bind(this);
+    this.portMessageReceived = this.portMessageReceived.bind(this);
 
-RemotePages.prototype = {
-  urls: null,
-  messagePorts: null,
-  listener: null,
-  destroyed: null,
+    for (const url of this.urls) {
+      RemotePageManager.addRemotePageListener(url, this.portCreated);
+    }
+  }
 
   destroy() {
     for (const url of this.urls) {
       RemotePageManager.removeRemotePageListener(url);
     }
 
     for (let port of this.messagePorts.values()) {
       this.removeMessagePort(port);
     }
 
     this.messagePorts = null;
     this.listener = null;
     this.destroyed = true;
-  },
+  }
 
   // Called when a page matching one of the urls has loaded in a frame.
   portCreated(port) {
     this.messagePorts.add(port);
 
     port.loaded = false;
     port.addMessageListener("RemotePage:Load", this.portMessageReceived);
     port.addMessageListener("RemotePage:Unload", this.portMessageReceived);
 
     for (let name of this.listener.keys()) {
       this.registerPortListener(port, name);
     }
 
     this.listener.callListeners({ target: port, name: "RemotePage:Init" });
-  },
+  }
 
   // A message has been received from one of the pages
   portMessageReceived(message) {
     switch (message.name) {
       case "RemotePage:Load":
         message.target.loaded = true;
         break;
       case "RemotePage:Unload":
         message.target.loaded = false;
         this.removeMessagePort(message.target);
         break;
     }
 
     this.listener.callListeners(message);
-  },
+  }
 
   // A page has closed
   removeMessagePort(port) {
     for (let name of this.listener.keys()) {
       port.removeMessageListener(name, this.portMessageReceived);
     }
 
     port.removeMessageListener("RemotePage:Load", this.portMessageReceived);
     port.removeMessageListener("RemotePage:Unload", this.portMessageReceived);
     this.messagePorts.delete(port);
-  },
+  }
 
   registerPortListener(port, name) {
     port.addMessageListener(name, this.portMessageReceived);
-  },
+  }
 
   // Sends a message to all known pages
   sendAsyncMessage(name, data = null) {
     for (let port of this.messagePorts.values()) {
       try {
         port.sendAsyncMessage(name, data);
       } catch (e) {
         // Unless the port is in the process of unloading, something strange
         // happened but allow other ports to receive the message
         if (e.result !== Cr.NS_ERROR_NOT_INITIALIZED)
           Cu.reportError(e);
       }
     }
-  },
+  }
 
   addMessageListener(name, callback) {
     if (this.destroyed) {
       throw new Error("RemotePages has been destroyed");
     }
 
     if (!this.listener.has(name)) {
       for (let port of this.messagePorts.values()) {
         this.registerPortListener(port, name);
       }
     }
 
     this.listener.addMessageListener(name, callback);
-  },
+  }
 
   removeMessageListener(name, callback) {
     if (this.destroyed) {
       throw new Error("RemotePages has been destroyed");
     }
 
     this.listener.removeMessageListener(name, callback);
-  },
+  }
 
   portsForBrowser(browser) {
     return [...this.messagePorts].filter(port => port.browser == browser);
-  },
-};
+  }
+}
 
 
 // Only exposes the public properties of the MessagePort
 function publicMessagePort(port) {
   let properties = ["addMessageListener", "removeMessageListener",
                     "sendAsyncMessage", "destroy"];
 
   let clean = {};
@@ -181,106 +176,101 @@ function publicMessagePort(port) {
       },
     });
   }
 
   return clean;
 }
 
 // The chome side of a message port
-function ChromeMessagePort(browser, portID, url) {
-  MessagePort.call(this, browser.messageManager, portID);
-
-  this._browser = browser;
-  this._permanentKey = browser.permanentKey;
-  this._url = url;
+class ChromeMessagePort extends MessagePort {
+  constructor(browser, portID, url) {
+    super(browser.messageManager, portID);
 
-  Services.obs.addObserver(this, "message-manager-disconnect");
-  this.publicPort = publicMessagePort(this);
-
-  this.swapBrowsers = this.swapBrowsers.bind(this);
-  this._browser.addEventListener("SwapDocShells", this.swapBrowsers);
-}
+    this._browser = browser;
+    this._permanentKey = browser.permanentKey;
+    this._url = url;
 
-ChromeMessagePort.prototype = Object.create(MessagePort.prototype);
+    Services.obs.addObserver(this, "message-manager-disconnect");
+    this.publicPort = publicMessagePort(this);
 
-Object.defineProperty(ChromeMessagePort.prototype, "browser", {
-  get() {
+    this.swapBrowsers = this.swapBrowsers.bind(this);
+    this._browser.addEventListener("SwapDocShells", this.swapBrowsers);
+  }
+
+  get browser() {
     return this._browser;
-  },
-});
-
-Object.defineProperty(ChromeMessagePort.prototype, "url", {
-  get() {
-    return this._url;
-  },
-});
+  }
 
-// Called when the docshell is being swapped with another browser. We have to
-// update to use the new browser's message manager
-ChromeMessagePort.prototype.swapBrowsers = function({ detail: newBrowser }) {
-  // We can see this event for the new browser before the swap completes so
-  // check that the browser we're tracking has our permanentKey.
-  if (this._browser.permanentKey != this._permanentKey)
-    return;
-
-  this._browser.removeEventListener("SwapDocShells", this.swapBrowsers);
-
-  this._browser = newBrowser;
-  this.swapMessageManager(newBrowser.messageManager);
-
-  this._browser.addEventListener("SwapDocShells", this.swapBrowsers);
-};
+  get url() {
+    return this._url;
+  }
 
-// Called when a message manager has been disconnected indicating that the
-// tab has closed or crashed
-ChromeMessagePort.prototype.observe = function(messageManager) {
-  if (messageManager != this.messageManager)
-    return;
+  // Called when the docshell is being swapped with another browser. We have to
+  // update to use the new browser's message manager
+  swapBrowsers({ detail: newBrowser }) {
+    // We can see this event for the new browser before the swap completes so
+    // check that the browser we're tracking has our permanentKey.
+    if (this._browser.permanentKey != this._permanentKey)
+      return;
 
-  this.listener.callListeners({
-    target: this.publicPort,
-    name: "RemotePage:Unload",
-    data: null,
-  });
-  this.destroy();
-};
+    this._browser.removeEventListener("SwapDocShells", this.swapBrowsers);
 
-// Called when a message is received from the message manager. This could
-// have come from any port in the message manager so verify the port ID.
-ChromeMessagePort.prototype.message = function({ data: messagedata }) {
-  if (this.destroyed || (messagedata.portID != this.portID)) {
-    return;
+    this._browser = newBrowser;
+    this.swapMessageManager(newBrowser.messageManager);
+
+    this._browser.addEventListener("SwapDocShells", this.swapBrowsers);
   }
 
-  let message = {
-    target: this.publicPort,
-    name: messagedata.name,
-    data: messagedata.data,
-  };
-  this.listener.callListeners(message);
+  // Called when a message manager has been disconnected indicating that the
+  // tab has closed or crashed
+  observe(messageManager) {
+    if (messageManager != this.messageManager)
+      return;
 
-  if (messagedata.name == "RemotePage:Unload")
+    this.listener.callListeners({
+      target: this.publicPort,
+      name: "RemotePage:Unload",
+      data: null,
+    });
     this.destroy();
-};
-
-ChromeMessagePort.prototype.destroy = function() {
-  try {
-    this._browser.removeEventListener(
-        "SwapDocShells", this.swapBrowsers);
-  } catch (e) {
-    // It's possible the browser instance is already dead so we can just ignore
-    // this error.
   }
 
-  this._browser = null;
-  Services.obs.removeObserver(this, "message-manager-disconnect");
-  MessagePort.prototype.destroy.call(this);
-};
+  // Called when a message is received from the message manager. This could
+  // have come from any port in the message manager so verify the port ID.
+  message({ data: messagedata }) {
+    if (this.destroyed || (messagedata.portID != this.portID)) {
+      return;
+    }
+
+    let message = {
+      target: this.publicPort,
+      name: messagedata.name,
+      data: messagedata.data,
+    };
+    this.listener.callListeners(message);
 
+    if (messagedata.name == "RemotePage:Unload")
+      this.destroy();
+  }
+
+  destroy() {
+    try {
+      this._browser.removeEventListener(
+          "SwapDocShells", this.swapBrowsers);
+    } catch (e) {
+      // It's possible the browser instance is already dead so we can just ignore
+      // this error.
+    }
+
+    this._browser = null;
+    Services.obs.removeObserver(this, "message-manager-disconnect");
+    super.destroy.call(this);
+  }
+}
 
 // Allows callers to register to connect to specific content pages. Registration
 // is done through the addRemotePageListener method
 var RemotePageManagerInternal = {
   // The currently registered remote pages
   pages: new Map(),
 
   // Initialises all the needed listeners