Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Errors
- This test gets skipped with pattern: http3 OR http2
- This test failed 2 times in the preceding 30 days. quicksearch this test
- Manifest: toolkit/components/extensions/test/mochitest/mochitest-remote.toml includes toolkit/components/extensions/test/mochitest/mochitest-common.toml
- Manifest: toolkit/components/extensions/test/mochitest/mochitest.toml includes toolkit/components/extensions/test/mochitest/mochitest-common.toml
<!DOCTYPE HTML>
<html>
<head>
<title>Tabs permissions test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
<script type="text/javascript" src="head.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="text/javascript">
"use strict";
// This is the longest running mochitest here, often times out on ASAN builds.
if (AppConstants.ASAN) {
SimpleTest.requestLongerTimeout(5);
}
const URL1 =
const URL2 =
const helperExtensionDef = {
manifest: {
permissions: ["webNavigation", "<all_urls>"],
},
async background() {
browser.test.onMessage.addListener(async message => {
switch (message.subject) {
case "createTab": {
const tabLoaded = new Promise(resolve => {
browser.webNavigation.onCompleted.addListener(function listener(
details
) {
if (details.url === message.data.url) {
browser.webNavigation.onCompleted.removeListener(listener);
resolve();
}
});
});
const tab = await browser.tabs.create({ url: message.data.url });
await tabLoaded;
browser.test.sendMessage("tabCreated", tab.id);
break;
}
case "changeTabURL": {
const tabLoaded = new Promise(resolve => {
browser.webNavigation.onCompleted.addListener(function listener(
details
) {
if (details.url === message.data.url) {
browser.webNavigation.onCompleted.removeListener(listener);
resolve();
}
});
});
await browser.tabs.update(message.data.tabId, {
url: message.data.url,
});
await tabLoaded;
browser.test.sendMessage("tabURLChanged", message.data.tabId);
break;
}
case "changeTabHashAndTitle": {
const tabChanged = new Promise(resolve => {
let hasURLChangeInfo = false,
hasTitleChangeInfo = false;
browser.tabs.onUpdated.addListener(function listener(
tabId,
changeInfo
) {
if (changeInfo.url?.endsWith(message.data.urlHash)) {
hasURLChangeInfo = true;
}
if (changeInfo.title === message.data.title) {
hasTitleChangeInfo = true;
}
if (hasURLChangeInfo && hasTitleChangeInfo) {
browser.tabs.onUpdated.removeListener(listener);
resolve();
}
});
});
await browser.tabs.executeScript(message.data.tabId, {
code: `
document.location.hash = ${JSON.stringify(message.data.urlHash)};
document.title = ${JSON.stringify(message.data.title)};
`,
});
await tabChanged;
browser.test.sendMessage("tabHashAndTitleChanged");
break;
}
case "removeTab": {
await browser.tabs.remove(message.data.tabId);
browser.test.sendMessage("tabRemoved");
break;
}
default:
browser.test.fail(`Received unexpected message: ${message}`);
}
});
},
};
/*
* Test tabs.query function
* Check if the correct tabs are queried by url or title based on the granted permissions
*/
async function test_query(testCases, permissions) {
const helperExtension = ExtensionTestUtils.loadExtension(helperExtensionDef);
const extension = ExtensionTestUtils.loadExtension({
manifest: {
permissions,
},
async background() {
// wait for start message
const [testCases, tabIdFromURL1, tabIdFromURL2] = await new Promise(
resolve => {
browser.test.onMessage.addListener(message => resolve(message));
}
);
for (const testCase of testCases) {
const query = testCase.query;
const matchingTabs = testCase.matchingTabs;
let tabQuery = await browser.tabs.query(query);
// ignore other tabs in the window
tabQuery = tabQuery.filter(tab => {
return tab.id === tabIdFromURL1 || tab.id === tabIdFromURL2;
});
browser.test.assertEq(matchingTabs, tabQuery.length, `Tabs queried`);
}
// send end message
browser.test.notifyPass("tabs.query");
},
});
await helperExtension.startup();
await extension.startup();
helperExtension.sendMessage({
subject: "createTab",
data: { url: URL1 },
});
const tabIdFromURL1 = await helperExtension.awaitMessage("tabCreated");
helperExtension.sendMessage({
subject: "createTab",
data: { url: URL2 },
});
const tabIdFromURL2 = await helperExtension.awaitMessage("tabCreated");
if (permissions.includes("activeTab")) {
extension.grantActiveTab(tabIdFromURL2);
}
extension.sendMessage([testCases, tabIdFromURL1, tabIdFromURL2]);
await extension.awaitFinish("tabs.query");
helperExtension.sendMessage({
subject: "removeTab",
data: { tabId: tabIdFromURL1 },
});
await helperExtension.awaitMessage("tabRemoved");
helperExtension.sendMessage({
subject: "removeTab",
data: { tabId: tabIdFromURL2 },
});
await helperExtension.awaitMessage("tabRemoved");
await extension.unload();
await helperExtension.unload();
}
add_task(function query_with_host_permission_url1() {
return test_query(
[
{
query: { url: "*://www.example.com/*" },
matchingTabs: 1,
},
{
query: { url: "<all_urls>" },
matchingTabs: 1,
},
{
query: { url: ["*://www.example.com/*", "*://example.net/*"] },
matchingTabs: 1,
},
{
query: { title: "The Title" },
matchingTabs: 1,
},
{
query: { title: "Another Title" },
matchingTabs: 0,
},
{
query: {},
matchingTabs: 2,
},
],
["*://www.example.com/*"]
);
});
add_task(function query_with_host_permission_url2() {
return test_query(
[
{
query: { url: "*://www.example.com/*" },
matchingTabs: 0,
},
{
query: { url: "<all_urls>" },
matchingTabs: 1,
},
{
query: { url: ["*://www.example.com/*", "*://example.net/*"] },
matchingTabs: 1,
},
{
query: { title: "The Title" },
matchingTabs: 0,
},
{
query: { title: "Another Title" },
matchingTabs: 1,
},
{
query: {},
matchingTabs: 2,
},
],
["*://example.net/*"]
);
});
// <all_urls> permission
add_task(function query_with_host_permission_all_urls() {
return test_query(
[
{
query: { url: "*://www.example.com/*" },
matchingTabs: 1,
},
{
query: { url: "<all_urls>" },
matchingTabs: 2,
},
{
query: { url: ["*://www.example.com/*", "*://example.net/*"] },
matchingTabs: 2,
},
{
query: { title: "The Title" },
matchingTabs: 1,
},
{
query: { title: "Another Title" },
matchingTabs: 1,
},
{
query: {},
matchingTabs: 2,
},
],
["<all_urls>"]
);
});
// tabs permission
add_task(function query_with_tabs_permission() {
return test_query(
[
{
query: { url: "*://www.example.com/*" },
matchingTabs: 1,
},
{
query: { url: "<all_urls>" },
matchingTabs: 2,
},
{
query: { url: ["*://www.example.com/*", "*://example.net/*"] },
matchingTabs: 2,
},
{
query: { title: "The Title" },
matchingTabs: 1,
},
{
query: { title: "Another Title" },
matchingTabs: 1,
},
{
query: {},
matchingTabs: 2,
},
],
["tabs"]
);
});
// activeTab permission
add_task(function query_with_activeTab_permission() {
return test_query(
[
{
query: { url: "*://www.example.com/*" },
matchingTabs: 0,
},
{
query: { url: "<all_urls>" },
matchingTabs: 1,
},
{
query: { url: ["*://www.example.com/*", "*://example.net/*"] },
matchingTabs: 1,
},
{
query: { title: "The Title" },
matchingTabs: 0,
},
{
query: { title: "Another Title" },
matchingTabs: 1,
},
{
query: {},
matchingTabs: 2,
},
],
["activeTab"]
);
});
// no permission
add_task(function query_without_permission() {
return test_query(
[
{
query: { url: "*://www.example.com/*" },
matchingTabs: 0,
},
{
query: { url: "<all_urls>" },
matchingTabs: 0,
},
{
query: { url: ["*://www.example.com/*", "*://example.net/*"] },
matchingTabs: 0,
},
{
query: { title: "The Title" },
matchingTabs: 0,
},
{
query: { title: "Another Title" },
matchingTabs: 0,
},
{
query: {},
matchingTabs: 2,
},
],
[]
);
});
/*
* Test tabs.onUpdate and tabs.get function
* Check if the changeInfo or tab object contains the restricted properties
* url and title only when the right permissions are granted
* The tab is updated without causing navigation in order to also test activeTab permission
*/
async function test_restricted_properties(
permissions,
hasRestrictedProperties
) {
const helperExtension = ExtensionTestUtils.loadExtension(helperExtensionDef);
const extension = ExtensionTestUtils.loadExtension({
manifest: {
permissions,
},
async background() {
// wait for test start signal and data
const [
hasRestrictedProperties,
tabId,
urlHash,
title,
] = await new Promise(resolve => {
browser.test.onMessage.addListener(message => {
resolve(message);
});
});
let hasURLChangeInfo = false,
hasTitleChangeInfo = false;
function onUpdateListener(tabId, changeInfo) {
if (changeInfo.url?.endsWith(urlHash)) {
hasURLChangeInfo = true;
}
if (changeInfo.title === title) {
hasTitleChangeInfo = true;
}
}
browser.tabs.onUpdated.addListener(onUpdateListener);
// wait for test evaluation signal and data
await new Promise(resolve => {
browser.test.onMessage.addListener(message => {
if (message === "collectTestResults") {
resolve(message);
}
});
browser.test.sendMessage("waitingForTabPropertyChanges");
});
// check onUpdate changeInfo
browser.test.assertEq(
hasRestrictedProperties,
hasURLChangeInfo,
`Has changeInfo property "url"`
);
browser.test.assertEq(
hasRestrictedProperties,
hasTitleChangeInfo,
`Has changeInfo property "title"`
);
// check tab properties
const tabGet = await browser.tabs.get(tabId);
browser.test.assertEq(
hasRestrictedProperties,
!!tabGet.url?.endsWith(urlHash),
`Has tab property "url"`
);
browser.test.assertEq(
hasRestrictedProperties,
tabGet.title === title,
`Has tab property "title"`
);
// send end message
browser.test.notifyPass("tabs.restricted_properties");
},
});
const urlHash = "#ChangedURL";
const title = "Changed Title";
await helperExtension.startup();
await extension.startup();
helperExtension.sendMessage({
subject: "createTab",
data: { url: URL1 },
});
const tabId = await helperExtension.awaitMessage("tabCreated");
if (permissions.includes("activeTab")) {
extension.grantActiveTab(tabId);
}
// send test start signal and data
extension.sendMessage([hasRestrictedProperties, tabId, urlHash, title]);
await extension.awaitMessage("waitingForTabPropertyChanges");
helperExtension.sendMessage({
subject: "changeTabHashAndTitle",
data: {
tabId,
urlHash,
title,
},
});
await helperExtension.awaitMessage("tabHashAndTitleChanged");
// send end signal and evaluate results
extension.sendMessage("collectTestResults");
await extension.awaitFinish("tabs.restricted_properties");
helperExtension.sendMessage({
subject: "removeTab",
data: { tabId },
});
await helperExtension.awaitMessage("tabRemoved");
await extension.unload();
await helperExtension.unload();
}
add_task(function has_restricted_properties_with_host_permission_url1() {
return test_restricted_properties(["*://www.example.com/*"], true);
});
add_task(function has_restricted_properties_with_host_permission_url2() {
return test_restricted_properties(["*://example.net/*"], false);
});
// <all_urls> permission
add_task(function has_restricted_properties_with_host_permission_all_urls() {
return test_restricted_properties(["<all_urls>"], true);
});
// tabs permission
add_task(function has_restricted_properties_with_tabs_permission() {
return test_restricted_properties(["tabs"], true);
});
// activeTab permission
add_task(function has_restricted_properties_with_activeTab_permission() {
return test_restricted_properties(["activeTab"], true);
// no permission
add_task(function has_restricted_properties_without_permission() {
return test_restricted_properties([], false);
});
/*
* Test tabs.onUpdate filter functionality
* Check if the restricted filter properties only work if the
* right permissions are granted
*/
async function test_onUpdateFilter(testCases, permissions) {
// Filters for onUpdated are not supported on Android.
if (AppConstants.platform === "android") {
return;
}
const helperExtension = ExtensionTestUtils.loadExtension(helperExtensionDef);
const extension = ExtensionTestUtils.loadExtension({
manifest: {
permissions,
},
async background() {
let listenerGotCalled = false;
function onUpdateListener() {
listenerGotCalled = true;
}
browser.test.onMessage.addListener(async message => {
switch (message.subject) {
case "setup": {
browser.tabs.onUpdated.addListener(
onUpdateListener,
message.data.filter
);
browser.test.sendMessage("done");
break;
}
case "collectTestResults": {
browser.test.assertEq(
message.data.expectEvent,
listenerGotCalled,
`Update listener called`
);
browser.tabs.onUpdated.removeListener(onUpdateListener);
listenerGotCalled = false;
browser.test.sendMessage("done");
break;
}
default:
browser.test.fail(`Received unexpected message: ${message}`);
}
});
},
});
await helperExtension.startup();
await extension.startup();
for (const testCase of testCases) {
helperExtension.sendMessage({
subject: "createTab",
data: { url: URL1 },
});
const tabId = await helperExtension.awaitMessage("tabCreated");
extension.sendMessage({
subject: "setup",
data: {
filter: testCase.filter,
},
});
await extension.awaitMessage("done");
helperExtension.sendMessage({
subject: "changeTabURL",
data: {
tabId,
url: URL2,
},
});
await helperExtension.awaitMessage("tabURLChanged");
extension.sendMessage({
subject: "collectTestResults",
data: {
expectEvent: testCase.expectEvent,
},
});
await extension.awaitMessage("done");
helperExtension.sendMessage({
subject: "removeTab",
data: { tabId },
});
await helperExtension.awaitMessage("tabRemoved");
}
await extension.unload();
await helperExtension.unload();
}
add_task(function onUpdateFilter_with_host_permission_url3() {
return test_onUpdateFilter(
[
{
filter: { urls: ["*://mozilla.org/*"] },
expectEvent: false,
},
{
filter: { urls: ["<all_urls>"] },
expectEvent: false,
},
{
filter: { urls: ["*://mozilla.org/*", "*://example.net/*"] },
expectEvent: false,
},
{
filter: { properties: ["title"] },
expectEvent: false,
},
{
filter: {},
expectEvent: true,
},
],
["*://mozilla.org/*"]
);
});
add_task(function onUpdateFilter_with_host_permission_url2() {
return test_onUpdateFilter(
[
{
filter: { urls: ["*://mozilla.org/*"] },
expectEvent: false,
},
{
filter: { urls: ["<all_urls>"] },
expectEvent: true,
},
{
filter: { urls: ["*://mozilla.org/*", "*://example.net/*"] },
expectEvent: true,
},
{
filter: { properties: ["title"] },
expectEvent: true,
},
{
filter: {},
expectEvent: true,
},
],
["*://example.net/*"]
);
});
// <all_urls> permission
add_task(function onUpdateFilter_with_host_permission_all_urls() {
return test_onUpdateFilter(
[
{
filter: { urls: ["*://mozilla.org/*"] },
expectEvent: false,
},
{
filter: { urls: ["<all_urls>"] },
expectEvent: true,
},
{
filter: { urls: ["*://mozilla.org/*", "*://example.net/*"] },
expectEvent: true,
},
{
filter: { properties: ["title"] },
expectEvent: true,
},
{
filter: {},
expectEvent: true,
},
],
["<all_urls>"]
);
});
// tabs permission
add_task(function onUpdateFilter_with_tabs_permission() {
return test_onUpdateFilter(
[
{
filter: { urls: ["*://mozilla.org/*"] },
expectEvent: false,
},
{
filter: { urls: ["<all_urls>"] },
expectEvent: true,
},
{
filter: { urls: ["*://mozilla.org/*", "*://example.net/*"] },
expectEvent: true,
},
{
filter: { properties: ["title"] },
expectEvent: true,
},
{
filter: {},
expectEvent: true,
},
],
["tabs"]
);
});
</script>
</body>
</html>