fix: Prevent a webpage document element to be detected as the Extension API object (#153)
This commit is contained in:
parent
450eee59a3
commit
ebd28186a1
|
@ -6,7 +6,7 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
if (typeof browser === "undefined") {
|
if (typeof browser === "undefined" || Object.getPrototypeOf(browser) !== Object.prototype) {
|
||||||
const CHROME_SEND_MESSAGE_CALLBACK_NO_RESPONSE_MESSAGE = "The message port closed before a response was received.";
|
const CHROME_SEND_MESSAGE_CALLBACK_NO_RESPONSE_MESSAGE = "The message port closed before a response was received.";
|
||||||
const SEND_RESPONSE_DEPRECATION_WARNING = "Returning a Promise is the preferred way to send a reply from an onMessage/onMessageExternal listener, as the sendResponse will be removed from the specs (See https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/API/runtime/onMessage)";
|
const SEND_RESPONSE_DEPRECATION_WARNING = "Returning a Promise is the preferred way to send a reply from an onMessage/onMessageExternal listener, as the sendResponse will be removed from the specs (See https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/API/runtime/onMessage)";
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
<!DOCTYPE>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Test Extension Background page</title>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!--
|
||||||
|
The following two DOM elements ensure that the polyfill doesn't detect the
|
||||||
|
globals defined for these DOM element ids as the Extensions API objects.
|
||||||
|
-->
|
||||||
|
<div id="browser"></div>
|
||||||
|
<div id="browser"></div>
|
||||||
|
<div id="chrome"></div>
|
||||||
|
|
||||||
|
<script src="copy-original-api-objects.js"></script>
|
||||||
|
<script src="browser-polyfill.js"></script>
|
||||||
|
<script src="background.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,14 @@
|
||||||
|
/* global originalAPIObjects */
|
||||||
|
|
||||||
|
browser.runtime.onMessage.addListener(async (msg, sender, sendResponse) => {
|
||||||
|
if (msg !== "test-api-object-in-background-page") {
|
||||||
|
throw new Error(`Unexpected message received: ${msg}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
browserIsDefined: !!browser,
|
||||||
|
chromeIsDefined: !!chrome,
|
||||||
|
browserIsUnchanged: browser === originalAPIObjects.browser,
|
||||||
|
windowBrowserIsUnchanged: window.browser === originalAPIObjects.browser,
|
||||||
|
};
|
||||||
|
});
|
|
@ -1,10 +1,12 @@
|
||||||
|
/* global originalAPIObjects */
|
||||||
|
|
||||||
test("browser api object in content script", (t) => {
|
test("browser api object in content script", (t) => {
|
||||||
t.ok(browser && browser.runtime, "a global browser API object should be defined");
|
t.ok(browser && browser.runtime, "a global browser API object should be defined");
|
||||||
t.ok(chrome && chrome.runtime, "a global chrome API object should be defined");
|
t.ok(chrome && chrome.runtime, "a global chrome API object should be defined");
|
||||||
|
|
||||||
if (navigator.userAgent.includes("Firefox/")) {
|
if (navigator.userAgent.includes("Firefox/")) {
|
||||||
// Check that the polyfill didn't create a polyfill wrapped browser API object on Firefox.
|
// Check that the polyfill didn't create a polyfill wrapped browser API object on Firefox.
|
||||||
t.equal(browser.runtime, chrome.runtime, "browser.runtime and chrome.runtime should be equal on Firefox");
|
t.equal(browser, originalAPIObjects.browser, "browser API object should not be changed on Firefox");
|
||||||
// On Firefox, window is not the global object for content scripts, and so we expect window.browser to not
|
// On Firefox, window is not the global object for content scripts, and so we expect window.browser to not
|
||||||
// be defined.
|
// be defined.
|
||||||
t.equal(window.browser, undefined, "window.browser is expected to be undefined on Firefox");
|
t.equal(window.browser, undefined, "window.browser is expected to be undefined on Firefox");
|
||||||
|
@ -16,3 +18,18 @@ test("browser api object in content script", (t) => {
|
||||||
t.equal(browser, window.browser, "browser and window.browser should be the same object");
|
t.equal(browser, window.browser, "browser and window.browser should be the same object");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("browser api object in background page", async (t) => {
|
||||||
|
const reply = await browser.runtime.sendMessage("test-api-object-in-background-page");
|
||||||
|
|
||||||
|
t.ok(reply.browserIsDefined, "a global browser API object should be defined");
|
||||||
|
t.ok(reply.chromeIsDefined, "a global chrome API object should be defined");
|
||||||
|
|
||||||
|
if (navigator.userAgent.includes("Firefox/")) {
|
||||||
|
t.ok(reply.browserIsUnchanged, "browser API object should not be changed on Firefox");
|
||||||
|
t.ok(reply.windowBrowserIsUnchanged, "window.browser API object should not be changed on Firefox");
|
||||||
|
} else {
|
||||||
|
t.ok(!reply.browserIsUnchanged, "browser API object should have been defined by the polyfill");
|
||||||
|
t.ok(!reply.windowBrowserIsUnchanged, "window.browser API object should have been defined by the polyfill");
|
||||||
|
}
|
||||||
|
});
|
8
test/fixtures/detect-existing-browser-api-object/copy-original-api-objects.js
vendored
Normal file
8
test/fixtures/detect-existing-browser-api-object/copy-original-api-objects.js
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
// Store a copy of the references to the original API objects.
|
||||||
|
const originalAPIObjects = { // eslint-disable-line no-unused-vars
|
||||||
|
// NOTE: on the Browsers that do not provide the browser API object natively,
|
||||||
|
// this will initially point to the HTMLCollection of the <div id="browser"> elements
|
||||||
|
// that are part of the background page.
|
||||||
|
browser,
|
||||||
|
chrome,
|
||||||
|
};
|
|
@ -3,12 +3,16 @@
|
||||||
"name": "test-detect-browser-api-object-in-content-script",
|
"name": "test-detect-browser-api-object-in-content-script",
|
||||||
"version": "0.1",
|
"version": "0.1",
|
||||||
"description": "test-detect-browser-api-object-in-content-script",
|
"description": "test-detect-browser-api-object-in-content-script",
|
||||||
|
"background": {
|
||||||
|
"page": "background.html"
|
||||||
|
},
|
||||||
"content_scripts": [
|
"content_scripts": [
|
||||||
{
|
{
|
||||||
"matches": [
|
"matches": [
|
||||||
"http://localhost/*"
|
"http://localhost/*"
|
||||||
],
|
],
|
||||||
"js": [
|
"js": [
|
||||||
|
"copy-original-api-objects.js",
|
||||||
"browser-polyfill.js",
|
"browser-polyfill.js",
|
||||||
"tape.js",
|
"tape.js",
|
||||||
"content.js"
|
"content.js"
|
|
@ -6,5 +6,13 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Browser Polyfill Test Page</h1>
|
<h1>Browser Polyfill Test Page</h1>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
The following two DOM elements ensure that the polyfill doesn't detect the
|
||||||
|
globals defined for these DOM element ids as the Extensions API objects.
|
||||||
|
-->
|
||||||
|
<div id="browser"></div>
|
||||||
|
<div id="browser"></div>
|
||||||
|
<div id="chrome"></div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -18,8 +18,8 @@ defineExtensionTests({
|
||||||
});
|
});
|
||||||
|
|
||||||
defineExtensionTests({
|
defineExtensionTests({
|
||||||
description: "polyfill should detect an existent browser API object in content scripts",
|
description: "polyfill should detect an existing browser API object in content scripts and extension pages",
|
||||||
extensions: ["detect-browser-api-object-in-content-script"],
|
extensions: ["detect-existing-browser-api-object"],
|
||||||
});
|
});
|
||||||
|
|
||||||
defineExtensionTests({
|
defineExtensionTests({
|
||||||
|
|
|
@ -43,7 +43,10 @@ function setupTestDOMWindow(chromeObject, browserObject = undefined) {
|
||||||
|
|
||||||
// Set (or reset) the browser property.
|
// Set (or reset) the browser property.
|
||||||
if (browserObject) {
|
if (browserObject) {
|
||||||
window.browser = browserObject;
|
// Make the fake browser object a `window.Object` instance, so that
|
||||||
|
// it passes the `Object.getPrototypeOf(browser) !== Object.prototype`
|
||||||
|
// check, otherwise it is going to be overridden by the polyfill (See #153).
|
||||||
|
window.browser = Object.assign(window.Object(), browserObject);
|
||||||
} else {
|
} else {
|
||||||
delete window.browser;
|
delete window.browser;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue