fune/toolkit/modules/BrowserUtils.jsm

187 lines
5.6 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/. */
"use strict";
var EXPORTED_SYMBOLS = ["BrowserUtils"];
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
var BrowserUtils = {
/**
* Return or create a principal with the content of one, and the originAttributes
* of an existing principal (e.g. on a docshell, where the originAttributes ought
* not to change, that is, we should keep the userContextId, privateBrowsingId,
* etc. the same when changing the principal).
*
* @param principal
* The principal whose content/null/system-ness we want.
* @param existingPrincipal
* The principal whose originAttributes we want, usually the current
* principal of a docshell.
* @return an nsIPrincipal that matches the content/null/system-ness of the first
* param, and the originAttributes of the second.
*/
principalWithMatchingOA(principal, existingPrincipal) {
// Don't care about system principals:
if (principal.isSystemPrincipal) {
return principal;
}
// If the originAttributes already match, just return the principal as-is.
if (existingPrincipal.originSuffix == principal.originSuffix) {
return principal;
}
let secMan = Services.scriptSecurityManager;
if (principal.isContentPrincipal) {
return secMan.principalWithOA(
principal,
existingPrincipal.originAttributes
);
}
if (principal.isNullPrincipal) {
return secMan.createNullPrincipal(existingPrincipal.originAttributes);
}
throw new Error(
"Can't change the originAttributes of an expanded principal!"
);
},
onBeforeLinkTraversal(originalTarget, linkURI, linkNode, isAppTab) {
// Don't modify non-default targets or targets that aren't in top-level app
// tab docshells (isAppTab will be false for app tab subframes).
if (originalTarget != "" || !isAppTab) {
return originalTarget;
}
// External links from within app tabs should always open in new tabs
// instead of replacing the app tab's page (Bug 575561)
let linkHost;
let docHost;
try {
linkHost = linkURI.host;
docHost = linkNode.ownerDocument.documentURIObject.host;
} catch (e) {
// nsIURI.host can throw for non-nsStandardURL nsIURIs.
// If we fail to get either host, just return originalTarget.
return originalTarget;
}
if (docHost == linkHost) {
return originalTarget;
}
// Special case: ignore "www" prefix if it is part of host string
let [longHost, shortHost] =
linkHost.length > docHost.length
? [linkHost, docHost]
: [docHost, linkHost];
if (longHost == "www." + shortHost) {
return originalTarget;
}
return "_blank";
},
/**
* Returns true if |mimeType| is text-based, or false otherwise.
*
* @param mimeType
* The MIME type to check.
*/
mimeTypeIsTextBased(mimeType) {
return (
mimeType.startsWith("text/") ||
mimeType.endsWith("+xml") ||
mimeType == "application/x-javascript" ||
mimeType == "application/javascript" ||
mimeType == "application/json" ||
mimeType == "application/xml"
);
},
/**
* Returns true if we can show a find bar, including FAYT, for the specified
* document location. The location must not be in a blacklist of specific
* "about:" pages for which find is disabled.
*
* This can be called from the parent process or from content processes.
*/
canFindInPage(location) {
return (
!location.startsWith("about:addons") &&
!location.startsWith(
"chrome://mozapps/content/extensions/aboutaddons.html"
) &&
!location.startsWith("about:preferences")
);
},
isFindbarVisible(docShell) {
const FINDER_JSM = "resource://gre/modules/Finder.jsm";
return (
Cu.isModuleLoaded(FINDER_JSM) &&
ChromeUtils.import(FINDER_JSM).Finder.isFindbarVisible(docShell)
);
},
/**
* Returns a Promise which resolves when the given observer topic has been
* observed.
*
* @param {string} topic
* The topic to observe.
* @param {function(nsISupports, string)} [test]
* An optional test function which, when called with the
* observer's subject and data, should return true if this is the
* expected notification, false otherwise.
* @returns {Promise<object>}
*/
promiseObserved(topic, test = () => true) {
return new Promise(resolve => {
let observer = (subject, topic, data) => {
if (test(subject, data)) {
Services.obs.removeObserver(observer, topic);
resolve({ subject, data });
}
};
Services.obs.addObserver(observer, topic);
});
},
isShareableURL(url) {
if (!url) {
return false;
}
// Disallow sharing URLs with more than 65535 characters.
if (url.spec.length > 65535) {
return false;
}
let scheme = url.scheme;
return !(
"about" == scheme ||
"resource" == scheme ||
"chrome" == scheme ||
"wyciwyg" == scheme ||
"blob" == scheme ||
"moz-extension" == scheme
);
},
};
XPCOMUtils.defineLazyPreferenceGetter(
BrowserUtils,
"navigationRequireUserInteraction",
"browser.navigation.requireUserInteraction",
false
);