748 lines
25 KiB
JavaScript
748 lines
25 KiB
JavaScript
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
|
/* 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/. */
|
|
|
|
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
|
|
|
|
const STRING_BUNDLE_URI = "chrome://browser/locale/feeds/subscribe.properties";
|
|
|
|
export function WebProtocolHandlerRegistrar() {}
|
|
|
|
const lazy = {};
|
|
|
|
XPCOMUtils.defineLazyServiceGetters(lazy, {
|
|
ExternalProtocolService: [
|
|
"@mozilla.org/uriloader/external-protocol-service;1",
|
|
"nsIExternalProtocolService",
|
|
],
|
|
});
|
|
|
|
ChromeUtils.defineESModuleGetters(lazy, {
|
|
NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
|
|
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
|
|
});
|
|
|
|
ChromeUtils.defineLazyGetter(lazy, "log", () => {
|
|
let { ConsoleAPI } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/Console.sys.mjs"
|
|
);
|
|
let consoleOptions = {
|
|
// tip: set maxLogLevel to "debug" and use lazy.log.debug() to create
|
|
// detailed messages during development. See LOG_LEVELS in Console.sys.mjs
|
|
// for details.
|
|
maxLogLevel: "warning",
|
|
maxLogLevelPref: "browser.protocolhandler.loglevel",
|
|
prefix: "WebProtocolHandlerRegistrar.sys.mjs",
|
|
};
|
|
return new ConsoleAPI(consoleOptions);
|
|
});
|
|
|
|
WebProtocolHandlerRegistrar.prototype = {
|
|
get stringBundle() {
|
|
let sb = Services.strings.createBundle(STRING_BUNDLE_URI);
|
|
delete WebProtocolHandlerRegistrar.prototype.stringBundle;
|
|
return (WebProtocolHandlerRegistrar.prototype.stringBundle = sb);
|
|
},
|
|
|
|
_getFormattedString(key, params) {
|
|
return this.stringBundle.formatStringFromName(key, params);
|
|
},
|
|
|
|
_getString(key) {
|
|
return this.stringBundle.GetStringFromName(key);
|
|
},
|
|
|
|
/* Because we want to iterate over the known webmailers in the observe method
|
|
* and with each site visited, we want to check as fast as possible if the
|
|
* current site is already registered as a mailto handler. Using the sites
|
|
* domain name as a key ensures that we can use Map.has(...) later to find
|
|
* it.
|
|
*/
|
|
_addedObservers: 0,
|
|
_knownWebmailerCache: new Map(),
|
|
_ensureWebmailerCache() {
|
|
this._knownWebmailerCache = new Map();
|
|
|
|
const handler =
|
|
lazy.ExternalProtocolService.getProtocolHandlerInfo("mailto");
|
|
|
|
for (const h of handler.possibleApplicationHandlers.enumerate()) {
|
|
// Services.io.newURI could fail for broken handlers in which case we
|
|
// simply leave them out, but write a debug message (just in case)
|
|
try {
|
|
if (h instanceof Ci.nsIWebHandlerApp && h.uriTemplate) {
|
|
const mailerUri = Services.io.newURI(h.uriTemplate);
|
|
if (mailerUri.scheme == "https") {
|
|
this._knownWebmailerCache.set(
|
|
Services.io.newURI(h.uriTemplate).host,
|
|
{
|
|
uriPath: Services.io.newURI(h.uriTemplate).resolve("."),
|
|
uriTemplate: Services.io.newURI(h.uriTemplate),
|
|
name: h.name,
|
|
}
|
|
);
|
|
}
|
|
}
|
|
} catch (e) {
|
|
lazy.log.debug(`Could not add ${h.uriTemplate} to cache: ${e.message}`);
|
|
}
|
|
}
|
|
},
|
|
|
|
/* This function can be called multiple times and (re-)initializes the cache
|
|
* if the feature is toggled on. If called with the feature off it will also
|
|
* unregister the observers.
|
|
*
|
|
* @param {boolean} firstInit
|
|
*
|
|
*/
|
|
init(firstInit = false) {
|
|
if (firstInit) {
|
|
lazy.NimbusFeatures.mailto.onUpdate(() =>
|
|
// make firstInit explicitly false to avoid multiple registrations.
|
|
this.init(false)
|
|
);
|
|
}
|
|
|
|
const observers = ["mailto::onLocationChange", "mailto::onClearCache"];
|
|
if (
|
|
lazy.NimbusFeatures.mailto.getVariable("dualPrompt") &&
|
|
lazy.NimbusFeatures.mailto.getVariable("dualPrompt.onLocationChange")
|
|
) {
|
|
this._ensureWebmailerCache();
|
|
// Make sure, that our local observers are never registered twice:
|
|
if (0 == this._addedObservers) {
|
|
observers.forEach(o => {
|
|
this._addedObservers++;
|
|
Services.obs.addObserver(this, o);
|
|
});
|
|
lazy.log.debug(`mailto observers activated: [${observers}]`);
|
|
}
|
|
} else {
|
|
// With `dualPrompt` and `dualPrompt.onLocationChange` toggled on we get
|
|
// up to two notifications when we turn the feature off again, but we
|
|
// only want to unregister the observers once.
|
|
//
|
|
// Using `hasObservers` would allow us to loop over all observers as long
|
|
// as there are more, but hasObservers is not implemented hence why we
|
|
// use `enumerateObservers` here to create the loop and `hasMoreElements`
|
|
// to return true or false as `hasObservers` would if it existed.
|
|
observers.forEach(o => {
|
|
if (
|
|
0 < this._addedObservers &&
|
|
Services.obs.enumerateObservers(o).hasMoreElements()
|
|
) {
|
|
Services.obs.removeObserver(this, o);
|
|
this._addedObservers--;
|
|
lazy.log.debug(`mailto observer "${o}" deactivated.`);
|
|
}
|
|
});
|
|
}
|
|
},
|
|
|
|
async observe(aBrowser, aTopic) {
|
|
try {
|
|
switch (aTopic) {
|
|
case "mailto::onLocationChange": {
|
|
// registerProtocolHandler only works for https
|
|
const uri = aBrowser.currentURI;
|
|
if (!uri?.schemeIs("https")) {
|
|
return;
|
|
}
|
|
|
|
const host = uri.host;
|
|
if (this._knownWebmailerCache.has(host)) {
|
|
// second: search the cache for an entry which starts with the path
|
|
// of the current uri. If it exists we identified the current page as
|
|
// webmailer (again).
|
|
const value = this._knownWebmailerCache.get(host);
|
|
this._askUserToSetMailtoHandler(
|
|
aBrowser,
|
|
"mailto",
|
|
value.uriTemplate,
|
|
value.name
|
|
);
|
|
}
|
|
break; // the switch(topic) statement
|
|
}
|
|
case "mailto::onClearCache":
|
|
// clear the cache for now. We could try to dynamically update the
|
|
// cache, which is easy if a webmailer is added to the settings, but
|
|
// becomes more complicated when webmailers are removed, because then
|
|
// the store gets rewritten and we would require an event to deal with
|
|
// that as well. So instead we recreate it entirely.
|
|
this._ensureWebmailerCache();
|
|
break;
|
|
default:
|
|
lazy.log.debug(`observe reached with unknown topic: ${aTopic}`);
|
|
}
|
|
} catch (e) {
|
|
lazy.log.debug(`Problem in observer: ${e}`);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* See nsIWebProtocolHandlerRegistrar
|
|
*/
|
|
removeProtocolHandler(aProtocol, aURITemplate) {
|
|
let handlerInfo =
|
|
lazy.ExternalProtocolService.getProtocolHandlerInfo(aProtocol);
|
|
let handlers = handlerInfo.possibleApplicationHandlers;
|
|
for (let i = 0; i < handlers.length; i++) {
|
|
try {
|
|
// We only want to test web handlers
|
|
let handler = handlers.queryElementAt(i, Ci.nsIWebHandlerApp);
|
|
if (handler.uriTemplate == aURITemplate) {
|
|
handlers.removeElementAt(i);
|
|
let hs = Cc["@mozilla.org/uriloader/handler-service;1"].getService(
|
|
Ci.nsIHandlerService
|
|
);
|
|
hs.store(handlerInfo);
|
|
return;
|
|
}
|
|
} catch (e) {
|
|
/* it wasn't a web handler */
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Determines if a web handler is already registered.
|
|
*
|
|
* @param {string} aProtocol
|
|
* The scheme of the web handler we are checking for.
|
|
* @param {string} aURITemplate
|
|
* The URI template that the handler uses to handle the protocol.
|
|
* @returns {boolean} true if it is already registered, false otherwise.
|
|
*/
|
|
_protocolHandlerRegistered(aProtocol, aURITemplate) {
|
|
let handlerInfo =
|
|
lazy.ExternalProtocolService.getProtocolHandlerInfo(aProtocol);
|
|
let handlers = handlerInfo.possibleApplicationHandlers;
|
|
for (let handler of handlers.enumerate()) {
|
|
// We only want to test web handlers
|
|
if (
|
|
handler instanceof Ci.nsIWebHandlerApp &&
|
|
handler.uriTemplate == aURITemplate
|
|
) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
|
|
/**
|
|
* Returns true if aURITemplate.spec points to the currently configured
|
|
* handler for aProtocol links and the OS default is also readily configured.
|
|
* Returns false if some of it can be made default.
|
|
*
|
|
* @param {string} aProtocol
|
|
* The scheme of the web handler we are checking for.
|
|
* @param {string} aURITemplate
|
|
* The URI template that the handler uses to handle the protocol.
|
|
*/
|
|
_isProtocolHandlerDefault(aProtocol, aURITemplate) {
|
|
const handlerInfo =
|
|
lazy.ExternalProtocolService.getProtocolHandlerInfo(aProtocol);
|
|
|
|
if (
|
|
handlerInfo.preferredAction == Ci.nsIHandlerInfo.useHelperApp &&
|
|
handlerInfo.preferredApplicationHandler instanceof Ci.nsIWebHandlerApp
|
|
) {
|
|
let webHandlerApp =
|
|
handlerInfo.preferredApplicationHandler.QueryInterface(
|
|
Ci.nsIWebHandlerApp
|
|
);
|
|
|
|
// If we are already configured as default, we cannot set a new default
|
|
// and if the current site is already registered as default webmailer we
|
|
// are fully set up as the default app for webmail.
|
|
if (
|
|
!this._canSetOSDefault(aProtocol) &&
|
|
webHandlerApp.uriTemplate == aURITemplate.spec
|
|
) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
|
|
/**
|
|
* Private method to return the installHash, which is important for app
|
|
* registration on OS level. Without it apps cannot be default/handler apps
|
|
* under Windows. Because this value is used to check if its possible to reset
|
|
* the default and to actually set it as well, this function puts the
|
|
* acquisition of the installHash in one place in the hopes that check and set
|
|
* conditions will never deviate.
|
|
*
|
|
* @returns {string} installHash
|
|
*/
|
|
_getInstallHash() {
|
|
const xreDirProvider = Cc[
|
|
"@mozilla.org/xre/directory-provider;1"
|
|
].getService(Ci.nsIXREDirProvider);
|
|
return xreDirProvider.getInstallHash();
|
|
},
|
|
|
|
/* Private method to check if we are already the default protocolhandler
|
|
* for `protocol`.
|
|
*
|
|
* @param {string} protocol name, e.g. mailto (without ://)
|
|
* @returns {boolean}
|
|
*/
|
|
_isOsDefault(protocol) {
|
|
let shellService = Cc[
|
|
"@mozilla.org/browser/shell-service;1"
|
|
].createInstance(Ci.nsIWindowsShellService);
|
|
|
|
if (shellService.isDefaultHandlerFor(protocol)) {
|
|
lazy.log.debug("_isOsDefault returns true.");
|
|
return true;
|
|
}
|
|
|
|
lazy.log.debug("_isOsDefault returns false.");
|
|
return false;
|
|
},
|
|
|
|
/**
|
|
* Private method to determine if we can set a new OS default for a certain
|
|
* protocol.
|
|
*
|
|
* @param {string} protocol name, e.g. mailto (without ://)
|
|
* @returns {boolean}
|
|
*/
|
|
_canSetOSDefault(protocol) {
|
|
// an installHash is required for the association with a scheme handler,
|
|
// also see _setOSDefault()
|
|
if ("" == this._getInstallHash()) {
|
|
lazy.log.debug("_canSetOSDefault returns false.");
|
|
return false;
|
|
}
|
|
|
|
if (this._isOsDefault(protocol)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
},
|
|
|
|
/**
|
|
* Private method to reset the OS default for a certain protocol/uri scheme.
|
|
* We basically ignore, that setDefaultExtensionHandlersUserChoice can fail
|
|
* when the installHash is wrong or cannot be determined.
|
|
*
|
|
* @param {string} protocol name, e.g. mailto (without ://)
|
|
* @returns {boolean}
|
|
*/
|
|
_setOSDefault(protocol) {
|
|
try {
|
|
let defaultAgent = Cc["@mozilla.org/default-agent;1"].createInstance(
|
|
Ci.nsIDefaultAgent
|
|
);
|
|
defaultAgent.setDefaultExtensionHandlersUserChoice(
|
|
this._getInstallHash(),
|
|
[protocol, "FirefoxURL"]
|
|
);
|
|
return true;
|
|
} catch (e) {
|
|
// TODO: why could not we just add the installHash and promote the running
|
|
// install to be a properly installed one?
|
|
lazy.log.debug(
|
|
"Could not set Firefox as default application for " +
|
|
protocol +
|
|
", because: " +
|
|
e.message
|
|
);
|
|
}
|
|
return false;
|
|
},
|
|
|
|
/**
|
|
* Private method to set the default uri to handle a certain protocol. This
|
|
* automates in a way what a user can do in settings under applications,
|
|
* where different 'actions' can be chosen for different 'content types'.
|
|
*
|
|
* @param {string} protocol
|
|
* @param {handler} handler
|
|
*/
|
|
_setProtocolHandlerDefault(protocol, handler) {
|
|
let handlerInfo =
|
|
lazy.ExternalProtocolService.getProtocolHandlerInfo(protocol);
|
|
handlerInfo.preferredAction = Ci.nsIHandlerInfo.useHelperApp;
|
|
handlerInfo.preferredApplicationHandler = handler;
|
|
handlerInfo.alwaysAskBeforeHandling = false;
|
|
let hs = Cc["@mozilla.org/uriloader/handler-service;1"].getService(
|
|
Ci.nsIHandlerService
|
|
);
|
|
hs.store(handlerInfo);
|
|
return handlerInfo;
|
|
},
|
|
|
|
/**
|
|
* Private method to add a ProtocolHandler of type nsIWebHandlerApp to the
|
|
* list of possible handlers for a protocol.
|
|
*
|
|
* @param {string} protocol - e.g. 'mailto', so again without ://
|
|
* @param {string} name - the protocol associated 'Action'
|
|
* @param {string} uri - the uri (compare 'use other...' in the preferences)
|
|
* @returns {handler} handler - either the existing one or a newly created
|
|
*/
|
|
_addWebProtocolHandler(protocol, name, uri) {
|
|
let phi = lazy.ExternalProtocolService.getProtocolHandlerInfo(protocol);
|
|
// not adding duplicates and bail out with the existing entry
|
|
for (let h of phi.possibleApplicationHandlers.enumerate()) {
|
|
if (h instanceof Ci.nsIWebHandlerApp && h.uriTemplate === uri) {
|
|
return h;
|
|
}
|
|
}
|
|
|
|
let handler = Cc["@mozilla.org/uriloader/web-handler-app;1"].createInstance(
|
|
Ci.nsIWebHandlerApp
|
|
);
|
|
handler.name = name;
|
|
handler.uriTemplate = uri;
|
|
|
|
let handlerInfo =
|
|
lazy.ExternalProtocolService.getProtocolHandlerInfo(protocol);
|
|
handlerInfo.possibleApplicationHandlers.appendElement(handler);
|
|
|
|
let hs = Cc["@mozilla.org/uriloader/handler-service;1"].getService(
|
|
Ci.nsIHandlerService
|
|
);
|
|
hs.store(handlerInfo);
|
|
|
|
return handler;
|
|
},
|
|
|
|
/*
|
|
* Function to store a value associated to a domain using the content pref
|
|
* service.
|
|
*
|
|
* @param {string} domain: the domain for this setting
|
|
* @param {string} setting: the name of the setting
|
|
* @param {string} value: the actual setting to be stored
|
|
* @param {string} context (optional): private window or not
|
|
* @returns {string} the stored preference (see: nsIContentPrefService2.idl)
|
|
*/
|
|
async _saveSiteSpecificSetting(domain, setting, value, context = null) {
|
|
const gContentPrefs = Cc["@mozilla.org/content-pref/service;1"].getService(
|
|
Ci.nsIContentPrefService2
|
|
);
|
|
|
|
return new Promise((resolve, reject) => {
|
|
gContentPrefs.set(domain, setting, value, context, {
|
|
handleResult(pref) {
|
|
resolve(pref);
|
|
},
|
|
handleCompletion() {},
|
|
handleError(err) {
|
|
reject(err);
|
|
},
|
|
});
|
|
});
|
|
},
|
|
|
|
/*
|
|
* Function to return a stored value from the content pref service. Returns
|
|
* a promise, so await can be used to synchonize the retrieval.
|
|
*
|
|
* @param {string} domain: the domain for this setting
|
|
* @param {string} setting: the name of the setting
|
|
* @param {string} context (optional): private window or not
|
|
* @param {string} def (optional): the default value to return
|
|
* @returns {string} either stored value or ""
|
|
*/
|
|
async _getSiteSpecificSetting(domain, setting, context = null, def = null) {
|
|
const gContentPrefs = Cc["@mozilla.org/content-pref/service;1"].getService(
|
|
Ci.nsIContentPrefService2
|
|
);
|
|
|
|
return await new Promise((resolve, reject) => {
|
|
gContentPrefs.getByDomainAndName(domain, setting, context, {
|
|
_result: def,
|
|
handleResult(pref) {
|
|
this._result = pref.value;
|
|
},
|
|
handleCompletion(_) {
|
|
resolve(this._result);
|
|
},
|
|
handleError(err) {
|
|
reject(err);
|
|
},
|
|
});
|
|
});
|
|
},
|
|
|
|
/**
|
|
* See nsIWebProtocolHandlerRegistrar
|
|
*/
|
|
registerProtocolHandler(
|
|
aProtocol,
|
|
aURI,
|
|
aTitle,
|
|
aDocumentURI,
|
|
aBrowserOrWindow
|
|
) {
|
|
// first mitigation: check if the API call comes from another domain
|
|
aProtocol = (aProtocol || "").toLowerCase();
|
|
if (!aURI || !aDocumentURI) {
|
|
return;
|
|
}
|
|
|
|
let browser = aBrowserOrWindow; // This is the e10s case.
|
|
if (aBrowserOrWindow instanceof Ci.nsIDOMWindow) {
|
|
// In the non-e10s case, grab the browser off the same-process window.
|
|
let rootDocShell = aBrowserOrWindow.docShell.sameTypeRootTreeItem;
|
|
browser = rootDocShell.QueryInterface(Ci.nsIDocShell).chromeEventHandler;
|
|
}
|
|
|
|
let browserWindow = browser.ownerGlobal;
|
|
try {
|
|
browserWindow.navigator.checkProtocolHandlerAllowed(
|
|
aProtocol,
|
|
aURI,
|
|
aDocumentURI
|
|
);
|
|
} catch (ex) {
|
|
// We should have already shown the user an error.
|
|
return;
|
|
}
|
|
if (lazy.NimbusFeatures.mailto.getVariable("dualPrompt")) {
|
|
if ("mailto" === aProtocol) {
|
|
lazy.NimbusFeatures.mailto.recordExposureEvent();
|
|
this._askUserToSetMailtoHandler(browser, aProtocol, aURI, aTitle);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// If the protocol handler is already registered, just return early.
|
|
if (this._protocolHandlerRegistered(aProtocol, aURI.spec)) {
|
|
return;
|
|
}
|
|
|
|
// Now Ask the user and provide the proper callback
|
|
let message = this._getFormattedString("addProtocolHandlerMessage", [
|
|
aURI.host,
|
|
aProtocol,
|
|
]);
|
|
|
|
let notificationIcon = aURI.prePath + "/favicon.ico";
|
|
let notificationValue = "Protocol Registration: " + aProtocol;
|
|
let addButton = {
|
|
label: this._getString("addProtocolHandlerAddButton"),
|
|
accessKey: this._getString("addProtocolHandlerAddButtonAccesskey"),
|
|
protocolInfo: { protocol: aProtocol, uri: aURI.spec, name: aTitle },
|
|
primary: true,
|
|
|
|
callback(aNotification, aButtonInfo) {
|
|
let protocol = aButtonInfo.protocolInfo.protocol;
|
|
let name = aButtonInfo.protocolInfo.name;
|
|
|
|
let handler = Cc[
|
|
"@mozilla.org/uriloader/web-handler-app;1"
|
|
].createInstance(Ci.nsIWebHandlerApp);
|
|
handler.name = name;
|
|
handler.uriTemplate = aButtonInfo.protocolInfo.uri;
|
|
|
|
let handlerInfo =
|
|
lazy.ExternalProtocolService.getProtocolHandlerInfo(protocol);
|
|
handlerInfo.possibleApplicationHandlers.appendElement(handler);
|
|
|
|
// Since the user has agreed to add a new handler, chances are good
|
|
// that the next time they see a handler of this type, they're going
|
|
// to want to use it. Reset the handlerInfo to ask before the next
|
|
// use.
|
|
handlerInfo.alwaysAskBeforeHandling = true;
|
|
|
|
let hs = Cc["@mozilla.org/uriloader/handler-service;1"].getService(
|
|
Ci.nsIHandlerService
|
|
);
|
|
hs.store(handlerInfo);
|
|
},
|
|
};
|
|
|
|
let notificationBox = browser.getTabBrowser().getNotificationBox(browser);
|
|
|
|
// check if the notification box is already shown
|
|
if (notificationBox.getNotificationWithValue(notificationValue)) {
|
|
return;
|
|
}
|
|
|
|
notificationBox.appendNotification(
|
|
notificationValue,
|
|
{
|
|
label: message,
|
|
image: notificationIcon,
|
|
priority: notificationBox.PRIORITY_INFO_LOW,
|
|
},
|
|
[addButton]
|
|
);
|
|
},
|
|
|
|
/*
|
|
* Special implementation for mailto: A prompt (notificationbox.js) is only
|
|
* shown if there is a realistic chance that we can really set the OS default,
|
|
* e.g. if we have been properly installed and the current page is not already
|
|
* the default and we have not asked users too often the same question.
|
|
*
|
|
* @param {string} browser
|
|
* @param {string} aProtocol
|
|
* @param {nsIURI} aURI
|
|
* @param {string} aTitle
|
|
*/
|
|
async _askUserToSetMailtoHandler(browser, aProtocol, aURI, aTitle) {
|
|
let notificationId = "OS Protocol Registration: " + aProtocol;
|
|
|
|
// guard: we do not want to reconfigure settings in private browsing mode
|
|
if (lazy.PrivateBrowsingUtils.isWindowPrivate(browser.ownerGlobal)) {
|
|
lazy.log.debug("prompt not shown, because this is a private window.");
|
|
return;
|
|
}
|
|
|
|
// guard: check if everything has been configured to use the current site
|
|
// as default webmailer and bail out if so.
|
|
if (this._isProtocolHandlerDefault(aProtocol, aURI)) {
|
|
lazy.log.debug(
|
|
`prompt not shown, because ${aTitle} is already configured to` +
|
|
` handle ${aProtocol}-links under ${aURI.spec}.`
|
|
);
|
|
return;
|
|
}
|
|
|
|
// guard: bail out if this site has been dismissed before (either by
|
|
// clicking the 'x' button or the 'not now' button.
|
|
let principal = browser.getTabBrowser().contentPrincipal;
|
|
if (
|
|
Ci.nsIPermissionManager.DENY_ACTION ==
|
|
Services.perms.testExactPermissionFromPrincipal(
|
|
principal,
|
|
"mailto-infobar-dismissed"
|
|
)
|
|
) {
|
|
let expiry =
|
|
Services.perms.getPermissionObject(
|
|
principal,
|
|
"mailto-infobar-dismissed",
|
|
true
|
|
).expireTime - Date.now();
|
|
|
|
lazy.log.debug(
|
|
`prompt not shown, because it is still dismissed for` +
|
|
` ${principal.host} and will be shown in` +
|
|
` ${(expiry / 1000).toFixed()} seconds again.`
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Now show the prompt if there is not already one...
|
|
let osDefaultNotificationBox = browser
|
|
.getTabBrowser()
|
|
.getNotificationBox(browser);
|
|
|
|
if (!osDefaultNotificationBox.getNotificationWithValue(notificationId)) {
|
|
let win = browser.ownerGlobal;
|
|
win.MozXULElement.insertFTLIfNeeded("browser/webProtocolHandler.ftl");
|
|
|
|
let notification = await osDefaultNotificationBox.appendNotification(
|
|
notificationId,
|
|
{
|
|
label: {
|
|
"l10n-id": "protocolhandler-mailto-handler-set",
|
|
"l10n-args": { url: aURI.host },
|
|
},
|
|
priority: osDefaultNotificationBox.PRIORITY_INFO_LOW,
|
|
eventCallback: eventType => {
|
|
if (eventType === "dismissed") {
|
|
// after a click on 'X' save a timestamp after which we can show
|
|
// the prompt again
|
|
Services.perms.addFromPrincipal(
|
|
principal,
|
|
"mailto-infobar-dismissed",
|
|
Ci.nsIPermissionManager.DENY_ACTION,
|
|
Ci.nsIPermissionManager.EXPIRE_TIME,
|
|
lazy.NimbusFeatures.mailto.getVariable(
|
|
"dualPrompt.dismissXClickMinutes"
|
|
) *
|
|
60 *
|
|
1000 +
|
|
Date.now()
|
|
);
|
|
Glean.protocolhandlerMailto.promptClicked.dismiss_os_default.add();
|
|
}
|
|
},
|
|
},
|
|
[
|
|
{
|
|
"l10n-id": "protocolhandler-mailto-os-handler-yes-button",
|
|
primary: true,
|
|
callback: newitem => {
|
|
let currentHandler = this._addWebProtocolHandler(
|
|
aProtocol,
|
|
aTitle,
|
|
aURI.spec
|
|
);
|
|
this._setProtocolHandlerDefault(aProtocol, currentHandler);
|
|
Glean.protocolhandlerMailto.promptClicked.set_local_default.add();
|
|
|
|
if (this._canSetOSDefault(aProtocol)) {
|
|
if (this._setOSDefault(aProtocol)) {
|
|
Glean.protocolhandlerMailto.promptClicked.set_os_default.add();
|
|
newitem.messageL10nId =
|
|
"protocolhandler-mailto-handler-confirm";
|
|
newitem.removeChild(newitem.buttonContainer);
|
|
newitem.setAttribute("type", "success"); // from moz-message-bar.css
|
|
newitem.eventCallback = null; // disable show only once per day for success
|
|
return true; // `true` does not hide the bar
|
|
}
|
|
|
|
// if anything goes wrong with setting the OS default, we want
|
|
// to be informed so that we can fix it.
|
|
Glean.protocolhandlerMailto.promptClicked.set_os_default_error.add();
|
|
return false;
|
|
}
|
|
|
|
// if the installation does not have an install hash, we cannot
|
|
// set the OS default, but mailto links from within the browser
|
|
// should still work.
|
|
Glean.protocolhandlerMailto.promptClicked.set_os_default_impossible.add();
|
|
return false;
|
|
},
|
|
},
|
|
{
|
|
"l10n-id": "protocolhandler-mailto-os-handler-no-button",
|
|
callback: () => {
|
|
// after a click on 'Not Now' save a timestamp after which we can
|
|
// show the prompt again
|
|
Services.perms.addFromPrincipal(
|
|
principal,
|
|
"mailto-infobar-dismissed",
|
|
Ci.nsIPermissionManager.DENY_ACTION,
|
|
Ci.nsIPermissionManager.EXPIRE_TIME,
|
|
lazy.NimbusFeatures.mailto.getVariable(
|
|
"dualPrompt.dismissNotNowMinutes"
|
|
) *
|
|
60 *
|
|
1000 +
|
|
Date.now()
|
|
);
|
|
Glean.protocolhandlerMailto.promptClicked.dismiss_os_default.add();
|
|
return false;
|
|
},
|
|
},
|
|
]
|
|
);
|
|
|
|
Glean.protocolhandlerMailto.handlerPromptShown.os_default.add();
|
|
// remove the icon from the infobar, which is automatically assigned
|
|
// after its priority, because the priority is also an indicator which
|
|
// type of bar it is, e.g. a warning or error:
|
|
notification.setAttribute("type", "system");
|
|
}
|
|
},
|
|
|
|
/**
|
|
* See nsISupports
|
|
*/
|
|
QueryInterface: ChromeUtils.generateQI(["nsIWebProtocolHandlerRegistrar"]),
|
|
};
|