Bug 1461548 - Part 0. Move RefreshBlocker to a new frame script. r=mconley draft
authorFelipe Gomes <felipc@gmail.com>
Mon, 14 May 2018 23:09:43 -0300
changeset 795130 0ea7e8c4e3cbbaeb5c503a32ee479018e1397650
parent 795129 5b4ad42dbee9442c4d713b22cfd74f70e09956a4
child 795131 0b09bcad8e2555278ab75e0ffed5275f15220d0d
push id109865
push userfelipc@gmail.com
push dateTue, 15 May 2018 02:10:44 +0000
reviewersmconley
bugs1461548
milestone62.0a1
Bug 1461548 - Part 0. Move RefreshBlocker to a new frame script. r=mconley MozReview-Commit-ID: 941z6MCP33S
browser/base/content/content-refreshblocker.js
browser/base/content/tab-content.js
browser/base/jar.mn
new file mode 100644
--- /dev/null
+++ b/browser/base/content/content-refreshblocker.js
@@ -0,0 +1,180 @@
+/* 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/. */
+
+/* eslint-env mozilla/frame-script */
+
+var RefreshBlocker = {
+  PREF: "accessibility.blockautorefresh",
+
+  // Bug 1247100 - When a refresh is caused by an HTTP header,
+  // onRefreshAttempted will be fired before onLocationChange.
+  // When a refresh is caused by a <meta> tag in the document,
+  // onRefreshAttempted will be fired after onLocationChange.
+  //
+  // We only ever want to send a message to the parent after
+  // onLocationChange has fired, since the parent uses the
+  // onLocationChange update to clear transient notifications.
+  // Sending the message before onLocationChange will result in
+  // us creating the notification, and then clearing it very
+  // soon after.
+  //
+  // To account for both cases (onRefreshAttempted before
+  // onLocationChange, and onRefreshAttempted after onLocationChange),
+  // we'll hold a mapping of DOM Windows that we see get
+  // sent through both onLocationChange and onRefreshAttempted.
+  // When either run, they'll check the WeakMap for the existence
+  // of the DOM Window. If it doesn't exist, it'll add it. If
+  // it finds it, it'll know that it's safe to send the message
+  // to the parent, since we know that both have fired.
+  //
+  // The DOM Window is removed from blockedWindows when we notice
+  // the nsIWebProgress change state to STATE_STOP for the
+  // STATE_IS_WINDOW case.
+  //
+  // DOM Windows are mapped to a JS object that contains the data
+  // to be sent to the parent to show the notification. Since that
+  // data is only known when onRefreshAttempted is fired, it's only
+  // ever stashed in the map if onRefreshAttempted fires first -
+  // otherwise, null is set as the value of the mapping.
+  blockedWindows: new WeakMap(),
+
+  init() {
+    if (Services.prefs.getBoolPref(this.PREF)) {
+      this.enable();
+    }
+
+    Services.prefs.addObserver(this.PREF, this);
+  },
+
+  uninit() {
+    if (Services.prefs.getBoolPref(this.PREF)) {
+      this.disable();
+    }
+
+    Services.prefs.removeObserver(this.PREF, this);
+  },
+
+  observe(subject, topic, data) {
+    if (topic == "nsPref:changed" && data == this.PREF) {
+      if (Services.prefs.getBoolPref(this.PREF)) {
+        this.enable();
+      } else {
+        this.disable();
+      }
+    }
+  },
+
+  enable() {
+    this._filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"]
+                     .createInstance(Ci.nsIWebProgress);
+    this._filter.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_ALL);
+    this._filter.target = tabEventTarget;
+
+    let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
+                              .getInterface(Ci.nsIWebProgress);
+    webProgress.addProgressListener(this._filter, Ci.nsIWebProgress.NOTIFY_ALL);
+
+    addMessageListener("RefreshBlocker:Refresh", this);
+  },
+
+  disable() {
+    let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
+                              .getInterface(Ci.nsIWebProgress);
+    webProgress.removeProgressListener(this._filter);
+
+    this._filter.removeProgressListener(this);
+    this._filter = null;
+
+    removeMessageListener("RefreshBlocker:Refresh", this);
+  },
+
+  send(data) {
+    sendAsyncMessage("RefreshBlocker:Blocked", data);
+  },
+
+  /**
+   * Notices when the nsIWebProgress transitions to STATE_STOP for
+   * the STATE_IS_WINDOW case, which will clear any mappings from
+   * blockedWindows.
+   */
+  onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
+    if (aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW &&
+        aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
+      this.blockedWindows.delete(aWebProgress.DOMWindow);
+    }
+  },
+
+  /**
+   * Notices when the location has changed. If, when running,
+   * onRefreshAttempted has already fired for this DOM Window, will
+   * send the appropriate refresh blocked data to the parent.
+   */
+  onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {
+    let win = aWebProgress.DOMWindow;
+    if (this.blockedWindows.has(win)) {
+      let data = this.blockedWindows.get(win);
+      if (data) {
+        // We saw onRefreshAttempted before onLocationChange, so
+        // send the message to the parent to show the notification.
+        this.send(data);
+      }
+    } else {
+      this.blockedWindows.set(win, null);
+    }
+  },
+
+  /**
+   * Notices when a refresh / reload was attempted. If, when running,
+   * onLocationChange has not yet run, will stash the appropriate data
+   * into the blockedWindows map to be sent when onLocationChange fires.
+   */
+  onRefreshAttempted(aWebProgress, aURI, aDelay, aSameURI) {
+    let win = aWebProgress.DOMWindow;
+    let outerWindowID = win.QueryInterface(Ci.nsIInterfaceRequestor)
+                           .getInterface(Ci.nsIDOMWindowUtils)
+                           .outerWindowID;
+
+    let data = {
+      URI: aURI.spec,
+      delay: aDelay,
+      sameURI: aSameURI,
+      outerWindowID,
+    };
+
+    if (this.blockedWindows.has(win)) {
+      // onLocationChange must have fired before, so we can tell the
+      // parent to show the notification.
+      this.send(data);
+    } else {
+      // onLocationChange hasn't fired yet, so stash the data in the
+      // map so that onLocationChange can send it when it fires.
+      this.blockedWindows.set(win, data);
+    }
+
+    return false;
+  },
+
+  receiveMessage(message) {
+    let data = message.data;
+
+    if (message.name == "RefreshBlocker:Refresh") {
+      let win = Services.wm.getOuterWindowWithId(data.outerWindowID);
+      let refreshURI = win.QueryInterface(Ci.nsIInterfaceRequestor)
+                          .getInterface(Ci.nsIDocShell)
+                          .QueryInterface(Ci.nsIRefreshURI);
+
+      let URI = Services.io.newURI(data.URI);
+
+      refreshURI.forceRefreshURI(URI, null, data.delay, true);
+    }
+  },
+
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener2, Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference]),
+};
+
+RefreshBlocker.init();
+
+addEventListener("unload", () => {
+  RefreshBlocker.uninit();
+});
--- a/browser/base/content/tab-content.js
+++ b/browser/base/content/tab-content.js
@@ -688,187 +688,16 @@ var DOMFullscreenHandler = {
         }
         break;
       }
     }
   }
 };
 DOMFullscreenHandler.init();
 
-var RefreshBlocker = {
-  PREF: "accessibility.blockautorefresh",
-
-  // Bug 1247100 - When a refresh is caused by an HTTP header,
-  // onRefreshAttempted will be fired before onLocationChange.
-  // When a refresh is caused by a <meta> tag in the document,
-  // onRefreshAttempted will be fired after onLocationChange.
-  //
-  // We only ever want to send a message to the parent after
-  // onLocationChange has fired, since the parent uses the
-  // onLocationChange update to clear transient notifications.
-  // Sending the message before onLocationChange will result in
-  // us creating the notification, and then clearing it very
-  // soon after.
-  //
-  // To account for both cases (onRefreshAttempted before
-  // onLocationChange, and onRefreshAttempted after onLocationChange),
-  // we'll hold a mapping of DOM Windows that we see get
-  // sent through both onLocationChange and onRefreshAttempted.
-  // When either run, they'll check the WeakMap for the existence
-  // of the DOM Window. If it doesn't exist, it'll add it. If
-  // it finds it, it'll know that it's safe to send the message
-  // to the parent, since we know that both have fired.
-  //
-  // The DOM Window is removed from blockedWindows when we notice
-  // the nsIWebProgress change state to STATE_STOP for the
-  // STATE_IS_WINDOW case.
-  //
-  // DOM Windows are mapped to a JS object that contains the data
-  // to be sent to the parent to show the notification. Since that
-  // data is only known when onRefreshAttempted is fired, it's only
-  // ever stashed in the map if onRefreshAttempted fires first -
-  // otherwise, null is set as the value of the mapping.
-  blockedWindows: new WeakMap(),
-
-  init() {
-    if (Services.prefs.getBoolPref(this.PREF)) {
-      this.enable();
-    }
-
-    Services.prefs.addObserver(this.PREF, this);
-  },
-
-  uninit() {
-    if (Services.prefs.getBoolPref(this.PREF)) {
-      this.disable();
-    }
-
-    Services.prefs.removeObserver(this.PREF, this);
-  },
-
-  observe(subject, topic, data) {
-    if (topic == "nsPref:changed" && data == this.PREF) {
-      if (Services.prefs.getBoolPref(this.PREF)) {
-        this.enable();
-      } else {
-        this.disable();
-      }
-    }
-  },
-
-  enable() {
-    this._filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"]
-                     .createInstance(Ci.nsIWebProgress);
-    this._filter.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_ALL);
-    this._filter.target = tabEventTarget;
-
-    let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
-                              .getInterface(Ci.nsIWebProgress);
-    webProgress.addProgressListener(this._filter, Ci.nsIWebProgress.NOTIFY_ALL);
-
-    addMessageListener("RefreshBlocker:Refresh", this);
-  },
-
-  disable() {
-    let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
-                              .getInterface(Ci.nsIWebProgress);
-    webProgress.removeProgressListener(this._filter);
-
-    this._filter.removeProgressListener(this);
-    this._filter = null;
-
-    removeMessageListener("RefreshBlocker:Refresh", this);
-  },
-
-  send(data) {
-    sendAsyncMessage("RefreshBlocker:Blocked", data);
-  },
-
-  /**
-   * Notices when the nsIWebProgress transitions to STATE_STOP for
-   * the STATE_IS_WINDOW case, which will clear any mappings from
-   * blockedWindows.
-   */
-  onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
-    if (aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW &&
-        aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
-      this.blockedWindows.delete(aWebProgress.DOMWindow);
-    }
-  },
-
-  /**
-   * Notices when the location has changed. If, when running,
-   * onRefreshAttempted has already fired for this DOM Window, will
-   * send the appropriate refresh blocked data to the parent.
-   */
-  onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {
-    let win = aWebProgress.DOMWindow;
-    if (this.blockedWindows.has(win)) {
-      let data = this.blockedWindows.get(win);
-      if (data) {
-        // We saw onRefreshAttempted before onLocationChange, so
-        // send the message to the parent to show the notification.
-        this.send(data);
-      }
-    } else {
-      this.blockedWindows.set(win, null);
-    }
-  },
-
-  /**
-   * Notices when a refresh / reload was attempted. If, when running,
-   * onLocationChange has not yet run, will stash the appropriate data
-   * into the blockedWindows map to be sent when onLocationChange fires.
-   */
-  onRefreshAttempted(aWebProgress, aURI, aDelay, aSameURI) {
-    let win = aWebProgress.DOMWindow;
-    let outerWindowID = win.QueryInterface(Ci.nsIInterfaceRequestor)
-                           .getInterface(Ci.nsIDOMWindowUtils)
-                           .outerWindowID;
-
-    let data = {
-      URI: aURI.spec,
-      delay: aDelay,
-      sameURI: aSameURI,
-      outerWindowID,
-    };
-
-    if (this.blockedWindows.has(win)) {
-      // onLocationChange must have fired before, so we can tell the
-      // parent to show the notification.
-      this.send(data);
-    } else {
-      // onLocationChange hasn't fired yet, so stash the data in the
-      // map so that onLocationChange can send it when it fires.
-      this.blockedWindows.set(win, data);
-    }
-
-    return false;
-  },
-
-  receiveMessage(message) {
-    let data = message.data;
-
-    if (message.name == "RefreshBlocker:Refresh") {
-      let win = Services.wm.getOuterWindowWithId(data.outerWindowID);
-      let refreshURI = win.QueryInterface(Ci.nsIInterfaceRequestor)
-                          .getInterface(Ci.nsIDocShell)
-                          .QueryInterface(Ci.nsIRefreshURI);
-
-      let URI = Services.io.newURI(data.URI);
-
-      refreshURI.forceRefreshURI(URI, null, data.delay, true);
-    }
-  },
-
-  QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener2, Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference]),
-};
-
-RefreshBlocker.init();
-
 var UserContextIdNotifier = {
   init() {
     addEventListener("DOMWindowCreated", this);
   },
 
   uninit() {
     removeEventListener("DOMWindowCreated", this);
   },
@@ -888,20 +717,16 @@ var UserContextIdNotifier = {
     sendAsyncMessage("Browser:WindowCreated", { userContextId });
   }
 };
 
 UserContextIdNotifier.init();
 
 Services.obs.notifyObservers(this, "tab-content-frameloader-created");
 
-addEventListener("unload", () => {
-  RefreshBlocker.uninit();
-});
-
 addMessageListener("AllowScriptsToClose", () => {
   content.QueryInterface(Ci.nsIInterfaceRequestor)
          .getInterface(Ci.nsIDOMWindowUtils)
          .allowScriptsToClose();
 });
 
 addEventListener("MozAfterPaint", function onFirstPaint() {
   removeEventListener("MozAfterPaint", onFirstPaint);
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -97,16 +97,17 @@ browser.jar:
         content/browser/defaultthemes/dark.icon.svg   (content/defaultthemes/dark.icon.svg)
         content/browser/defaultthemes/light.icon.svg  (content/defaultthemes/light.icon.svg)
 *       content/browser/pageinfo/pageInfo.xul         (content/pageinfo/pageInfo.xul)
         content/browser/pageinfo/pageInfo.js          (content/pageinfo/pageInfo.js)
         content/browser/pageinfo/pageInfo.css         (content/pageinfo/pageInfo.css)
         content/browser/pageinfo/feeds.js             (content/pageinfo/feeds.js)
         content/browser/pageinfo/permissions.js       (content/pageinfo/permissions.js)
         content/browser/pageinfo/security.js          (content/pageinfo/security.js)
+        content/browser/content-refreshblocker.js     (content/content-refreshblocker.js)
         content/browser/robot.ico                     (content/robot.ico)
         content/browser/static-robot.png              (content/static-robot.png)
         content/browser/safeMode.css                  (content/safeMode.css)
         content/browser/safeMode.js                   (content/safeMode.js)
         content/browser/safeMode.xul                  (content/safeMode.xul)
         content/browser/sanitize.xul                  (content/sanitize.xul)
         content/browser/sanitizeDialog.js             (content/sanitizeDialog.js)
         content/browser/sanitizeDialog.css            (content/sanitizeDialog.css)