Revision control

Copy as Markdown

Other Tools

/* 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 { PlacesUtils } from "resource://gre/modules/PlacesUtils.sys.mjs";
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
const lazy = XPCOMUtils.declareLazy({
SearchService: "moz-src:///toolkit/components/search/SearchService.sys.mjs",
});
/**
* Clone principal and add permission to use the application associated with
* the specified external protocol/scheme.
*
* @param { nsIPrincipal } principal - the principal to clone and attach the
* permission to
* @param {nsIURI} uri - the uri which is to be opened
* @param {OriginAttributesDictionary} [originAttributes]
*
* @returns {nsIPrincipal}
*/
export function getClonedPrincipalWithProtocolPermission(
principal,
uri,
originAttributes
) {
const clone = Services.scriptSecurityManager.principalWithOA(
principal,
originAttributes ?? principal.originAttributes
);
Services.perms.addFromPrincipal(
clone,
`open-protocol-handler^${uri.scheme}`,
Services.perms.ALLOW_ACTION
);
return clone;
}
/**
* Create a general content principal and add permission to use the application
* associated with the specified external protocol/scheme.
*
* @param { nsIURI } uri - the uri which is to be opened
* @returns {nsIPrincipal}
*/
export function getContentPrincipalWithProtocolPermission(uri) {
const principal = Services.scriptSecurityManager.createContentPrincipal(
{}
);
Services.perms.addFromPrincipal(
principal,
`open-protocol-handler^${uri.scheme}`,
Services.perms.ALLOW_ACTION
);
return principal;
}
/**
* Forces a url to open in an external application according to the protocol
* service settings.
*
* @param {string|nsIURI} url - A url string or an nsIURI containing the url to
* open.
* @param {object} [options]
* @param {boolean} [options.addToHistory=true] - Whether to add the opened url
* to the history.
* @param {nsIPrincipal} [options.principal] - A triggering principal.
*/
export function openLinkExternally(url, options) {
const addToHistory = options?.addToHistory ?? true;
let uri = url;
if (!(uri instanceof Ci.nsIURI)) {
uri = Services.io.newURI(url);
}
if (addToHistory) {
// This can fail if there is a problem with the places database.
PlacesUtils.history
.insert({
url, // accepts both string and nsIURI
visits: [
{
date: new Date(),
},
],
})
.catch(console.error);
}
const isHttp = ["http", "https"].includes(uri.scheme);
// Never prompt for http. For file, always prompt.
const promptForPermission =
uri.scheme == "file" ||
(!isHttp &&
Services.prefs.getBoolPref("mail.external_protocol_requires_permission"));
let principal = options?.principal;
if (!promptForPermission) {
principal = principal
? getClonedPrincipalWithProtocolPermission(principal, uri)
: getContentPrincipalWithProtocolPermission(uri);
}
principal ??= Services.scriptSecurityManager.getSystemPrincipal();
Cc["@mozilla.org/uriloader/external-protocol-service;1"]
.getService(Ci.nsIExternalProtocolService)
.loadURI(uri, principal);
}
/**
* Opens the given url in a new tab in the most recent mail window. All provided
* parameters are passed into openTab(), if any.
*
* @param {string} url
* @param {object} [params]
*/
export function openLinkInNewTab(url, params = {}) {
const mailWindow = Services.wm.getMostRecentWindow("mail:3pane");
if (mailWindow) {
mailWindow.focus();
mailWindow.document
.getElementById("tabmail")
.openTab("contentTab", { url, ...params });
}
}
/**
*
* @param {string} query - The string to search for.
*/
export function openWebSearch(query) {
return lazy.SearchService.init().then(async () => {
const engine = await lazy.SearchService.getDefault();
openLinkExternally(engine.getSubmission(query).uri.spec);
Glean.mail.websearchUsage[engine.name.toLowerCase()].add(1);
});
}
/**
* Open a URL from a click listener in the UI only when the primary mouse button
* is pressed.
*
* @param {string|nsIURI} url
* @param {MouseEvent} event
*/
export function openUILink(url, event) {
if (!event.button) {
openLinkExternally(url);
}
}