Bug 1531838 - Inline PanelUI code and avoid leaving behind properties on windows. r=johannh
authorNihanth Subramanya <nhnt11@gmail.com>
Sun, 14 Apr 2019 17:47:50 +0000
changeset 469653 9f7b63f61c83
parent 469652 56b4bb2871d1
child 469654 d6e09cefb3c7
push id35878
push userapavel@mozilla.com
push dateTue, 16 Apr 2019 15:43:40 +0000
treeherdermozilla-central@258af4e91151 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjohannh
bugs1531838
milestone68.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 1531838 - Inline PanelUI code and avoid leaving behind properties on windows. r=johannh Differential Revision: https://phabricator.services.mozilla.com/D26699
browser/extensions/fxmonitor/moz.build
browser/extensions/fxmonitor/privileged/FirefoxMonitor.jsm
browser/extensions/fxmonitor/privileged/subscripts/PanelUI.jsm
--- a/browser/extensions/fxmonitor/moz.build
+++ b/browser/extensions/fxmonitor/moz.build
@@ -23,14 +23,13 @@ FINAL_TARGET_FILES.features['fxmonitor@m
   'privileged/api.js',
   'privileged/FirefoxMonitor.css',
   'privileged/FirefoxMonitor.jsm',
   'privileged/schema.json'
 ]
 
 FINAL_TARGET_FILES.features['fxmonitor@mozilla.org']['privileged']['subscripts'] += [
   'privileged/subscripts/EveryWindow.jsm',
-  'privileged/subscripts/Globals.jsm',
-  'privileged/subscripts/PanelUI.jsm'
+  'privileged/subscripts/Globals.jsm'
 ]
 
 with Files('**'):
   BUG_COMPONENT = ('Firefox', 'Firefox Monitor')
--- a/browser/extensions/fxmonitor/privileged/FirefoxMonitor.jsm
+++ b/browser/extensions/fxmonitor/privileged/FirefoxMonitor.jsm
@@ -99,20 +99,16 @@ this.FirefoxMonitor = {
     /* globals Preferences, RemoteSettings, fetch, btoa, XUL_NS */
     Services.scriptloader.loadSubScript(
       this.getURL("privileged/subscripts/Globals.jsm"));
 
     /* globals EveryWindow */
     Services.scriptloader.loadSubScript(
       this.getURL("privileged/subscripts/EveryWindow.jsm"));
 
-    /* globals PanelUI */
-    Services.scriptloader.loadSubScript(
-      this.getURL("privileged/subscripts/PanelUI.jsm"));
-
     // Expire our telemetry on November 1, at which time
     // we should redo data-review.
     let telemetryExpiryDate = new Date(2019, 10, 1); // Month is zero-index
     let today = new Date();
     let expired = today.getTime() > telemetryExpiryDate.getTime();
 
     Services.telemetry.registerEvents("fxmonitor", {
       "interaction": {
@@ -227,51 +223,35 @@ this.FirefoxMonitor = {
       // If we can't get the host for the URL, it's not one we
       // care about for breach alerts anyway.
       return;
     }
 
     this.warnIfNeeded(aBrowser, host);
   },
 
+  notificationsByWindow: new WeakMap(),
+  panelUIsByWindow: new WeakMap(),
+
   async startObserving() {
     if (this.observerAdded) {
       return;
     }
 
     await this.delayedInit();
 
     EveryWindow.registerCallback(
       this.kNotificationID,
       (win) => {
         // Inject our stylesheet.
         let DOMWindowUtils = win.windowUtils;
         DOMWindowUtils.loadSheetUsingURIString(this.getURL("privileged/FirefoxMonitor.css"),
                                                DOMWindowUtils.AUTHOR_SHEET);
 
-        // Set up some helper functions on the window object
-        // for the popup notification to use.
-        win.FirefoxMonitorUtils = {
-          // Keeps track of all notifications currently shown,
-          // so that we can clear them out properly if we get
-          // disabled.
-          notifications: new Set(),
-          disable: () => {
-            this.disable();
-          },
-          getString: (aKey) => {
-            return this.getString(aKey);
-          },
-          getFormattedString: (aKey, args) => {
-            return this.getFormattedString(aKey, args);
-          },
-          getFirefoxMonitorURL: (aSiteName) => {
-            return `${this.FirefoxMonitorURL}/?breach=${encodeURIComponent(aSiteName)}&utm_source=firefox&utm_medium=popup`;
-          },
-        };
+        this.notificationsByWindow.set(win, new Set());
 
         // Setup the popup notification stuff. First, the URL bar icon:
         let doc = win.document;
         let notificationBox = doc.getElementById("notification-popup-box");
         // We create a box to use as the anchor, and put an icon image
         // inside it. This way, when we animate the icon, its scale change
         // does not cause the popup notification to bounce due to the anchor
         // point moving.
@@ -293,46 +273,41 @@ this.FirefoxMonitor = {
         let pn = doc.createElementNS(XUL_NS, "popupnotification");
         let pnContent = doc.createElementNS(XUL_NS, "popupnotificationcontent");
         let panelUI = new PanelUI(doc);
         pnContent.appendChild(panelUI.box);
         pn.appendChild(pnContent);
         pn.setAttribute("id", `${this.kNotificationID}-notification`);
         pn.setAttribute("hidden", "true");
         parentElt.appendChild(pn);
-        win.FirefoxMonitorPanelUI = panelUI;
+        this.panelUIsByWindow.set(win, panelUI);
 
         // Start listening across all tabs!
         win.gBrowser.addTabsProgressListener(this);
       },
       (win) => {
         // If the window is being destroyed and gBrowser no longer exists,
         // don't bother doing anything.
         if (!win.gBrowser) {
           return;
         }
 
         let DOMWindowUtils = win.windowUtils;
-        if (!DOMWindowUtils) {
-          // win.windowUtils was added in 63, fallback if it's not available.
-          DOMWindowUtils = win.QueryInterface(Ci.nsIInterfaceRequestor)
-                              .getInterface(Ci.nsIDOMWindowUtils);
-        }
         DOMWindowUtils.removeSheetUsingURIString(this.getURL("privileged/FirefoxMonitor.css"),
                                                  DOMWindowUtils.AUTHOR_SHEET);
 
-        win.FirefoxMonitorUtils.notifications.forEach(n => {
+        this.notificationsByWindow.get(win).forEach(n => {
           n.remove();
         });
-        delete win.FirefoxMonitorUtils;
+        this.notificationsByWindow.delete(win);
 
         let doc = win.document;
         doc.getElementById(`${this.kNotificationID}-notification-anchor`).remove();
         doc.getElementById(`${this.kNotificationID}-notification`).remove();
-        delete win.FirefoxMonitorPanelUI;
+        this.panelUIsByWindow.delete(win);
 
         win.gBrowser.removeTabsProgressListener(this);
       },
     );
 
     this.observerAdded = true;
   },
 
@@ -369,17 +344,17 @@ this.FirefoxMonitor = {
       Preferences.set(this.kFirstAlertShownPref, true);
     }
 
     this.warnedHostsSet.add(host);
     Preferences.set(this.kWarnedHostsPref, JSON.stringify([...this.warnedHostsSet]));
 
     let doc = browser.ownerDocument;
     let win = doc.defaultView;
-    let panelUI = doc.defaultView.FirefoxMonitorPanelUI;
+    let panelUI = this.panelUIsByWindow.get(win);
 
     let animatedOnce = false;
     let populatePanel = (event) => {
       switch (event) {
         case "showing":
           panelUI.refresh(site);
           if (animatedOnce) {
             // If we've already animated once for this site, don't animate again.
@@ -395,17 +370,17 @@ this.FirefoxMonitor = {
              .removeAttribute("fxmonitoranimationdone");
           doc.getElementById(`${this.kNotificationID}-notification-anchor`)
              .removeAttribute("fxmonitoranimationdone");
           break;
         case "shown":
           animatedOnce = true;
           break;
         case "removed":
-          win.FirefoxMonitorUtils.notifications.delete(
+          this.notificationsByWindow.get(win).delete(
             win.PopupNotifications.getNotification(this.kNotificationID, browser));
           Services.telemetry.recordEvent("fxmonitor", "interaction", "doorhanger_removed");
           break;
       }
     };
 
     let n = win.PopupNotifications.show(
       browser, this.kNotificationID, "",
@@ -415,11 +390,123 @@ this.FirefoxMonitor = {
         hideClose: true,
         eventCallback: populatePanel,
         popupIconURL: this.getURL("assets/monitor32.svg"),
       }
     );
 
     Services.telemetry.recordEvent("fxmonitor", "interaction", "doorhanger_shown");
 
-    win.FirefoxMonitorUtils.notifications.add(n);
+    this.notificationsByWindow.get(win).add(n);
   },
 };
+
+/* globals PluralForm */
+
+function PanelUI(doc) {
+  this.site = null;
+  this.doc = doc;
+
+  let box = doc.createElementNS(XUL_NS, "vbox");
+
+  let elt = doc.createElementNS(XUL_NS, "description");
+  elt.textContent = this.getString("fxmonitor.popupHeader");
+  elt.classList.add("headerText");
+  box.appendChild(elt);
+
+  elt = doc.createElementNS(XUL_NS, "description");
+  elt.classList.add("popupText");
+  box.appendChild(elt);
+
+  this.box = box;
+}
+
+PanelUI.prototype = {
+  getString(aKey) {
+    return FirefoxMonitor.getString(aKey);
+  },
+
+  getFormattedString(aKey, args) {
+    return FirefoxMonitor.getFormattedString(aKey, args);
+  },
+
+  get brandString() {
+    if (this._brandString) {
+      return this._brandString;
+    }
+    return this._brandString = this.getString("fxmonitor.brandName");
+  },
+
+  getFirefoxMonitorURL: (aSiteName) => {
+    return `${FirefoxMonitor.FirefoxMonitorURL}/?breach=${encodeURIComponent(aSiteName)}&utm_source=firefox&utm_medium=popup`;
+  },
+
+  get primaryAction() {
+    if (this._primaryAction) {
+      return this._primaryAction;
+    }
+    return this._primaryAction = {
+      label: this.getFormattedString("fxmonitor.checkButton.label", [this.brandString]),
+      accessKey: this.getString("fxmonitor.checkButton.accessKey"),
+      callback: () => {
+        let win = this.doc.defaultView;
+        win.openTrustedLinkIn(
+          this.getFirefoxMonitorURL(this.site.Name), "tab", { });
+
+        Services.telemetry.recordEvent("fxmonitor", "interaction", "check_btn");
+      },
+    };
+  },
+
+  get secondaryActions() {
+    if (this._secondaryActions) {
+      return this._secondaryActions;
+    }
+    return this._secondaryActions = [
+      {
+        label: this.getString("fxmonitor.dismissButton.label"),
+        accessKey: this.getString("fxmonitor.dismissButton.accessKey"),
+        callback: () => {
+          Services.telemetry.recordEvent("fxmonitor", "interaction", "dismiss_btn");
+        },
+      }, {
+        label: this.getFormattedString("fxmonitor.neverShowButton.label", [this.brandString]),
+        accessKey: this.getString("fxmonitor.neverShowButton.accessKey"),
+        callback: () => {
+          FirefoxMonitor.disable();
+          Services.telemetry.recordEvent("fxmonitor", "interaction", "never_show_btn");
+        },
+      },
+    ];
+  },
+
+  refresh(site) {
+    this.site = site;
+
+    let elt = this.box.querySelector(".popupText");
+
+    // If > 100k, the PwnCount is rounded down to the most significant
+    // digit and prefixed with "More than".
+    // Ex.: 12,345 -> 12,345
+    //      234,567 -> More than 200,000
+    //      345,678,901 -> More than 300,000,000
+    //      4,567,890,123 -> More than 4,000,000,000
+    let k100k = 100000;
+    let pwnCount = site.PwnCount;
+    let stringName = "fxmonitor.popupText";
+    if (pwnCount > k100k) {
+      let multiplier = 1;
+      while (pwnCount >= 10) {
+        pwnCount /= 10;
+        multiplier *= 10;
+      }
+      pwnCount = Math.floor(pwnCount) * multiplier;
+      stringName = "fxmonitor.popupTextRounded";
+    }
+
+    elt.textContent =
+      PluralForm.get(pwnCount, this.getString(stringName))
+                .replace("#1", pwnCount.toLocaleString())
+                .replace("#2", site.Name)
+                .replace("#3", site.Year)
+                .replace("#4", this.brandString);
+  },
+};
deleted file mode 100644
--- a/browser/extensions/fxmonitor/privileged/subscripts/PanelUI.jsm
+++ /dev/null
@@ -1,117 +0,0 @@
-/* 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/. */
-
-/* globals XUL_NS, Services, PluralForm */
-
-function PanelUI(doc) {
-  this.site = null;
-  this.doc = doc;
-
-  let box = doc.createElementNS(XUL_NS, "vbox");
-
-  let elt = doc.createElementNS(XUL_NS, "description");
-  elt.textContent = this.getString("fxmonitor.popupHeader");
-  elt.classList.add("headerText");
-  box.appendChild(elt);
-
-  elt = doc.createElementNS(XUL_NS, "description");
-  elt.classList.add("popupText");
-  box.appendChild(elt);
-
-  this.box = box;
-}
-
-PanelUI.prototype = {
-  get FirefoxMonitorUtils() {
-    // Set on every window by FirefoxMonitor.jsm for PanelUI to use.
-    // Because sharing is caring.
-    return this.doc.defaultView.FirefoxMonitorUtils;
-  },
-
-  getString(aKey) {
-    return this.FirefoxMonitorUtils.getString(aKey);
-  },
-
-  getFormattedString(aKey, args) {
-    return this.FirefoxMonitorUtils.getFormattedString(aKey, args);
-  },
-
-  get brandString() {
-    if (this._brandString) {
-      return this._brandString;
-    }
-    return this._brandString = this.getString("fxmonitor.brandName");
-  },
-
-  get primaryAction() {
-    if (this._primaryAction) {
-      return this._primaryAction;
-    }
-    return this._primaryAction = {
-      label: this.getFormattedString("fxmonitor.checkButton.label", [this.brandString]),
-      accessKey: this.getString("fxmonitor.checkButton.accessKey"),
-      callback: () => {
-        let win = this.doc.defaultView;
-        win.openTrustedLinkIn(
-          win.FirefoxMonitorUtils.getFirefoxMonitorURL(this.site.Name), "tab", { });
-
-        Services.telemetry.recordEvent("fxmonitor", "interaction", "check_btn");
-      },
-    };
-  },
-
-  get secondaryActions() {
-    if (this._secondaryActions) {
-      return this._secondaryActions;
-    }
-    return this._secondaryActions = [
-      {
-        label: this.getString("fxmonitor.dismissButton.label"),
-        accessKey: this.getString("fxmonitor.dismissButton.accessKey"),
-        callback: () => {
-          Services.telemetry.recordEvent("fxmonitor", "interaction", "dismiss_btn");
-        },
-      }, {
-        label: this.getFormattedString("fxmonitor.neverShowButton.label", [this.brandString]),
-        accessKey: this.getString("fxmonitor.neverShowButton.accessKey"),
-        callback: () => {
-          this.FirefoxMonitorUtils.disable();
-          Services.telemetry.recordEvent("fxmonitor", "interaction", "never_show_btn");
-        },
-      },
-    ];
-  },
-
-  refresh(site) {
-    this.site = site;
-
-    let elt = this.box.querySelector(".popupText");
-
-    // If > 100k, the PwnCount is rounded down to the most significant
-    // digit and prefixed with "More than".
-    // Ex.: 12,345 -> 12,345
-    //      234,567 -> More than 200,000
-    //      345,678,901 -> More than 300,000,000
-    //      4,567,890,123 -> More than 4,000,000,000
-    let k100k = 100000;
-    let pwnCount = site.PwnCount;
-    let stringName = "fxmonitor.popupText";
-    if (pwnCount > k100k) {
-      let multiplier = 1;
-      while (pwnCount >= 10) {
-        pwnCount /= 10;
-        multiplier *= 10;
-      }
-      pwnCount = Math.floor(pwnCount) * multiplier;
-      stringName = "fxmonitor.popupTextRounded";
-    }
-
-    elt.textContent =
-      PluralForm.get(pwnCount, this.getString(stringName))
-                .replace("#1", pwnCount.toLocaleString())
-                .replace("#2", site.Name)
-                .replace("#3", site.Year)
-                .replace("#4", this.brandString);
-  },
-};