mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-11 16:32:59 +00:00
229719aff7
Differential Revision: https://phabricator.services.mozilla.com/D184933
181 lines
6.8 KiB
JavaScript
181 lines
6.8 KiB
JavaScript
/* -*- mode: js; 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 lazy = {};
|
|
|
|
XPCOMUtils.defineLazyPreferenceGetter(
|
|
lazy,
|
|
"trimHttps",
|
|
"browser.urlbar.trimHttps"
|
|
);
|
|
export var BrowserUIUtils = {
|
|
/**
|
|
* Check whether a page can be considered as 'empty', that its URI
|
|
* reflects its origin, and that if it's loaded in a tab, that tab
|
|
* could be considered 'empty' (e.g. like the result of opening
|
|
* a 'blank' new tab).
|
|
*
|
|
* We have to do more than just check the URI, because especially
|
|
* for things like about:blank, it is possible that the opener or
|
|
* some other page has control over the contents of the page.
|
|
*
|
|
* @param {Browser} browser
|
|
* The browser whose page we're checking.
|
|
* @param {nsIURI} [uri]
|
|
* The URI against which we're checking (the browser's currentURI
|
|
* if omitted).
|
|
*
|
|
* @return {boolean} false if the page was opened by or is controlled by
|
|
* arbitrary web content, unless that content corresponds with the URI.
|
|
* true if the page is blank and controlled by a principal matching
|
|
* that URI (or the system principal if the principal has no URI)
|
|
*/
|
|
checkEmptyPageOrigin(browser, uri = browser.currentURI) {
|
|
// If another page opened this page with e.g. window.open, this page might
|
|
// be controlled by its opener.
|
|
if (browser.hasContentOpener) {
|
|
return false;
|
|
}
|
|
let contentPrincipal = browser.contentPrincipal;
|
|
// Not all principals have URIs...
|
|
// There are two special-cases involving about:blank. One is where
|
|
// the user has manually loaded it and it got created with a null
|
|
// principal. The other involves the case where we load
|
|
// some other empty page in a browser and the current page is the
|
|
// initial about:blank page (which has that as its principal, not
|
|
// just URI in which case it could be web-based). Especially in
|
|
// e10s, we need to tackle that case specifically to avoid race
|
|
// conditions when updating the URL bar.
|
|
//
|
|
// Note that we check the documentURI here, since the currentURI on
|
|
// the browser might have been set by SessionStore in order to
|
|
// support switch-to-tab without having actually loaded the content
|
|
// yet.
|
|
let uriToCheck = browser.documentURI || uri;
|
|
if (
|
|
(uriToCheck.spec == "about:blank" && contentPrincipal.isNullPrincipal) ||
|
|
contentPrincipal.spec == "about:blank"
|
|
) {
|
|
return true;
|
|
}
|
|
if (contentPrincipal.isContentPrincipal) {
|
|
return contentPrincipal.equalsURI(uri);
|
|
}
|
|
// ... so for those that don't have them, enforce that the page has the
|
|
// system principal (this matches e.g. on about:newtab).
|
|
return contentPrincipal.isSystemPrincipal;
|
|
},
|
|
|
|
/**
|
|
* Generate a document fragment for a localized string that has DOM
|
|
* node replacements. This avoids using getFormattedString followed
|
|
* by assigning to innerHTML. Fluent can probably replace this when
|
|
* it is in use everywhere.
|
|
*
|
|
* @param {Document} doc
|
|
* @param {String} msg
|
|
* The string to put replacements in. Fetch from
|
|
* a stringbundle using getString or GetStringFromName,
|
|
* or even an inserted dtd string.
|
|
* @param {Node|String} nodesOrStrings
|
|
* The replacement items. Can be a mix of Nodes
|
|
* and Strings. However, for correct behaviour, the
|
|
* number of items provided needs to exactly match
|
|
* the number of replacement strings in the l10n string.
|
|
* @returns {DocumentFragment}
|
|
* A document fragment. In the trivial case (no
|
|
* replacements), this will simply be a fragment with 1
|
|
* child, a text node containing the localized string.
|
|
*/
|
|
getLocalizedFragment(doc, msg, ...nodesOrStrings) {
|
|
// Ensure replacement points are indexed:
|
|
for (let i = 1; i <= nodesOrStrings.length; i++) {
|
|
if (!msg.includes("%" + i + "$S")) {
|
|
msg = msg.replace(/%S/, "%" + i + "$S");
|
|
}
|
|
}
|
|
let numberOfInsertionPoints = msg.match(/%\d+\$S/g).length;
|
|
if (numberOfInsertionPoints != nodesOrStrings.length) {
|
|
console.error(
|
|
`Message has ${numberOfInsertionPoints} insertion points, ` +
|
|
`but got ${nodesOrStrings.length} replacement parameters!`
|
|
);
|
|
}
|
|
|
|
let fragment = doc.createDocumentFragment();
|
|
let parts = [msg];
|
|
let insertionPoint = 1;
|
|
for (let replacement of nodesOrStrings) {
|
|
let insertionString = "%" + insertionPoint++ + "$S";
|
|
let partIndex = parts.findIndex(
|
|
part => typeof part == "string" && part.includes(insertionString)
|
|
);
|
|
if (partIndex == -1) {
|
|
fragment.appendChild(doc.createTextNode(msg));
|
|
return fragment;
|
|
}
|
|
|
|
if (typeof replacement == "string") {
|
|
parts[partIndex] = parts[partIndex].replace(
|
|
insertionString,
|
|
replacement
|
|
);
|
|
} else {
|
|
let [firstBit, lastBit] = parts[partIndex].split(insertionString);
|
|
parts.splice(partIndex, 1, firstBit, replacement, lastBit);
|
|
}
|
|
}
|
|
|
|
// Put everything in a document fragment:
|
|
for (let part of parts) {
|
|
if (typeof part == "string") {
|
|
if (part) {
|
|
fragment.appendChild(doc.createTextNode(part));
|
|
}
|
|
} else {
|
|
fragment.appendChild(part);
|
|
}
|
|
}
|
|
return fragment;
|
|
},
|
|
|
|
removeSingleTrailingSlashFromURL(aURL) {
|
|
// remove single trailing slash for http/https/ftp URLs
|
|
return aURL.replace(/^((?:http|https|ftp):\/\/[^/]+)\/$/, "$1");
|
|
},
|
|
|
|
get trimURLProtocol() {
|
|
return lazy.trimHttps ? "https://" : "http://";
|
|
},
|
|
|
|
/**
|
|
* Returns a URL which has been trimmed by removing 'http://' or 'https://',
|
|
* when the pref 'trimHttps' is set to true, and any trailing slash
|
|
* (in http/https/ftp urls). Note that a trimmed url may not load the same
|
|
* page as the original url, so before loading it, it must be passed through
|
|
* URIFixup, to check trimming doesn't change its destination. We don't run
|
|
* the URIFixup check here, because trimURL is in the page load path
|
|
* (see onLocationChange), so it must be fast and simple.
|
|
*
|
|
* @param {string} aURL The URL to trim.
|
|
* @returns {string} The trimmed string.
|
|
*/
|
|
trimURL(aURL) {
|
|
let url = this.removeSingleTrailingSlashFromURL(aURL);
|
|
return url.startsWith(this.trimURLProtocol)
|
|
? url.substring(this.trimURLProtocol.length)
|
|
: url;
|
|
},
|
|
};
|
|
|
|
XPCOMUtils.defineLazyPreferenceGetter(
|
|
BrowserUIUtils,
|
|
"quitShortcutDisabled",
|
|
"browser.quitShortcut.disabled",
|
|
false
|
|
);
|