зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 3 changesets (bug 1711168, bug 1773115) for causing failures at test_ext_web_accessible_resources.html. CLOSED TREE
Backed out changeset 703c909eb009 (bug 1773115) Backed out changeset 902385a7ce60 (bug 1711168) Backed out changeset 3d061ce03de7 (bug 1711168)
This commit is contained in:
Родитель
a8e8878706
Коммит
ae6f4c94fc
|
@ -611,24 +611,6 @@ this.tabs = class extends ExtensionAPIPersistent {
|
|||
return tab;
|
||||
}
|
||||
|
||||
function setContentTriggeringPrincipal(url, browser, options) {
|
||||
// For urls that we want to allow an extension to open in a tab, but
|
||||
// that it may not otherwise have access to, we set the triggering
|
||||
// principal to the url that is being opened. This is used for newtab,
|
||||
// about: and moz-extension: protocols.
|
||||
// We also prevent discarded, or lazy tabs by setting allowInheritPrincipal to false.
|
||||
options.allowInheritPrincipal = false;
|
||||
options.triggeringPrincipal = Services.scriptSecurityManager.createContentPrincipal(
|
||||
Services.io.newURI(url),
|
||||
{
|
||||
userContextId: options.userContextId,
|
||||
privateBrowsingId: PrivateBrowsingUtils.isBrowserPrivate(browser)
|
||||
? 1
|
||||
: 0,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
let tabsApi = {
|
||||
tabs: {
|
||||
onActivated: new EventManager({
|
||||
|
@ -724,11 +706,9 @@ this.tabs = class extends ExtensionAPIPersistent {
|
|||
}
|
||||
}).then(window => {
|
||||
let url;
|
||||
let principal = context.principal;
|
||||
|
||||
let options = {
|
||||
allowInheritPrincipal: true,
|
||||
triggeringPrincipal: context.principal,
|
||||
};
|
||||
let options = {};
|
||||
if (createProperties.cookieStoreId) {
|
||||
// May throw if validation fails.
|
||||
options.userContextId = getUserContextIdForCookieStoreId(
|
||||
|
@ -741,10 +721,7 @@ this.tabs = class extends ExtensionAPIPersistent {
|
|||
if (createProperties.url !== null) {
|
||||
url = context.uri.resolve(createProperties.url);
|
||||
|
||||
if (
|
||||
!url.startsWith("moz-extension://") &&
|
||||
!context.checkLoadURL(url, { dontReportErrors: true })
|
||||
) {
|
||||
if (!context.checkLoadURL(url, { dontReportErrors: true })) {
|
||||
return Promise.reject({ message: `Illegal URL: ${url}` });
|
||||
}
|
||||
|
||||
|
@ -754,10 +731,30 @@ this.tabs = class extends ExtensionAPIPersistent {
|
|||
} else {
|
||||
url = window.BROWSER_NEW_TAB_URL;
|
||||
}
|
||||
// Only set allowInheritPrincipal on discardable urls as it
|
||||
// will override creating a lazy browser. Setting triggeringPrincipal
|
||||
// will ensure other cases are handled, but setting it may prevent
|
||||
// creating about and data urls.
|
||||
let discardable = url && !url.startsWith("about:");
|
||||
// Handle moz-ext separately from the discardable flag to retain prior behavior.
|
||||
if (!discardable || url?.startsWith("moz-extension://")) {
|
||||
setContentTriggeringPrincipal(url, window.gBrowser, options);
|
||||
if (!discardable) {
|
||||
// Make sure things like about:blank and data: URIs never inherit,
|
||||
// and instead always get a NullPrincipal.
|
||||
options.allowInheritPrincipal = false;
|
||||
// Falling back to content here as about: requires it, however is safe.
|
||||
principal = Services.scriptSecurityManager.createContentPrincipal(
|
||||
Services.io.newURI(url),
|
||||
{
|
||||
userContextId: options.userContextId,
|
||||
privateBrowsingId: PrivateBrowsingUtils.isBrowserPrivate(
|
||||
window.gBrowser
|
||||
)
|
||||
? 1
|
||||
: 0,
|
||||
}
|
||||
);
|
||||
} else {
|
||||
options.allowInheritPrincipal = true;
|
||||
options.triggeringPrincipal = context.principal;
|
||||
}
|
||||
|
||||
tabListener.initTabReady();
|
||||
|
@ -817,6 +814,7 @@ this.tabs = class extends ExtensionAPIPersistent {
|
|||
});
|
||||
}
|
||||
|
||||
options.triggeringPrincipal = principal;
|
||||
let nativeTab = window.gBrowser.addTab(url, options);
|
||||
|
||||
if (active) {
|
||||
|
@ -890,6 +888,10 @@ this.tabs = class extends ExtensionAPIPersistent {
|
|||
if (updateProperties.url !== null) {
|
||||
let url = context.uri.resolve(updateProperties.url);
|
||||
|
||||
if (!context.checkLoadURL(url, { dontReportErrors: true })) {
|
||||
return Promise.reject({ message: `Illegal URL: ${url}` });
|
||||
}
|
||||
|
||||
let options = {
|
||||
flags: updateProperties.loadReplace
|
||||
? Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY
|
||||
|
@ -897,15 +899,6 @@ this.tabs = class extends ExtensionAPIPersistent {
|
|||
triggeringPrincipal: context.principal,
|
||||
};
|
||||
|
||||
if (!context.checkLoadURL(url, { dontReportErrors: true })) {
|
||||
// We allow loading top level tabs for "other" extensions.
|
||||
if (url.startsWith("moz-extension://")) {
|
||||
setContentTriggeringPrincipal(url, tabbrowser, options);
|
||||
} else {
|
||||
return Promise.reject({ message: `Illegal URL: ${url}` });
|
||||
}
|
||||
}
|
||||
|
||||
let browser = nativeTab.linkedBrowser;
|
||||
if (nativeTab.linkedPanel) {
|
||||
browser.loadURI(url, options);
|
||||
|
|
|
@ -87,26 +87,6 @@ this.windows = class extends ExtensionAPIPersistent {
|
|||
|
||||
const { windowManager } = extension;
|
||||
|
||||
function getTriggeringPrincipalForUrl(url) {
|
||||
if (context.checkLoadURL(url, { dontReportErrors: true })) {
|
||||
return context.principal;
|
||||
}
|
||||
let window = context.currentWindow || windowTracker.topWindow;
|
||||
// The extension principal cannot directly load about:-URLs except for about:blank, and
|
||||
// possibly some other loads such as moz-extension. Ensure any page set as a home page
|
||||
// will load by using a content principal.
|
||||
return Services.scriptSecurityManager.createContentPrincipal(
|
||||
Services.io.newURI(url),
|
||||
{
|
||||
privateBrowsingId: PrivateBrowsingUtils.isBrowserPrivate(
|
||||
window.gBrowser
|
||||
)
|
||||
? 1
|
||||
: 0,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
windows: {
|
||||
onCreated: new EventManager({
|
||||
|
@ -202,12 +182,7 @@ this.windows = class extends ExtensionAPIPersistent {
|
|||
Ci.nsIMutableArray
|
||||
);
|
||||
|
||||
// Creating a new window allows one single triggering principal for all tabs that
|
||||
// are created in the window. Due to that, if we need a browser principal to load
|
||||
// some urls, we fallback to using a content principal like we do in the tabs api.
|
||||
// Throws if url is an array and any url can't be loaded by the extension principal.
|
||||
let { allowScriptsToClose, principal } = createData;
|
||||
|
||||
let principal = context.principal;
|
||||
if (createData.tabId !== null) {
|
||||
if (createData.url !== null) {
|
||||
throw new ExtensionError(
|
||||
|
@ -256,24 +231,12 @@ this.windows = class extends ExtensionAPIPersistent {
|
|||
let array = Cc["@mozilla.org/array;1"].createInstance(
|
||||
Ci.nsIMutableArray
|
||||
);
|
||||
for (let url of createData.url.map(u => context.uri.resolve(u))) {
|
||||
// We can only provide a single triggering principal when
|
||||
// opening a window, so if the extension cannot normally
|
||||
// access a url, we fail. This includes about and moz-ext
|
||||
// urls.
|
||||
if (!context.checkLoadURL(url, { dontReportErrors: true })) {
|
||||
return Promise.reject({ message: `Illegal URL: ${url}` });
|
||||
}
|
||||
for (let url of createData.url) {
|
||||
array.appendElement(mkstr(url));
|
||||
}
|
||||
args.appendElement(array);
|
||||
} else {
|
||||
let url = context.uri.resolve(createData.url);
|
||||
args.appendElement(mkstr(url));
|
||||
principal = getTriggeringPrincipalForUrl(url);
|
||||
if (allowScriptsToClose === null) {
|
||||
allowScriptsToClose = url.startsWith("moz-extension://");
|
||||
}
|
||||
args.appendElement(mkstr(createData.url));
|
||||
}
|
||||
} else {
|
||||
let url =
|
||||
|
@ -282,7 +245,15 @@ this.windows = class extends ExtensionAPIPersistent {
|
|||
? "about:privatebrowsing"
|
||||
: HomePage.get().split("|", 1)[0];
|
||||
args.appendElement(mkstr(url));
|
||||
principal = getTriggeringPrincipalForUrl(url);
|
||||
|
||||
if (
|
||||
url.startsWith("about:") &&
|
||||
!context.checkLoadURL(url, { dontReportErrors: true })
|
||||
) {
|
||||
// The extension principal cannot directly load about:-URLs,
|
||||
// except for about:blank. So use the system principal instead.
|
||||
principal = Services.scriptSecurityManager.getSystemPrincipal();
|
||||
}
|
||||
}
|
||||
|
||||
args.appendElement(null); // extraOptions
|
||||
|
@ -300,7 +271,6 @@ this.windows = class extends ExtensionAPIPersistent {
|
|||
createData.cookieStoreId,
|
||||
createData.incognito
|
||||
);
|
||||
|
||||
args.appendElement(userContextIdSupports); // userContextId
|
||||
} else {
|
||||
args.appendElement(null);
|
||||
|
@ -346,6 +316,12 @@ this.windows = class extends ExtensionAPIPersistent {
|
|||
}
|
||||
}
|
||||
|
||||
let { allowScriptsToClose, url } = createData;
|
||||
if (allowScriptsToClose === null) {
|
||||
allowScriptsToClose =
|
||||
typeof url === "string" && url.startsWith("moz-extension://");
|
||||
}
|
||||
|
||||
let window = Services.ww.openWindow(
|
||||
null,
|
||||
AppConstants.BROWSER_CHROME_URL,
|
||||
|
|
|
@ -252,10 +252,10 @@
|
|||
"description": "A URL or array of URLs to open as tabs in the window. Fully-qualified URLs must include a scheme (i.e. 'http://www.google.com', not 'www.google.com'). Relative URLs will be relative to the current page within the extension. Defaults to the New Tab Page.",
|
||||
"optional": true,
|
||||
"choices": [
|
||||
{ "type": "string" },
|
||||
{ "type": "string", "format": "relativeUrl" },
|
||||
{
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
"items": { "type": "string", "format": "relativeUrl" }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
requestLongerTimeout(4);
|
||||
|
||||
const { XPCOMUtils } = ChromeUtils.importESModule(
|
||||
"resource://gre/modules/XPCOMUtils.sys.mjs"
|
||||
);
|
||||
|
@ -719,133 +717,3 @@ add_task(async function test_overriding_home_page_incognito_external() {
|
|||
|
||||
await extension.unload();
|
||||
});
|
||||
|
||||
// This tests that the homepage provided by an extension can be opened by any extension
|
||||
// and does not require web_accessible_resource entries.
|
||||
async function _test_overriding_home_page_open(manifest_version) {
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
manifest_version,
|
||||
chrome_settings_overrides: { homepage: "home.html" },
|
||||
name: "homepage provider",
|
||||
applications: {
|
||||
gecko: { id: "homepage@mochitest" },
|
||||
},
|
||||
},
|
||||
files: {
|
||||
"home.html": `<h1>Home Page!</h1><pre id="result"></pre><script src="home.js"></script>`,
|
||||
"home.js": () => {
|
||||
document.querySelector("#result").textContent = "homepage loaded";
|
||||
},
|
||||
},
|
||||
useAddonManager: "permanent",
|
||||
});
|
||||
|
||||
await extension.startup();
|
||||
|
||||
// ensure it works and deal with initial panel prompt.
|
||||
await testHomePageWindow({
|
||||
expectPanel: true,
|
||||
async test(win) {
|
||||
Assert.equal(
|
||||
HomePage.get(win),
|
||||
`moz-extension://${extension.uuid}/home.html`,
|
||||
"The homepage is set"
|
||||
);
|
||||
Assert.equal(
|
||||
win.gURLBar.value,
|
||||
`moz-extension://${extension.uuid}/home.html`,
|
||||
"extension is control in window"
|
||||
);
|
||||
const { selectedBrowser } = win.gBrowser;
|
||||
const result = await SpecialPowers.spawn(
|
||||
selectedBrowser,
|
||||
[],
|
||||
async () => {
|
||||
const { document } = this.content;
|
||||
if (document.readyState !== "complete") {
|
||||
await new Promise(resolve => (document.onload = resolve));
|
||||
}
|
||||
return document.querySelector("#result").textContent;
|
||||
}
|
||||
);
|
||||
Assert.equal(
|
||||
result,
|
||||
"homepage loaded",
|
||||
"Overridden homepage loaded successfully"
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
// Extension used to open the homepage in a new window.
|
||||
let opener = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
permissions: ["tabs"],
|
||||
},
|
||||
async background() {
|
||||
let win;
|
||||
browser.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => {
|
||||
if (tab.windowId !== win.id || tab.status !== "complete") {
|
||||
return;
|
||||
}
|
||||
browser.test.sendMessage("created", tab.url);
|
||||
});
|
||||
browser.test.onMessage.addListener(async msg => {
|
||||
if (msg == "create") {
|
||||
win = await browser.windows.create({});
|
||||
browser.test.assertTrue(
|
||||
win.id !== browser.windows.WINDOW_ID_NONE,
|
||||
"New window was created."
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
function listener(msg) {
|
||||
Assert.ok(!/may not load or link to moz-extension/.test(msg.message));
|
||||
}
|
||||
Services.console.registerListener(listener);
|
||||
registerCleanupFunction(() => {
|
||||
Services.console.unregisterListener(listener);
|
||||
});
|
||||
|
||||
await opener.startup();
|
||||
const promiseNewWindow = BrowserTestUtils.waitForNewWindow();
|
||||
await opener.sendMessage("create");
|
||||
let homepageUrl = await opener.awaitMessage("created");
|
||||
|
||||
Assert.equal(
|
||||
homepageUrl,
|
||||
`moz-extension://${extension.uuid}/home.html`,
|
||||
"The homepage is set"
|
||||
);
|
||||
|
||||
const newWin = await promiseNewWindow;
|
||||
Assert.equal(
|
||||
await SpecialPowers.spawn(newWin.gBrowser.selectedBrowser, [], async () => {
|
||||
const { document } = this.content;
|
||||
if (document.readyState !== "complete") {
|
||||
await new Promise(resolve => (document.onload = resolve));
|
||||
}
|
||||
return document.querySelector("#result").textContent;
|
||||
}),
|
||||
"homepage loaded",
|
||||
"Overridden homepage loaded as expected"
|
||||
);
|
||||
|
||||
await BrowserTestUtils.closeWindow(newWin);
|
||||
await opener.unload();
|
||||
await extension.unload();
|
||||
}
|
||||
|
||||
add_task(async function test_overriding_home_page_open_mv2() {
|
||||
await _test_overriding_home_page_open(2);
|
||||
});
|
||||
|
||||
add_task(async function test_overriding_home_page_open_mv3() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["extensions.manifestV3.enabled", true]],
|
||||
});
|
||||
await _test_overriding_home_page_open(3);
|
||||
});
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
requestLongerTimeout(4);
|
||||
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"ExtensionSettingsStore",
|
||||
|
@ -715,72 +713,3 @@ add_task(async function testNewTabPrefsReset() {
|
|||
"privateAllowed pref is not set"
|
||||
);
|
||||
});
|
||||
|
||||
// This test ensures that an extension provided newtab
|
||||
// can be opened by another extension (e.g. tab manager)
|
||||
// regardless of whether the newtab url is made available
|
||||
// in web_accessible_resources.
|
||||
add_task(async function test_newtab_from_extension() {
|
||||
let panel = getNewTabDoorhanger().closest("panel");
|
||||
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
applications: {
|
||||
gecko: {
|
||||
id: "newtaburl@mochi.test",
|
||||
},
|
||||
},
|
||||
chrome_url_overrides: {
|
||||
newtab: "newtab.html",
|
||||
},
|
||||
},
|
||||
files: {
|
||||
"newtab.html": `<h1>New tab!</h1><script src="newtab.js"></script>`,
|
||||
"newtab.js": () => {
|
||||
browser.test.sendMessage("newtab-loaded");
|
||||
},
|
||||
},
|
||||
useAddonManager: "temporary",
|
||||
});
|
||||
|
||||
await extension.startup();
|
||||
let extensionNewTabUrl = `moz-extension://${extension.uuid}/newtab.html`;
|
||||
|
||||
let popupShown = promisePopupShown(panel);
|
||||
let tab = await promiseNewTab(extensionNewTabUrl);
|
||||
await popupShown;
|
||||
|
||||
// This will show a confirmation doorhanger, make sure we don't leave it open.
|
||||
let popupHidden = promisePopupHidden(panel);
|
||||
panel.hidePopup();
|
||||
await popupHidden;
|
||||
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
|
||||
// extension to open the newtab
|
||||
let opener = ExtensionTestUtils.loadExtension({
|
||||
async background() {
|
||||
let newtab = await browser.tabs.create({});
|
||||
browser.test.assertTrue(
|
||||
newtab.id !== browser.tabs.TAB_ID_NONE,
|
||||
"New tab was created."
|
||||
);
|
||||
await browser.tabs.remove(newtab.id);
|
||||
browser.test.sendMessage("complete");
|
||||
},
|
||||
});
|
||||
|
||||
function listener(msg) {
|
||||
Assert.ok(!/may not load or link to moz-extension/.test(msg.message));
|
||||
}
|
||||
Services.console.registerListener(listener);
|
||||
registerCleanupFunction(() => {
|
||||
Services.console.unregisterListener(listener);
|
||||
});
|
||||
|
||||
await opener.startup();
|
||||
await opener.awaitMessage("complete");
|
||||
await extension.awaitMessage("newtab-loaded");
|
||||
await opener.unload();
|
||||
await extension.unload();
|
||||
});
|
||||
|
|
|
@ -3,171 +3,112 @@
|
|||
"use strict";
|
||||
|
||||
add_task(async function testWindowCreate() {
|
||||
let pageExt = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
applications: { gecko: { id: "page@mochitest" } },
|
||||
protocol_handlers: [
|
||||
{
|
||||
protocol: "ext+foo",
|
||||
name: "a foo protocol handler",
|
||||
uriTemplate: "page.html?val=%s",
|
||||
},
|
||||
],
|
||||
},
|
||||
files: {
|
||||
"page.html": `<html><head>
|
||||
<meta charset="utf-8">
|
||||
</head></html>`,
|
||||
},
|
||||
});
|
||||
await pageExt.startup();
|
||||
|
||||
async function background(OTHER_PAGE) {
|
||||
browser.test.log(`== using ${OTHER_PAGE}`);
|
||||
const EXTENSION_URL = browser.runtime.getURL("test.html");
|
||||
const EXT_PROTO = "ext+bar:foo";
|
||||
const OTHER_PROTO = "ext+foo:bar";
|
||||
|
||||
let windows = new (class extends Map {
|
||||
// eslint-disable-line new-parens
|
||||
get(id) {
|
||||
if (!this.has(id)) {
|
||||
let window = {
|
||||
tabs: new Map(),
|
||||
};
|
||||
window.promise = new Promise(resolve => {
|
||||
window.resolvePromise = resolve;
|
||||
});
|
||||
|
||||
this.set(id, window);
|
||||
}
|
||||
|
||||
return super.get(id);
|
||||
}
|
||||
})();
|
||||
|
||||
browser.tabs.onUpdated.addListener((tabId, changed, tab) => {
|
||||
if (changed.status == "complete" && tab.url !== "about:blank") {
|
||||
let window = windows.get(tab.windowId);
|
||||
window.tabs.set(tab.index, tab);
|
||||
|
||||
if (window.tabs.size === window.expectedTabs) {
|
||||
browser.test.log("resolving a window load");
|
||||
window.resolvePromise(window);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
async function create(options) {
|
||||
browser.test.log(`creating window for ${options.url}`);
|
||||
let window = await browser.windows.create(options);
|
||||
let win = windows.get(window.id);
|
||||
win.id = window.id;
|
||||
|
||||
win.expectedTabs = Array.isArray(options.url) ? options.url.length : 1;
|
||||
|
||||
return win.promise;
|
||||
}
|
||||
|
||||
function createFail(options) {
|
||||
return browser.windows
|
||||
.create(options)
|
||||
.then(() => {
|
||||
browser.test.fail(`window opened with ${options.url}`);
|
||||
})
|
||||
.catch(() => {
|
||||
browser.test.succeed(`window could not open with ${options.url}`);
|
||||
});
|
||||
}
|
||||
|
||||
let TEST_SETS = [
|
||||
{
|
||||
name: "Single protocol URL in this extension",
|
||||
url: EXT_PROTO,
|
||||
expect: [`${EXTENSION_URL}?val=ext%2Bbar%3Afoo`],
|
||||
},
|
||||
{
|
||||
name: "Single, relative URL",
|
||||
url: "test.html",
|
||||
expect: [EXTENSION_URL],
|
||||
},
|
||||
{
|
||||
name: "Single, absolute, extension URL",
|
||||
url: EXTENSION_URL,
|
||||
expect: [EXTENSION_URL],
|
||||
},
|
||||
{
|
||||
name: "Single, absolute, other extension URL",
|
||||
url: OTHER_PAGE,
|
||||
expect: [OTHER_PAGE],
|
||||
},
|
||||
{
|
||||
name: "Single protocol URL in other extension",
|
||||
url: OTHER_PROTO,
|
||||
expect: [`${OTHER_PAGE}?val=ext%2Bfoo%3Abar`],
|
||||
},
|
||||
{
|
||||
name: "multiple urls",
|
||||
url: [EXT_PROTO, "test.html", EXTENSION_URL, OTHER_PROTO],
|
||||
expect: [
|
||||
`${EXTENSION_URL}?val=ext%2Bbar%3Afoo`,
|
||||
EXTENSION_URL,
|
||||
EXTENSION_URL,
|
||||
`${OTHER_PAGE}?val=ext%2Bfoo%3Abar`,
|
||||
],
|
||||
},
|
||||
];
|
||||
try {
|
||||
let windows = await Promise.all(
|
||||
TEST_SETS.map(t => create({ url: t.url }))
|
||||
);
|
||||
|
||||
TEST_SETS.forEach((test, i) => {
|
||||
test.expect.forEach((expectUrl, x) => {
|
||||
browser.test.assertEq(
|
||||
expectUrl,
|
||||
windows[i].tabs.get(x)?.url,
|
||||
TEST_SETS[i].name
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Promise.all(windows.map(({ id }) => browser.windows.remove(id))).then(
|
||||
() => {
|
||||
browser.test.notifyPass("window-create-url");
|
||||
}
|
||||
);
|
||||
|
||||
// Expecting to fail when opening windows with multiple urls that includes
|
||||
// other extension urls.
|
||||
await Promise.all([createFail({ url: [EXTENSION_URL, OTHER_PAGE] })]);
|
||||
} catch (e) {
|
||||
browser.test.fail(`${e} :: ${e.stack}`);
|
||||
browser.test.notifyFail("window-create-url");
|
||||
}
|
||||
}
|
||||
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
permissions: ["tabs"],
|
||||
protocol_handlers: [
|
||||
{
|
||||
protocol: "ext+bar",
|
||||
name: "a bar protocol handler",
|
||||
uriTemplate: "test.html?val=%s",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
background: `(${background})("moz-extension://${pageExt.uuid}/page.html")`,
|
||||
background: async function() {
|
||||
const EXTENSION_URL = browser.runtime.getURL("test.html");
|
||||
const REMOTE_URL = browser.runtime.getURL("test.html");
|
||||
|
||||
let windows = new (class extends Map {
|
||||
// eslint-disable-line new-parens
|
||||
get(id) {
|
||||
if (!this.has(id)) {
|
||||
let window = {
|
||||
tabs: new Map(),
|
||||
};
|
||||
window.promise = new Promise(resolve => {
|
||||
window.resolvePromise = resolve;
|
||||
});
|
||||
|
||||
this.set(id, window);
|
||||
}
|
||||
|
||||
return super.get(id);
|
||||
}
|
||||
})();
|
||||
|
||||
browser.tabs.onUpdated.addListener((tabId, changed, tab) => {
|
||||
if (changed.status == "complete" && tab.url !== "about:blank") {
|
||||
let window = windows.get(tab.windowId);
|
||||
window.tabs.set(tab.index, tab);
|
||||
|
||||
if (window.tabs.size === window.expectedTabs) {
|
||||
window.resolvePromise(window);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
async function create(options) {
|
||||
let window = await browser.windows.create(options);
|
||||
let win = windows.get(window.id);
|
||||
win.id = window.id;
|
||||
|
||||
win.expectedTabs = Array.isArray(options.url) ? options.url.length : 1;
|
||||
|
||||
return win.promise;
|
||||
}
|
||||
|
||||
try {
|
||||
let windows = await Promise.all([
|
||||
create({ url: REMOTE_URL }),
|
||||
create({ url: "test.html" }),
|
||||
create({ url: EXTENSION_URL }),
|
||||
create({ url: [REMOTE_URL, "test.html", EXTENSION_URL] }),
|
||||
]);
|
||||
browser.test.assertEq(
|
||||
REMOTE_URL,
|
||||
windows[0].tabs.get(0).url,
|
||||
"Single, absolute, remote URL"
|
||||
);
|
||||
|
||||
browser.test.assertEq(
|
||||
REMOTE_URL,
|
||||
windows[1].tabs.get(0).url,
|
||||
"Single, relative URL"
|
||||
);
|
||||
|
||||
browser.test.assertEq(
|
||||
REMOTE_URL,
|
||||
windows[2].tabs.get(0).url,
|
||||
"Single, absolute, extension URL"
|
||||
);
|
||||
|
||||
browser.test.assertEq(
|
||||
REMOTE_URL,
|
||||
windows[3].tabs.get(0).url,
|
||||
"url[0]: Absolute, remote URL"
|
||||
);
|
||||
browser.test.assertEq(
|
||||
EXTENSION_URL,
|
||||
windows[3].tabs.get(1).url,
|
||||
"url[1]: Relative URL"
|
||||
);
|
||||
browser.test.assertEq(
|
||||
EXTENSION_URL,
|
||||
windows[3].tabs.get(2).url,
|
||||
"url[2]: Absolute, extension URL"
|
||||
);
|
||||
|
||||
Promise.all(windows.map(({ id }) => browser.windows.remove(id))).then(
|
||||
() => {
|
||||
browser.test.notifyPass("window-create-url");
|
||||
}
|
||||
);
|
||||
} catch (e) {
|
||||
browser.test.fail(`${e} :: ${e.stack}`);
|
||||
browser.test.notifyFail("window-create-url");
|
||||
}
|
||||
},
|
||||
|
||||
files: {
|
||||
"test.html": `<!DOCTYPE html><html><head><meta charset="utf-8"></head><body></body></html>`,
|
||||
"test.html": `<DOCTYPE html><html><head><meta charset="utf-8"></head></html>`,
|
||||
},
|
||||
});
|
||||
|
||||
await extension.startup();
|
||||
await extension.awaitFinish("window-create-url");
|
||||
await extension.unload();
|
||||
await pageExt.unload();
|
||||
});
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
#include "nsAboutProtocolUtils.h"
|
||||
#include "ThirdPartyUtil.h"
|
||||
#include "mozilla/ContentPrincipal.h"
|
||||
#include "mozilla/ExtensionPolicyService.h"
|
||||
#include "mozilla/NullPrincipal.h"
|
||||
#include "mozilla/dom/BlobURLProtocolHandler.h"
|
||||
#include "mozilla/dom/ChromeUtils.h"
|
||||
|
@ -597,8 +596,6 @@ nsresult BasePrincipal::CheckMayLoadHelper(nsIURI* aURI,
|
|||
}
|
||||
}
|
||||
|
||||
// Web Accessible Resources in MV2 Extensions are marked with
|
||||
// URI_FETCHABLE_BY_ANYONE
|
||||
bool fetchableByAnyone;
|
||||
rv = NS_URIChainHasFlags(aURI, nsIProtocolHandler::URI_FETCHABLE_BY_ANYONE,
|
||||
&fetchableByAnyone);
|
||||
|
@ -606,32 +603,14 @@ nsresult BasePrincipal::CheckMayLoadHelper(nsIURI* aURI,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
// Get the principal uri for the last flag check or error.
|
||||
nsCOMPtr<nsIURI> prinURI;
|
||||
rv = GetURI(getter_AddRefs(prinURI));
|
||||
if (!(NS_SUCCEEDED(rv) && prinURI)) {
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
|
||||
// If MV3 Extension uris are web accessible by this principal it is allowed to
|
||||
// load.
|
||||
bool maybeWebAccessible = false;
|
||||
NS_URIChainHasFlags(aURI, nsIProtocolHandler::WEBEXT_URI_WEB_ACCESSIBLE,
|
||||
&maybeWebAccessible);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (maybeWebAccessible) {
|
||||
bool isWebAccessible = false;
|
||||
rv = ExtensionPolicyService::GetSingleton().SourceMayLoadExtensionURI(
|
||||
prinURI, aURI, &isWebAccessible);
|
||||
if (NS_SUCCEEDED(rv) && isWebAccessible) {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (aReport) {
|
||||
nsScriptSecurityManager::ReportError(
|
||||
"CheckSameOriginError", prinURI, aURI,
|
||||
mOriginAttributes.mPrivateBrowsingId > 0, aInnerWindowID);
|
||||
nsCOMPtr<nsIURI> prinURI;
|
||||
rv = GetURI(getter_AddRefs(prinURI));
|
||||
if (NS_SUCCEEDED(rv) && prinURI) {
|
||||
nsScriptSecurityManager::ReportError(
|
||||
"CheckSameOriginError", prinURI, aURI,
|
||||
mOriginAttributes.mPrivateBrowsingId > 0, aInnerWindowID);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
|
|
|
@ -741,6 +741,21 @@ nsScriptSecurityManager::CheckLoadURIWithPrincipal(nsIPrincipal* aPrincipal,
|
|||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
|
||||
// Extensions may allow access to a web accessible resource.
|
||||
bool maybeWebAccessible = false;
|
||||
NS_URIChainHasFlags(targetBaseURI,
|
||||
nsIProtocolHandler::WEBEXT_URI_WEB_ACCESSIBLE,
|
||||
&maybeWebAccessible);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (maybeWebAccessible) {
|
||||
bool isWebAccessible = false;
|
||||
rv = ExtensionPolicyService::GetSingleton().SourceMayLoadExtensionURI(
|
||||
sourceURI, targetBaseURI, &isWebAccessible);
|
||||
if (!(NS_SUCCEEDED(rv) && isWebAccessible)) {
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for uris that are only loadable by principals that subsume them
|
||||
bool targetURIIsLoadableBySubsumers = false;
|
||||
rv = NS_URIChainHasFlags(targetBaseURI,
|
||||
|
@ -814,7 +829,6 @@ nsScriptSecurityManager::CheckLoadURIWithPrincipal(nsIPrincipal* aPrincipal,
|
|||
bool schemesMatch =
|
||||
scheme.Equals(otherScheme, nsCaseInsensitiveCStringComparator);
|
||||
bool isSamePage = false;
|
||||
bool isExtensionMismatch = false;
|
||||
// about: URIs are special snowflakes.
|
||||
if (scheme.EqualsLiteral("about") && schemesMatch) {
|
||||
nsAutoCString moduleName, otherModuleName;
|
||||
|
@ -862,13 +876,6 @@ nsScriptSecurityManager::CheckLoadURIWithPrincipal(nsIPrincipal* aPrincipal,
|
|||
}
|
||||
}
|
||||
}
|
||||
} else if (schemesMatch && scheme.EqualsLiteral("moz-extension")) {
|
||||
// If it is not the same exension, we want to ensure we end up
|
||||
// calling CheckLoadURIFlags
|
||||
nsAutoCString host, otherHost;
|
||||
currentURI->GetHost(host);
|
||||
currentOtherURI->GetHost(otherHost);
|
||||
isExtensionMismatch = !host.Equals(otherHost);
|
||||
} else {
|
||||
bool equalExceptRef = false;
|
||||
rv = currentURI->EqualsExceptRef(currentOtherURI, &equalExceptRef);
|
||||
|
@ -877,12 +884,10 @@ nsScriptSecurityManager::CheckLoadURIWithPrincipal(nsIPrincipal* aPrincipal,
|
|||
|
||||
// If schemes are not equal, or they're equal but the target URI
|
||||
// is different from the source URI and doesn't always allow linking
|
||||
// from the same scheme, or this is two different extensions, check
|
||||
// if the URI flags of the current target URI allow the current
|
||||
// source URI to link to it.
|
||||
// from the same scheme, check if the URI flags of the current target
|
||||
// URI allow the current source URI to link to it.
|
||||
// The policy is specified by the protocol flags on both URIs.
|
||||
if (!schemesMatch || (denySameSchemeLinks && !isSamePage) ||
|
||||
isExtensionMismatch) {
|
||||
if (!schemesMatch || (denySameSchemeLinks && !isSamePage)) {
|
||||
return CheckLoadURIFlags(
|
||||
currentURI, currentOtherURI, sourceBaseURI, targetBaseURI, aFlags,
|
||||
aPrincipal->OriginAttributesRef().mPrivateBrowsingId > 0,
|
||||
|
@ -931,8 +936,7 @@ nsresult nsScriptSecurityManager::CheckLoadURIFlags(
|
|||
nsresult rv = aTargetBaseURI->GetScheme(targetScheme);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
// Check for system target URI. Regular (non web accessible) extension
|
||||
// URIs will also have URI_DANGEROUS_TO_LOAD.
|
||||
// Check for system target URI
|
||||
rv = DenyAccessIfURIHasFlags(aTargetURI,
|
||||
nsIProtocolHandler::URI_DANGEROUS_TO_LOAD);
|
||||
if (NS_FAILED(rv)) {
|
||||
|
@ -958,26 +962,6 @@ nsresult nsScriptSecurityManager::CheckLoadURIFlags(
|
|||
}
|
||||
}
|
||||
|
||||
// If MV3 Extension uris are web accessible they have
|
||||
// WEBEXT_URI_WEB_ACCESSIBLE.
|
||||
bool maybeWebAccessible = false;
|
||||
NS_URIChainHasFlags(aTargetURI, nsIProtocolHandler::WEBEXT_URI_WEB_ACCESSIBLE,
|
||||
&maybeWebAccessible);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (maybeWebAccessible) {
|
||||
bool isWebAccessible = false;
|
||||
rv = ExtensionPolicyService::GetSingleton().SourceMayLoadExtensionURI(
|
||||
aSourceURI, aTargetURI, &isWebAccessible);
|
||||
if (NS_SUCCEEDED(rv) && isWebAccessible) {
|
||||
return NS_OK;
|
||||
}
|
||||
if (reportErrors) {
|
||||
ReportError(errorTag, aSourceURI, aTargetURI, aFromPrivateWindow,
|
||||
aInnerWindowID);
|
||||
}
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
|
||||
// Check for chrome target URI
|
||||
bool targetURIIsUIResource = false;
|
||||
rv = NS_URIChainHasFlags(aTargetURI, nsIProtocolHandler::URI_IS_UI_RESOURCE,
|
||||
|
|
|
@ -274,8 +274,7 @@ interface WebExtensionPolicy {
|
|||
|
||||
dictionary WebAccessibleResourceInit {
|
||||
required sequence<MatchGlobOrString> resources;
|
||||
MatchPatternSetOrStringSequence? matches = null;
|
||||
sequence<DOMString>? extension_ids = null;
|
||||
MatchPatternSetOrStringSequence matches;
|
||||
};
|
||||
|
||||
dictionary WebExtensionInit {
|
||||
|
|
|
@ -1248,7 +1248,7 @@ nsresult nsContentSecurityManager::CheckAllowLoadInPrivilegedAboutContext(
|
|||
}
|
||||
|
||||
/*
|
||||
* Every protocol handler must set one of the six security flags
|
||||
* Every protocol handler must set one of the five security flags
|
||||
* defined in nsIProtocolHandler - if not - deny the load.
|
||||
*/
|
||||
nsresult nsContentSecurityManager::CheckChannelHasProtocolSecurityFlag(
|
||||
|
@ -1273,9 +1273,6 @@ nsresult nsContentSecurityManager::CheckChannelHasProtocolSecurityFlag(
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
uint32_t securityFlagsSet = 0;
|
||||
if (flags & nsIProtocolHandler::WEBEXT_URI_WEB_ACCESSIBLE) {
|
||||
securityFlagsSet += 1;
|
||||
}
|
||||
if (flags & nsIProtocolHandler::URI_LOADABLE_BY_ANYONE) {
|
||||
securityFlagsSet += 1;
|
||||
}
|
||||
|
|
|
@ -275,8 +275,7 @@ interface nsIProtocolHandler : nsISupports
|
|||
/**
|
||||
* This URI may be fetched and the contents are visible to anyone. This is
|
||||
* semantically equivalent to the resource being served with all-access CORS
|
||||
* headers. This is only used in MV2 Extensions and should not otherwise
|
||||
* be used.
|
||||
* headers.
|
||||
*/
|
||||
const unsigned long URI_FETCHABLE_BY_ANYONE = (1 << 18);
|
||||
|
||||
|
@ -313,7 +312,7 @@ interface nsIProtocolHandler : nsISupports
|
|||
|
||||
/**
|
||||
* This is an extension web accessible uri that is loadable if checked
|
||||
* against an allowlist using ExtensionPolicyService::SourceMayLoadExtensionURI.
|
||||
* against an allow whitelist.
|
||||
*/
|
||||
const unsigned long WEBEXT_URI_WEB_ACCESSIBLE = (1 << 24);
|
||||
};
|
||||
|
|
|
@ -416,16 +416,15 @@ nsresult ExtensionProtocolHandler::GetFlagsForURI(nsIURI* aURI,
|
|||
|
||||
URLInfo url(aURI);
|
||||
if (auto* policy = EPS().GetByURL(url)) {
|
||||
// In general a moz-extension URI is only loadable by chrome, but an
|
||||
// allowlist subset are web-accessible (and cross-origin fetchable).
|
||||
// The allowlist is checked using EPS.SourceMayLoadExtensionURI in
|
||||
// BasePrincipal and nsScriptSecurityManager.
|
||||
// In general a moz-extension URI is only loadable by chrome, but a
|
||||
// whitelisted subset are web-accessible (and cross-origin fetchable). Check
|
||||
// that whitelist. For Manifest V3 extensions, an additional whitelist
|
||||
// for the source loading the url must be checked so we add the flag
|
||||
// WEBEXT_URI_WEB_ACCESSIBLE, which is then checked in
|
||||
// nsScriptSecurityManager.
|
||||
if (policy->IsWebAccessiblePath(url.FilePath())) {
|
||||
if (policy->ManifestVersion() < 3) {
|
||||
flags |= URI_LOADABLE_BY_ANYONE | URI_FETCHABLE_BY_ANYONE;
|
||||
} else {
|
||||
flags |= WEBEXT_URI_WEB_ACCESSIBLE;
|
||||
}
|
||||
flags |= URI_LOADABLE_BY_ANYONE | URI_FETCHABLE_BY_ANYONE |
|
||||
WEBEXT_URI_WEB_ACCESSIBLE;
|
||||
} else {
|
||||
flags |= URI_DANGEROUS_TO_LOAD;
|
||||
}
|
||||
|
|
|
@ -304,18 +304,6 @@ const POSTPROCESSORS = {
|
|||
context.logError(context.makeError(msg));
|
||||
throw new Error(msg);
|
||||
},
|
||||
|
||||
webAccessibleMatching(value, context) {
|
||||
// Ensure each object has at least one of matches or extension_ids array.
|
||||
for (let obj of value) {
|
||||
if (!obj.matches && !obj.extension_ids) {
|
||||
const msg = `web_accessible_resources requires one of "matches" or "extension_ids"`;
|
||||
context.logError(context.makeError(msg));
|
||||
throw new Error(msg);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
},
|
||||
};
|
||||
|
||||
// Parses a regular expression, with support for the Python extended
|
||||
|
|
|
@ -143,27 +143,12 @@ WebAccessibleResource::WebAccessibleResource(
|
|||
return;
|
||||
}
|
||||
|
||||
if (!aInit.mMatches.IsNull()) {
|
||||
if (aInit.mMatches.WasPassed()) {
|
||||
MatchPatternOptions options;
|
||||
options.mRestrictSchemes = true;
|
||||
mMatches = ParseMatches(aGlobal, aInit.mMatches.Value(), options,
|
||||
ErrorBehavior::CreateEmptyPattern, aRv);
|
||||
}
|
||||
|
||||
if (!aInit.mExtension_ids.IsNull()) {
|
||||
mExtensionIDs = new AtomSet(aInit.mExtension_ids.Value());
|
||||
}
|
||||
}
|
||||
|
||||
bool WebAccessibleResource::IsExtensionMatch(const URLInfo& aURI) {
|
||||
if (!mExtensionIDs) {
|
||||
return false;
|
||||
}
|
||||
if (mExtensionIDs->Contains(nsGkAtoms::_asterisk)) {
|
||||
return true;
|
||||
}
|
||||
WebExtensionPolicy* policy = EPS().GetByHost(aURI.Host());
|
||||
return policy && mExtensionIDs->Contains(policy->Id());
|
||||
}
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebAccessibleResource)
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include "mozilla/WeakPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsGkAtoms.h"
|
||||
#include "nsISupports.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
|
@ -51,23 +50,16 @@ class WebAccessibleResource final : public nsISupports {
|
|||
}
|
||||
|
||||
bool SourceMayAccessPath(const URLInfo& aURI, const nsAString& aPath) {
|
||||
return mWebAccessiblePaths.Matches(aPath) &&
|
||||
(IsHostMatch(aURI) || IsExtensionMatch(aURI));
|
||||
return mWebAccessiblePaths.Matches(aPath) && mMatches &&
|
||||
mMatches->Matches(aURI);
|
||||
}
|
||||
|
||||
bool IsHostMatch(const URLInfo& aURI) {
|
||||
return mMatches && mMatches->Matches(aURI);
|
||||
}
|
||||
|
||||
bool IsExtensionMatch(const URLInfo& aURI);
|
||||
|
||||
protected:
|
||||
virtual ~WebAccessibleResource() = default;
|
||||
|
||||
private:
|
||||
MatchGlobSet mWebAccessiblePaths;
|
||||
RefPtr<MatchPatternSet> mMatches;
|
||||
RefPtr<AtomSet> mExtensionIDs;
|
||||
};
|
||||
|
||||
class WebExtensionPolicy final : public nsISupports,
|
||||
|
@ -125,11 +117,6 @@ class WebExtensionPolicy final : public nsISupports,
|
|||
}
|
||||
|
||||
bool SourceMayAccessPath(const URLInfo& aURI, const nsAString& aPath) const {
|
||||
if (aURI.Scheme() == nsGkAtoms::moz_extension &&
|
||||
mHostname.Equals(aURI.Host())) {
|
||||
// An extension can always access it's own paths.
|
||||
return true;
|
||||
}
|
||||
if (mManifestVersion < 3) {
|
||||
return IsWebAccessiblePath(aPath);
|
||||
}
|
||||
|
|
|
@ -288,32 +288,16 @@
|
|||
{
|
||||
"min_manifest_version": 3,
|
||||
"type": "array",
|
||||
"postprocess": "webAccessibleMatching",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"resources": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"matches": {
|
||||
"optional": true,
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": { "$ref": "MatchPattern" }
|
||||
},
|
||||
"extension_ids": {
|
||||
"optional": true,
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"choices": [
|
||||
{"$ref": "ExtensionID" },
|
||||
{ "type": "string", "enum": ["*"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,8 +25,6 @@ let image = atob(
|
|||
const IMAGE_ARRAYBUFFER = Uint8Array.from(image, byte => byte.charCodeAt(0))
|
||||
.buffer;
|
||||
|
||||
const ANDROID = navigator.userAgent.includes("Android");
|
||||
|
||||
async function testImageLoading(src, expectedAction) {
|
||||
let imageLoadingPromise = new Promise((resolve, reject) => {
|
||||
let cleanupListeners;
|
||||
|
@ -375,100 +373,6 @@ add_task(async function test_web_accessible_resources_mixed_content() {
|
|||
|
||||
await extension.unload();
|
||||
});
|
||||
|
||||
// test that MV2 extensions continue to open other MV2 extension pages
|
||||
// when they are not listed in web_accessible_resources.
|
||||
add_task(async function test_web_accessible_resources_extensions_MV2() {
|
||||
function background() {
|
||||
let newtab;
|
||||
let win;
|
||||
let expectUrl;
|
||||
browser.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => {
|
||||
if (tab.url != expectUrl || tab.status !== "complete") {
|
||||
return;
|
||||
}
|
||||
browser.test.sendMessage("onUpdated", tab.url);
|
||||
});
|
||||
browser.test.onMessage.addListener(async (msg, url) => {
|
||||
expectUrl = url;
|
||||
if (msg == "create") {
|
||||
newtab = await browser.tabs.create({ url });
|
||||
browser.test.assertTrue(
|
||||
newtab.id !== browser.tabs.TAB_ID_NONE,
|
||||
"New tab was created."
|
||||
);
|
||||
} else if (msg == "update") {
|
||||
await browser.tabs.update(newtab.id, { url });
|
||||
} else if (msg == "remove") {
|
||||
await browser.tabs.remove(newtab.id);
|
||||
newtab = null;
|
||||
browser.test.sendMessage("completed");
|
||||
} else if (msg == "open-window") {
|
||||
win = await browser.windows.create({ url });
|
||||
} else if (msg == "close-window") {
|
||||
await browser.windows.remove(win.id);
|
||||
browser.test.sendMessage("completed");
|
||||
win = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
applications: { gecko: { id: "this-mv2@mochitest" } },
|
||||
},
|
||||
background,
|
||||
files: {
|
||||
"page.html": `<html><head>
|
||||
<meta charset="utf-8">
|
||||
</head></html>`,
|
||||
},
|
||||
});
|
||||
|
||||
async function testTabsAction(ext, action, url) {
|
||||
ext.sendMessage(action, url);
|
||||
let loaded = await ext.awaitMessage("onUpdated");
|
||||
is(loaded, url, "extension url was loaded");
|
||||
}
|
||||
|
||||
await extension.startup();
|
||||
let extensionUrl = `moz-extension://${extension.uuid}/page.html`;
|
||||
|
||||
// Test opening its own pages
|
||||
await testTabsAction(extension, "create", extensionUrl);
|
||||
await testTabsAction(extension, "update", extensionUrl);
|
||||
extension.sendMessage("remove");
|
||||
await extension.awaitMessage("completed");
|
||||
if (!ANDROID) {
|
||||
await testTabsAction(extension, "open-window", extensionUrl);
|
||||
extension.sendMessage("close-window");
|
||||
await extension.awaitMessage("completed");
|
||||
}
|
||||
|
||||
// Extension used to open the homepage in a new window.
|
||||
let other = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
permissions: ["tabs", "<all_urls>"],
|
||||
},
|
||||
background,
|
||||
});
|
||||
await other.startup();
|
||||
|
||||
// Test opening another extensions pages
|
||||
await testTabsAction(other, "create", extensionUrl);
|
||||
await testTabsAction(other, "update", extensionUrl);
|
||||
other.sendMessage("remove");
|
||||
await other.awaitMessage("completed");
|
||||
if (!ANDROID) {
|
||||
await testTabsAction(other, "open-window", extensionUrl);
|
||||
other.sendMessage("close-window");
|
||||
await other.awaitMessage("completed");
|
||||
}
|
||||
|
||||
await extension.unload();
|
||||
await other.unload();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
|
|
@ -132,11 +132,6 @@ add_task(async function test_WebExtensionPolicy() {
|
|||
"Non-web-accessible path should not be web-accessible"
|
||||
);
|
||||
|
||||
ok(
|
||||
policy.sourceMayAccessPath(mozExtURI, "/bar.baz"),
|
||||
"Web-accessible path should be web-accessible to self"
|
||||
);
|
||||
|
||||
// Localization
|
||||
|
||||
equal(
|
||||
|
@ -257,114 +252,6 @@ add_task(async function test_WebExtensionPolicy() {
|
|||
}
|
||||
});
|
||||
|
||||
add_task(async function test_WebExtensionPolicy_V3() {
|
||||
const id = "foo@bar.baz";
|
||||
const uuid = "ca9d3f23-125c-4b24-abfc-1ca2692b0610";
|
||||
const id2 = "foo-2@bar.baz";
|
||||
const uuid2 = "89383c45-7db4-4999-83f7-f4cc246372cd";
|
||||
const id3 = "foo-3@bar.baz";
|
||||
const uuid3 = "56652231-D7E2-45D1-BDBD-BD3BFF80927E";
|
||||
|
||||
const baseURL = "file:///foo/";
|
||||
const mozExtURL = `moz-extension://${uuid}/`;
|
||||
const mozExtURI = newURI(mozExtURL);
|
||||
|
||||
let policy = new WebExtensionPolicy({
|
||||
id,
|
||||
mozExtensionHostname: uuid,
|
||||
baseURL,
|
||||
manifestVersion: 3,
|
||||
|
||||
localizeCallback(str) {
|
||||
return `<${str}>`;
|
||||
},
|
||||
|
||||
allowedOrigins: new MatchPatternSet(["http://foo.bar/", "*://*.baz/"], {
|
||||
ignorePath: true,
|
||||
}),
|
||||
permissions: ["<all_urls>"],
|
||||
webAccessibleResources: [
|
||||
{
|
||||
resources: ["/foo/*", "/bar.baz"].map(glob => new MatchGlob(glob)),
|
||||
matches: ["http://foo.bar/"],
|
||||
extension_ids: [id3],
|
||||
},
|
||||
{
|
||||
resources: ["/foo.bar.baz"].map(glob => new MatchGlob(glob)),
|
||||
extension_ids: ["*"],
|
||||
},
|
||||
],
|
||||
});
|
||||
policy.active = true;
|
||||
|
||||
let policy2 = new WebExtensionPolicy({
|
||||
id: id2,
|
||||
mozExtensionHostname: uuid2,
|
||||
baseURL,
|
||||
localizeCallback() {},
|
||||
allowedOrigins: new MatchPatternSet([]),
|
||||
permissions: ["<all_urls>"],
|
||||
});
|
||||
policy2.active = true;
|
||||
|
||||
let policy3 = new WebExtensionPolicy({
|
||||
id: id3,
|
||||
mozExtensionHostname: uuid3,
|
||||
baseURL,
|
||||
localizeCallback() {},
|
||||
allowedOrigins: new MatchPatternSet([]),
|
||||
permissions: ["<all_urls>"],
|
||||
});
|
||||
policy3.active = true;
|
||||
|
||||
ok(
|
||||
policy.isWebAccessiblePath("/bar.baz"),
|
||||
"Web-accessible path should be web-accessible"
|
||||
);
|
||||
ok(
|
||||
!policy.isWebAccessiblePath("/bar.baz/quux"),
|
||||
"Non-web-accessible path should not be web-accessible"
|
||||
);
|
||||
// Extension can always access itself
|
||||
ok(
|
||||
policy.sourceMayAccessPath(mozExtURI, "/bar.baz"),
|
||||
"Web-accessible path should be accessible to self"
|
||||
);
|
||||
ok(
|
||||
policy.sourceMayAccessPath(mozExtURI, "/foo.bar.baz"),
|
||||
"Web-accessible path should be accessible to self"
|
||||
);
|
||||
|
||||
ok(
|
||||
!policy.sourceMayAccessPath(newURI(`https://${uuid}/`), "/bar.baz"),
|
||||
"Web-accessible path should not be accessible due to scheme mismatch"
|
||||
);
|
||||
|
||||
let extURI = newURI(policy2.getURL(""));
|
||||
ok(
|
||||
!policy.sourceMayAccessPath(extURI, "/bar.baz"),
|
||||
"Web-accessible path should not be accessible to other extension"
|
||||
);
|
||||
ok(
|
||||
policy.sourceMayAccessPath(extURI, "/foo.bar.baz"),
|
||||
"Web-accessible path should be accessible to other extension"
|
||||
);
|
||||
|
||||
extURI = newURI(policy3.getURL(""));
|
||||
ok(
|
||||
!policy.sourceMayAccessPath(extURI, "/bar.baz"),
|
||||
"Web-accessible path should be accessible to other extension"
|
||||
);
|
||||
ok(
|
||||
policy.sourceMayAccessPath(extURI, "/foo.bar.baz"),
|
||||
"Web-accessible path should be accessible to other extension"
|
||||
);
|
||||
|
||||
policy.active = false;
|
||||
policy2.active = false;
|
||||
policy3.active = false;
|
||||
});
|
||||
|
||||
add_task(async function test_WebExtensionPolicy_registerContentScripts() {
|
||||
const id = "foo@bar.baz";
|
||||
const uuid = "77a7b9d3-e73c-4cf3-97fb-1824868fe00f";
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
"use strict";
|
||||
|
||||
Services.prefs.setBoolPref("extensions.manifestV3.enabled", true);
|
||||
|
||||
const server = createHttpServer({ hosts: ["example.com", "example.org"] });
|
||||
|
@ -12,77 +11,10 @@ let image = atob(
|
|||
const IMAGE_ARRAYBUFFER = Uint8Array.from(image, byte => byte.charCodeAt(0))
|
||||
.buffer;
|
||||
|
||||
add_task(async function test_web_accessible_resources_matching() {
|
||||
let extension = await ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
manifest_version: 3,
|
||||
web_accessible_resources: [
|
||||
{
|
||||
resources: ["/accessible.html"],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
await Assert.rejects(
|
||||
extension.startup(),
|
||||
/web_accessible_resources requires one of "matches" or "extension_ids"/,
|
||||
"web_accessible_resources object format incorrect"
|
||||
);
|
||||
|
||||
extension = await ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
manifest_version: 3,
|
||||
web_accessible_resources: [
|
||||
{
|
||||
resources: ["/accessible.html"],
|
||||
matches: ["http://example.com/data/*"],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
await extension.startup();
|
||||
ok(true, "web_accessible_resources with matches loads");
|
||||
await extension.unload();
|
||||
|
||||
extension = await ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
manifest_version: 3,
|
||||
web_accessible_resources: [
|
||||
{
|
||||
resources: ["/accessible.html"],
|
||||
extension_ids: ["foo@mochitest"],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
await extension.startup();
|
||||
ok(true, "web_accessible_resources with extensions loads");
|
||||
await extension.unload();
|
||||
|
||||
extension = await ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
manifest_version: 3,
|
||||
web_accessible_resources: [
|
||||
{
|
||||
resources: ["/accessible.html"],
|
||||
matches: ["http://example.com/data/*"],
|
||||
extension_ids: ["foo@mochitest"],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
await extension.startup();
|
||||
ok(true, "web_accessible_resources with matches and extensions loads");
|
||||
await extension.unload();
|
||||
});
|
||||
|
||||
add_task(async function test_web_accessible_resources() {
|
||||
async function contentScript() {
|
||||
let canLoad = window.location.href.startsWith("http://example.com");
|
||||
|
||||
let urls = [
|
||||
{
|
||||
name: "iframe",
|
||||
|
@ -204,178 +136,3 @@ add_task(async function test_web_accessible_resources() {
|
|||
await page.close();
|
||||
await extension.unload();
|
||||
});
|
||||
|
||||
async function pageScript() {
|
||||
function test_element_src(data) {
|
||||
return new Promise(resolve => {
|
||||
let elem = document.createElement(data.elem);
|
||||
let elemContext =
|
||||
data.content_context && elem.wrappedJSObject
|
||||
? elem.wrappedJSObject
|
||||
: elem;
|
||||
elemContext.setAttribute("src", data.url);
|
||||
elem.addEventListener(
|
||||
"load",
|
||||
() => {
|
||||
browser.test.log(`got load event for ${data.url}`);
|
||||
resolve(true);
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
elem.addEventListener(
|
||||
"error",
|
||||
() => {
|
||||
browser.test.log(`got error event for ${data.url}`);
|
||||
resolve(false);
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
document.body.appendChild(elem);
|
||||
});
|
||||
}
|
||||
browser.test.onMessage.addListener(async msg => {
|
||||
browser.test.log(`testing ${JSON.stringify(msg)}`);
|
||||
let loaded = await test_element_src(msg);
|
||||
browser.test.assertEq(loaded, msg.shouldLoad, `${msg.name} loaded`);
|
||||
browser.test.sendMessage("web-accessible-resources");
|
||||
});
|
||||
browser.test.sendMessage("page-loaded");
|
||||
}
|
||||
|
||||
add_task(async function test_web_accessible_resources_extensions() {
|
||||
let other = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
applications: { gecko: { id: "other@mochitest" } },
|
||||
},
|
||||
files: {
|
||||
"page.js": pageScript,
|
||||
|
||||
"page.html": `<html><head>
|
||||
<meta charset="utf-8">
|
||||
<script src="page.js"></script>
|
||||
</head></html>`,
|
||||
},
|
||||
});
|
||||
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
manifest_version: 3,
|
||||
applications: { gecko: { id: "this@mochitest" } },
|
||||
web_accessible_resources: [
|
||||
{
|
||||
resources: ["/image.png"],
|
||||
extension_ids: ["other@mochitest"],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
files: {
|
||||
"image.png": IMAGE_ARRAYBUFFER,
|
||||
"inaccessible.png": IMAGE_ARRAYBUFFER,
|
||||
"page.js": pageScript,
|
||||
|
||||
"page.html": `<html><head>
|
||||
<meta charset="utf-8">
|
||||
<script src="page.js"></script>
|
||||
</head></html>`,
|
||||
},
|
||||
});
|
||||
|
||||
await extension.startup();
|
||||
let extensionUrl = `moz-extension://${extension.uuid}/`;
|
||||
|
||||
await other.startup();
|
||||
let pageUrl = `moz-extension://${other.uuid}/page.html`;
|
||||
|
||||
let page = await ExtensionTestUtils.loadContentPage(pageUrl);
|
||||
await other.awaitMessage("page-loaded");
|
||||
|
||||
other.sendMessage({
|
||||
name: "accessible resource",
|
||||
elem: "img",
|
||||
url: `${extensionUrl}image.png`,
|
||||
shouldLoad: true,
|
||||
});
|
||||
await other.awaitMessage("web-accessible-resources");
|
||||
|
||||
other.sendMessage({
|
||||
name: "inaccessible resource",
|
||||
elem: "img",
|
||||
url: `${extensionUrl}inaccessible.png`,
|
||||
shouldLoad: false,
|
||||
});
|
||||
await other.awaitMessage("web-accessible-resources");
|
||||
|
||||
await page.close();
|
||||
|
||||
// test that the extension may load it's own web accessible resource
|
||||
page = await ExtensionTestUtils.loadContentPage(`${extensionUrl}page.html`);
|
||||
await extension.awaitMessage("page-loaded");
|
||||
|
||||
extension.sendMessage({
|
||||
name: "accessible resource",
|
||||
elem: "img",
|
||||
url: `${extensionUrl}image.png`,
|
||||
shouldLoad: true,
|
||||
});
|
||||
await extension.awaitMessage("web-accessible-resources");
|
||||
|
||||
await page.close();
|
||||
await extension.unload();
|
||||
await other.unload();
|
||||
});
|
||||
|
||||
// test that a web page not in matches cannot load the resource
|
||||
add_task(async function test_web_accessible_resources_inaccessible() {
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
temporarilyInstalled: true,
|
||||
manifest: {
|
||||
manifest_version: 3,
|
||||
applications: { gecko: { id: "web@mochitest" } },
|
||||
content_scripts: [
|
||||
{
|
||||
matches: ["http://example.com/data/*"],
|
||||
js: ["page.js"],
|
||||
run_at: "document_idle",
|
||||
},
|
||||
],
|
||||
web_accessible_resources: [
|
||||
{
|
||||
resources: ["/image.png"],
|
||||
extension_ids: ["some_other_ext@mochitest"],
|
||||
},
|
||||
],
|
||||
host_permissions: ["*://example.com/*"],
|
||||
granted_host_permissions: true,
|
||||
},
|
||||
|
||||
files: {
|
||||
"image.png": IMAGE_ARRAYBUFFER,
|
||||
"page.js": pageScript,
|
||||
|
||||
"page.html": `<html><head>
|
||||
<meta charset="utf-8">
|
||||
<script src="page.js"></script>
|
||||
</head></html>`,
|
||||
},
|
||||
});
|
||||
|
||||
await extension.startup();
|
||||
let extensionUrl = `moz-extension://${extension.uuid}/`;
|
||||
let page = await ExtensionTestUtils.loadContentPage(
|
||||
"http://example.com/data/"
|
||||
);
|
||||
await extension.awaitMessage("page-loaded");
|
||||
|
||||
extension.sendMessage({
|
||||
name: "cannot access resource",
|
||||
elem: "img",
|
||||
url: `${extensionUrl}image.png`,
|
||||
content_context: true,
|
||||
shouldLoad: false,
|
||||
});
|
||||
await extension.awaitMessage("web-accessible-resources");
|
||||
|
||||
await page.close();
|
||||
await extension.unload();
|
||||
});
|
||||
|
|
Загрузка…
Ссылка в новой задаче