activity-stream/lib/ASRouterTriggerListeners.jsm

119 строки
3.9 KiB
JavaScript

/* 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";
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
/**
* A Map from trigger IDs to singleton trigger listeners. Each listener must
* have idempotent `init` and `uninit` methods.
*/
this.ASRouterTriggerListeners = new Map([
/**
* Attach listeners to every browser window to detect location changes, and
* notify the trigger handler whenever we navigate to a URL with a hostname
* we're looking for.
*/
["openURL", {
_initialized: false,
_triggerHandler: null,
_hosts: null,
/*
* If the listener is already initialised, `init` will replace the trigger
* handler and add any new hosts to `this._hosts`.
*/
init(triggerHandler, hosts = []) {
if (!this._initialized) {
this.onLocationChange = this.onLocationChange.bind(this);
// Listen for new windows being opened
Services.ww.registerNotification(this);
// Add listeners to all existing browser windows
for (let win of Services.wm.getEnumerator("navigator:browser")) {
if (win.closed || PrivateBrowsingUtils.isWindowPrivate(win)) {
continue;
}
win.gBrowser.addTabsProgressListener(this);
}
this._initialized = true;
}
this._triggerHandler = triggerHandler;
if (this._hosts) {
hosts.forEach(h => this._hosts.add(h));
} else {
this._hosts = new Set(hosts); // Clone the hosts to avoid unexpected behaviour
}
},
uninit() {
if (this._initialized) {
Services.ww.unregisterNotification(this);
for (let win of Services.wm.getEnumerator("navigator:browser")) {
if (win.closed || PrivateBrowsingUtils.isWindowPrivate(win)) {
continue;
}
win.gBrowser.removeTabsProgressListener(this);
}
this._initialized = false;
this._triggerHandler = null;
this._hosts = null;
}
},
onLocationChange(aBrowser, aWebProgress, aRequest, aLocationURI, aFlags) {
const location = aLocationURI ? aLocationURI.spec : "";
// Some websites trigger redirect events after they finish loading even
// though the location remains the same. This results in onLocationChange
// events to be fired twice.
const isSameDocument = !!(aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT);
if (location && aWebProgress.isTopLevel && !isSameDocument) {
try {
const host = (new URL(location)).hostname;
if (this._hosts.has(host)) {
this._triggerHandler(aBrowser, {id: "openURL", param: host});
}
} catch (e) {} // Couldn't parse location URL
}
},
observe(win, topic, data) {
let onLoad;
switch (topic) {
case "domwindowopened":
if (!(win instanceof Ci.nsIDOMWindow) || win.closed || PrivateBrowsingUtils.isWindowPrivate(win)) {
break;
}
onLoad = () => {
// Ignore non-browser windows.
if (win.document.documentElement.getAttribute("windowtype") === "navigator:browser") {
win.gBrowser.addTabsProgressListener(this);
}
};
win.addEventListener("load", onLoad, {once: true});
break;
case "domwindowclosed":
if ((win instanceof Ci.nsIDOMWindow) &&
win.document.documentElement.getAttribute("windowtype") === "navigator:browser") {
win.gBrowser.removeTabsProgressListener(this);
}
break;
}
},
}],
]);
const EXPORTED_SYMBOLS = ["ASRouterTriggerListeners"];