Bug 1396399: Test for content scripts and activeTab permission.

Differential Revision: https://phabricator.services.mozilla.com/D90333
This commit is contained in:
Andrew Swan 2020-09-22 17:41:36 +00:00
Родитель e56a7a73a7
Коммит 6b380a56a7
7 изменённых файлов: 418 добавлений и 0 удалений

Просмотреть файл

@ -1958,6 +1958,10 @@ class SpecialPowersChild extends JSWindowActorChild {
sendMessage(...args) {
sp.sendAsyncMessage("SPExtensionMessage", { id, args });
},
grantActiveTab(tabId) {
sp.sendAsyncMessage("SPExtensionGrantActiveTab", { id, tabId });
},
};
this.sendAsyncMessage("SPLoadExtension", { ext, id });

Просмотреть файл

@ -1188,6 +1188,13 @@ class SpecialPowersParent extends JSWindowActorParent {
return undefined;
}
case "SPExtensionGrantActiveTab": {
let { id, tabId } = aMessage.data;
let { tabManager } = this._extensions.get(id);
tabManager.addActiveTabPermission(tabManager.get(tabId).nativeTab);
return undefined;
}
case "SPUnloadExtension": {
let id = aMessage.data.id;
let extension = this._extensions.get(id);

Просмотреть файл

@ -0,0 +1,11 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<iframe id="emptyframe"></iframe>
<iframe id="regularframe" src="http://test1.example.com/"></iframe>
</body>
</html>

Просмотреть файл

@ -0,0 +1,10 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<iframe srcdoc="<iframe src='http://test1.example.com/'&gt;</iframe&gt;"></iframe>
</body>
</html>

Просмотреть файл

@ -0,0 +1,10 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<iframe id="frame" src="http://test2.example.com/"></iframe>
</body>
</html>

Просмотреть файл

@ -8,6 +8,9 @@ support-files =
file_contains_img.html
file_contains_iframe.html
file_green.html
file_contentscript_activeTab.html
file_contentscript_activeTab2.html
file_contentscript_iframe.html
file_image_bad.png
file_image_good.png
file_image_great.png
@ -78,6 +81,8 @@ skip-if = os == 'android'
skip-if = headless # Bug 1405872
[test_ext_contentscript_about_blank.html]
skip-if = os == 'android' # bug 1369440
[test_ext_contentscript_activeTab.html]
skip-if = os == 'android' || true # Broken by DocumentChannel
[test_ext_contentscript_cache.html]
skip-if = (os == 'linux' && debug) || (toolkit == 'android' && debug) # bug 1348241
fail-if = xorigin # TypeError: can't access property "staticScripts", ext is undefined - Should not throw any errors

Просмотреть файл

@ -0,0 +1,371 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for content script</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" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="text/javascript">
"use strict";
// Create a test extension with the provided function as the background
// script. The background script will have a few helpful functions
// available.
/* global awaitLoad, gatherFrameSources */
function makeExtension(background) {
// Wait for a webNavigation.onCompleted event where the details for the
// loaded page match the attributes of `filter`.
function awaitLoad(filter) {
return new Promise(resolve => {
const listener = details => {
if (Object.keys(filter).every(key => details[key] === filter[key])) {
browser.webNavigation.onCompleted.removeListener(listener);
resolve();
}
};
browser.webNavigation.onCompleted.addListener(listener);
});
}
// Return a string with a (sorted) list of the source of all frames
// in the given tab into which this extension can inject scripts
// (ie all frames for which it has the activeTab permission).
// Source is the hostname for frames in http sources, or the full
// location href in other documents (eg about: pages)
async function gatherFrameSources(tabid) {
let result = await browser.tabs.executeScript(tabid, {
allFrames: true,
matchAboutBlank: true,
code: "window.location.hostname || window.location.href;",
});
return String(result.sort());
}
return ExtensionTestUtils.loadExtension({
manifest: {
permissions: ["activeTab", "webNavigation"],
},
background: `${awaitLoad}\n${gatherFrameSources}\n${ExtensionTestCommon.serializeScript(background)}`,
});
}
// Test that executeScript() fails without the activeTab permission
// (or any specific origin permissions).
add_task(async function test_no_activeTab() {
let extension = makeExtension(async function background() {
const URL = "http://mochi.test:8888/tests/toolkit/components/extensions/test/mochitest/file_contentscript_activeTab.html";
let [tab] = await Promise.all([
browser.tabs.create({url: URL}),
awaitLoad({frameId: 0}),
]);
try {
await gatherFrameSources(tab.id);
browser.test.fail("executeScript() should fail without activeTab permission");
} catch (err) {
browser.test.assertTrue(/^Missing host permission/.test(err.message),
"executeScript() without activeTab permission failed");
}
await browser.tabs.remove(tab.id);
browser.test.notifyPass("no-active-tab");
});
await extension.startup();
await extension.awaitFinish("no-active-tab");
await extension.unload();
});
// Test that dynamically created iframes do not get the activeTab permission
add_task(async function test_dynamic_frames() {
let extension = makeExtension(async function background() {
const BASE_HOST = "www.example.com";
let [tab] = await Promise.all([
browser.tabs.create({url: `http://${BASE_HOST}/`}),
awaitLoad({frameId: 0}),
]);
function inject() {
let nframes = 4;
function frameLoaded() {
nframes--;
if (nframes == 0) {
browser.runtime.sendMessage("frames-loaded");
}
}
let frame = document.createElement("iframe");
frame.addEventListener("load", frameLoaded, {once: true});
document.body.appendChild(frame);
let div = document.createElement("div");
div.innerHTML = "<iframe src='http://test1.example.com/'></iframe>";
let framelist = div.getElementsByTagName("iframe");
browser.test.assertEq(1, framelist.length, "Found 1 frame inside div");
framelist[0].addEventListener("load", frameLoaded, {once: true});
document.body.appendChild(div);
let div2 = document.createElement("div");
div2.innerHTML = "<iframe srcdoc=\"<iframe src='http://test2.example.com/'&gt;</iframe&gt;\"></iframe>";
framelist = div2.getElementsByTagName("iframe");
browser.test.assertEq(1, framelist.length, "Found 1 frame inside div");
framelist[0].addEventListener("load", frameLoaded, {once: true});
document.body.appendChild(div2);
const URL = "http://www.example.com/tests/toolkit/components/extensions/test/mochitest/file_contentscript_iframe.html";
let xhr = new XMLHttpRequest();
xhr.open("GET", URL);
xhr.responseType = "document";
xhr.overrideMimeType("text/html");
xhr.addEventListener("load", () => {
if (xhr.readyState != 4) {
return;
}
if (xhr.status != 200) {
browser.runtime.sendMessage("error");
}
let frame = xhr.response.getElementById("frame");
browser.test.assertTrue(frame, "Found frame in response document");
frame.addEventListener("load", frameLoaded, {once: true});
document.body.appendChild(frame);
}, {once: true});
xhr.addEventListener("error", () => {
browser.runtime.sendMessage("error");
}, {once: true});
xhr.send();
}
browser.test.onMessage.addListener(async () => {
let loadedPromise = new Promise((resolve, reject) => {
let listener = msg => {
let unlisten = () => browser.runtime.onMessage.removeListener(listener);
if (msg == "frames-loaded") {
unlisten();
resolve();
} else if (msg == "error") {
unlisten();
reject();
}
};
browser.runtime.onMessage.addListener(listener);
});
await browser.tabs.executeScript(tab.id, {
code: `(${inject})();`,
});
await loadedPromise;
let result = await gatherFrameSources(tab.id);
browser.test.assertEq(String([BASE_HOST]), result,
"Script is not injected into dynamically created frames");
await browser.tabs.remove(tab.id);
browser.test.notifyPass("dynamic-frames");
});
browser.test.sendMessage("ready", tab.id);
});
await extension.startup();
let tabId = await extension.awaitMessage("ready");
extension.grantActiveTab(tabId);
extension.sendMessage("go");
await extension.awaitFinish("dynamic-frames");
await extension.unload();
});
// Test that an iframe created from an <iframe srcdoc> gets the
// activeTab permission.
add_task(async function test_srcdoc() {
let extension = makeExtension(async function background() {
const URL = "http://mochi.test:8888/tests/toolkit/components/extensions/test/mochitest/file_contentscript_activeTab2.html";
const OUTER_SOURCE = "about:srcdoc";
const PAGE_SOURCE = "mochi.test";
const FRAME_SOURCE = "test1.example.com";
let [tab] = await Promise.all([
browser.tabs.create({url: URL}),
awaitLoad({frameId: 0}),
]);
browser.test.onMessage.addListener(async msg => {
if (msg == "go") {
let result = await gatherFrameSources(tab.id);
browser.test.assertEq(String([OUTER_SOURCE, PAGE_SOURCE, FRAME_SOURCE]),
result,
"Script is injected into frame created from <iframe srcdoc>");
await browser.tabs.remove(tab.id);
browser.test.notifyPass("srcdoc");
}
});
browser.test.sendMessage("ready", tab.id);
});
await extension.startup();
let tabId = await extension.awaitMessage("ready");
extension.grantActiveTab(tabId);
extension.sendMessage("go");
await extension.awaitFinish("srcdoc");
await extension.unload();
});
// Test that navigating frames by setting the src attribute from the
// parent page revokes the activeTab permission.
add_task(async function test_navigate_by_src() {
let extension = makeExtension(async function background() {
const URL = "http://mochi.test:8888/tests/toolkit/components/extensions/test/mochitest/file_contentscript_activeTab.html";
const PAGE_SOURCE = "mochi.test";
const EMPTY_SOURCE = "about:blank";
const FRAME_SOURCE = "test1.example.com";
let [tab] = await Promise.all([
browser.tabs.create({url: URL}),
awaitLoad({frameId: 0}),
]);
browser.test.onMessage.addListener(async msg => {
if (msg == "go") {
let result = await gatherFrameSources(tab.id);
browser.test.assertEq(String([EMPTY_SOURCE, PAGE_SOURCE, FRAME_SOURCE]),
result,
"In original page, script is injected into base page and original frames");
let loadedPromise = awaitLoad({tabId: tab.id});
await browser.tabs.executeScript(tab.id, {
code: "document.getElementById('emptyframe').src = 'http://test2.example.com/';",
});
await loadedPromise;
result = await gatherFrameSources(tab.id);
browser.test.assertEq(String([PAGE_SOURCE, FRAME_SOURCE]), result,
"Script is not injected into initially empty frame after navigation");
loadedPromise = awaitLoad({tabId: tab.id});
await browser.tabs.executeScript(tab.id, {
code: "document.getElementById('regularframe').src = 'http://test2.example.com/';",
});
await loadedPromise;
result = await gatherFrameSources(tab.id);
browser.test.assertEq(String([PAGE_SOURCE]), result,
"Script is not injected into regular frame after navigation");
await browser.tabs.remove(tab.id);
browser.test.notifyPass("test-scripts");
}
});
browser.test.sendMessage("ready", tab.id);
});
await extension.startup();
let tabId = await extension.awaitMessage("ready");
extension.grantActiveTab(tabId);
extension.sendMessage("go");
await extension.awaitFinish("test-scripts");
await extension.unload();
});
// Test that navigating frames by setting window.location from inside the
// frame revokes the activeTab permission.
add_task(async function test_navigate_by_window_location() {
let extension = makeExtension(async function background() {
const URL = "http://mochi.test:8888/tests/toolkit/components/extensions/test/mochitest/file_contentscript_activeTab.html";
const PAGE_SOURCE = "mochi.test";
const EMPTY_SOURCE = "about:blank";
const FRAME_SOURCE = "test1.example.com";
let [tab] = await Promise.all([
browser.tabs.create({url: URL}),
awaitLoad({frameId: 0}),
]);
browser.test.onMessage.addListener(async msg => {
if (msg == "go") {
let result = await gatherFrameSources(tab.id);
browser.test.assertEq(String([EMPTY_SOURCE, PAGE_SOURCE, FRAME_SOURCE]),
result,
"Script initially injected into all frames");
let nframes = 0;
let frames = await browser.webNavigation.getAllFrames({tabId: tab.id});
for (let frame of frames) {
if (frame.parentFrameId == -1) {
continue;
}
let loadPromise = awaitLoad({
tabId: tab.id,
frameId: frame.frameId,
});
await browser.tabs.executeScript(tab.id, {
frameId: frame.frameId,
matchAboutBlank: true,
code: "window.location.href = 'https://test2.example.com/';",
});
await loadPromise;
try {
result = await browser.tabs.executeScript(tab.id, {
frameId: frame.frameId,
matchAboutBlank: true,
code: "window.location.hostname;",
});
browser.test.fail("executeScript should have failed on navigated frame");
} catch (err) {
browser.test.assertEq("Frame not found, or missing host permission", err.message);
}
nframes++;
}
browser.test.assertEq(2, nframes, "Found 2 frames");
await browser.tabs.remove(tab.id);
browser.test.notifyPass("scripted-navigation");
}
});
browser.test.sendMessage("ready", tab.id);
});
await extension.startup();
let tabId = await extension.awaitMessage("ready");
extension.grantActiveTab(tabId);
extension.sendMessage("go");
await extension.awaitFinish("scripted-navigation");
await extension.unload();
});
</script>
</body>
</html>