es.
   */
  onRefreshAttempted(aWebProgress, aURI, aDelay, aSameURI) {
    let win = aWebProgress.DOMWindow;

    let data = {
      browsingContext: win.browsingContext,
      URI: aURI.spec,
      delay: aDelay,
      sameURI: aSameURI,
    };

    if (this.blockedWindows.has(win)) {
      // onLocationChange must have fired before, so we can tell the
      // parent to show the notification.
      this.send(win, 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;
  },

  send(win, data) {
    // Due to the |nsDocLoader| calling its |nsIWebProgressListener|s in
    // reverse order, this will occur *before* the |BrowserChild| can send its
    // |OnLocationChange| event to the parent, but we need this message to
    // arrive after to ensure that the refresh blocker notification is not
    // immediately cleared by the |OnLocationChange| from |BrowserChild|.
    setTimeout(() => {
      // An exception can occur if refresh blocking was turned off
      // during a pageload.
      try {
        let actor = win.windowGlobalChild.getActor("RefreshBlocker");
        if (actor) {
          actor.sendAsyncMessage("RefreshBlocker:Blocked", data);
        }
      } catch (ex) {}
    }, 0);
  },

  QueryInterface: ChromeUtils.generateQI([
    "nsIWebProgressListener2",
    "nsIWebProgressListener",
    "nsISupportsWeakReference",
  ]),
};

export class RefreshBlockerChild extends JSWindowActorChild {
  didDestroy() {
    // If the refresh blocking preference is turned off, all of the
    // RefreshBlockerChild actors will get destroyed, so disable
    // refresh blocking only in this case.
    if (!Services.prefs.getBoolPref(REFRESHBLOCKING_PREF)) {
      this.disable(this.docShell);
    }
  }

  enable() {
    ChromeUtils.domProcessChild
      .getActor("RefreshBlockerObserver")
      .enable(this.docShell);
  }

  disable() {
    ChromeUtils.domProcessChild
      .getActor("RefreshBlockerObserver")
      .disable(this.docShell);
  }

  receiveMessage(message) {
    let data = message.data;

    switch (message.name) {
      case "RefreshBlocker:Refresh": {
        let docShell = data.browsingContext.docShell;
        let refreshURI = docShell.QueryInterface(Ci.nsIRefreshURI);
        let URI = Services.io.newURI(data.URI);
        refreshURI.forceRefreshURI(URI, null, data.delay);
        break;
      }

      case "PreferenceChanged":
        if (data.isEnabled) {
          this.enable(this.docShell);
        } else {
          this.disable(this.docShell);
        }
    }
  }
}

export class RefreshBlockerObserverChild extends JSProcessActorChild {
  constructor() {
    super();
    this.filtersMap = new Map();
  }

  observe(subject, topic) {
    switch (topic) {
      case "webnavigation-create":
      case "chrome-webnavigation-create":
        if (Services.prefs.getBoolPref(REFRESHBLOCKING_PREF)) {
          this.enable(subject.QueryInterface(Ci.nsIDocShell));
        }
        break;

      case "webnavigation-destroy":
      case "chrome-webnavigation-destroy":
        if (Services.prefs.getBoolPref(REFRESHBLOCKING_PREF)) {
          this.disable(subject.QueryInterface(Ci.nsIDocShell));
        }
        break;
    }
  }

  enable(docShell) {
    if (this.filtersMap.has(docShell)) {
      return;
    }

    let filter = Cc[
      "@mozilla.org/appshell/component/browser-status-filter;1"
    ].createInstance(Ci.nsIWebProgress);

    filter.addProgressListener(progressListener, Ci.nsIWebProgress.NOTIFY_ALL);

    this.filtersMap.set(docShell, filter);

    let webProgress = docShell
      .QueryInterface(Ci.nsIInterfaceRequestor)
      .getInterface(Ci.nsIWebProgress);
    webProgress.addProgressListener(filter, Ci.nsIWebProgress.NOTIFY_ALL);
  }

  disable(docShell) {
    let filter = this.filtersMap.get(docShell);
    if (!filter) {
      return;
    }

    let webProgress = docShell
      .QueryInterface(Ci.nsIInterfaceRequestor)
      .getInterface(Ci.nsIWebProgress);
    webProgress.removeProgressListener(filter);

    filter.removeProgressListener(progressListener);
    this.filtersMap.delete(docShell);
  }
}
PK