summaryrefslogtreecommitdiffstats
path: root/mobile/android/geckoview/src/androidTest/assets/web_extensions
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/geckoview/src/androidTest/assets/web_extensions')
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/.eslintrc.js14
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/background.js190
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/button/beasts-32-light.pngbin0 -> 1395 bytes
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/button/beasts-32.pngbin0 -> 1093 bytes
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/button/expected.pngbin0 -> 1074 bytes
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/button/geo-19.pngbin0 -> 225 bytes
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/button/geo-38.pngbin0 -> 225 bytes
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/button/icon.svg1
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/content.js4
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/manifest.json43
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/test-open-popup-browser-action.html14
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/test-open-popup-browser-action.js7
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/test-open-popup-page-action.html14
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/test-open-popup-page-action.js7
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/test-popup-messaging.html9
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/test-popup-messaging.js24
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/test-popup.html9
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/test-popup.js3
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/borderify-missing-id.xpibin0 -> 1827 bytes
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/borderify-unsigned.xpibin0 -> 1882 bytes
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/borderify-unsigned.zipbin0 -> 1882 bytes
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/borderify.xpibin0 -> 9221 bytes
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/borderify/borderify.js1
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/borderify/icons/border-48.pngbin0 -> 225 bytes
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/borderify/icons/icon.svg1
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/borderify/manifest.json23
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/browsing-data-built-in/background.js44
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/browsing-data-built-in/manifest.json15
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/browsing-data/background.js8
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/browsing-data/manifest.json15
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/download-flags-false/download.js3
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/download-flags-false/manifest.json15
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/download-flags-true/download.js16
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/download-flags-true/manifest.json15
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/download-onChanged/download.js18
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/download-onChanged/manifest.json15
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/dummy-incompatible.xpibin0 -> 521 bytes
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/dummy.xpibin0 -> 544 bytes
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/dummy/dummy.js1
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/dummy/manifest.json21
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/extension-page-restore/manifest.json11
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/extension-page-restore/tab-script.js5
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/extension-page-restore/tab.html10
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/extension-page-update/background-script.js7
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/extension-page-update/manifest.json21
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/extension-page-update/tab-script.js2
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/extension-page-update/tab.html10
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/extension-page-update/tabs.js1
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/langpack_signed.xpibin0 -> 4452 bytes
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/messaging-content/manifest.json22
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/messaging-content/messaging.js29
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/messaging-iframe/manifest.json23
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/messaging-iframe/messaging.js11
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/messaging/background.js28
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/messaging/icons/border-48.pngbin0 -> 225 bytes
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/messaging/manifest.json18
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/notification-test/background.js6
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/notification-test/manifest.json15
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/openoptionspage-1/background.js1
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/openoptionspage-1/manifest.json20
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/openoptionspage-2/background.js1
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/openoptionspage-2/manifest.json20
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/page-history/manifest.json11
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/page-history/page.html9
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/permission-request/clickToRequestPermission.html11
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/permission-request/manifest.json13
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/permission-request/request-permission.js11
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/redirect-to-android-resource/background.js39
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/redirect-to-android-resource/manifest.json25
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/redirect-to-android-resource/web-accessible-script.js3
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-activate-remove-2/background.js16
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-activate-remove-2/manifest.json15
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-activate-remove/background.js16
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-activate-remove/manifest.json15
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-create-2/background.js4
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-create-2/manifest.json15
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-create-remove/background.js3
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-create-remove/manifest.json15
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-create/background.js1
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-create/manifest.json15
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-remove/background.js3
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-remove/manifest.json15
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/TestSupportChild.sys.mjs83
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/TestSupportProcessChild.sys.mjs22
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/background.js127
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/manifest.json42
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-api.js256
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-schema.json308
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-support.js60
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/update-1/borderify.js1
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/update-1/manifest.json18
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/update-2/borderify.js1
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/update-2/manifest.json17
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/update-postpone-1/background.js3
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/update-postpone-1/borderify.js1
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/update-postpone-1/manifest.json21
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/update-postpone-2/borderify.js1
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/update-postpone-2/manifest.json17
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/update-with-perms-1/borderify.js1
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/update-with-perms-1/manifest.json18
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/update-with-perms-2/borderify.js1
-rw-r--r--mobile/android/geckoview/src/androidTest/assets/web_extensions/update-with-perms-2/manifest.json18
102 files changed, 2082 insertions, 0 deletions
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/.eslintrc.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/.eslintrc.js
new file mode 100644
index 0000000000..41c5ed8080
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/.eslintrc.js
@@ -0,0 +1,14 @@
+"use strict";
+
+module.exports = {
+ env: {
+ webextensions: true,
+ },
+ globals: {
+ ExtensionAPI: true,
+ // available to frameScripts
+ addMessageListener: false,
+ content: false,
+ sendAsyncMessage: false,
+ },
+};
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/background.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/background.js
new file mode 100644
index 0000000000..dab0f5d897
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/background.js
@@ -0,0 +1,190 @@
+const port = browser.runtime.connectNative("browser");
+port.onMessage.addListener(message => {
+ handleMessage(message, null);
+});
+
+browser.runtime.onMessage.addListener((message, sender) => {
+ handleMessage(message, sender.tab.id);
+});
+
+browser.pageAction.onClicked.addListener(tab => {
+ port.postMessage({ method: "onClicked", tabId: tab.id, type: "pageAction" });
+});
+
+browser.browserAction.onClicked.addListener(tab => {
+ port.postMessage({
+ method: "onClicked",
+ tabId: tab.id,
+ type: "browserAction",
+ });
+});
+
+function handlePageActionMessage(message, tabId) {
+ switch (message.action) {
+ case "enable":
+ browser.pageAction.show(tabId);
+ break;
+
+ case "disable":
+ browser.pageAction.hide(tabId);
+ break;
+
+ case "setPopup":
+ browser.pageAction.setPopup({
+ tabId,
+ popup: message.popup,
+ });
+ break;
+
+ case "setPopupCheckRestrictions":
+ browser.pageAction
+ .setPopup({
+ tabId,
+ popup: message.popup,
+ })
+ .then(
+ () => {
+ port.postMessage({
+ resultFor: "setPopup",
+ type: "pageAction",
+ success: true,
+ });
+ },
+ err => {
+ port.postMessage({
+ resultFor: "setPopup",
+ type: "pageAction",
+ success: false,
+ error: String(err),
+ });
+ }
+ );
+ break;
+
+ case "setTitle":
+ browser.pageAction.setTitle({
+ tabId,
+ title: message.title,
+ });
+ break;
+
+ case "setIcon":
+ browser.pageAction.setIcon({
+ tabId,
+ imageData: message.imageData,
+ path: message.path,
+ });
+ break;
+
+ default:
+ throw new Error(`Page Action does not support ${message.action}`);
+ }
+}
+
+function handleBrowserActionMessage(message, tabId) {
+ switch (message.action) {
+ case "enable":
+ browser.browserAction.enable(tabId);
+ break;
+
+ case "disable":
+ browser.browserAction.disable(tabId);
+ break;
+
+ case "setBadgeText":
+ browser.browserAction.setBadgeText({
+ tabId,
+ text: message.text,
+ });
+ break;
+
+ case "setBadgeTextColor":
+ browser.browserAction.setBadgeTextColor({
+ tabId,
+ color: message.color,
+ });
+ break;
+
+ case "setBadgeBackgroundColor":
+ browser.browserAction.setBadgeBackgroundColor({
+ tabId,
+ color: message.color,
+ });
+ break;
+
+ case "setPopup":
+ browser.browserAction.setPopup({
+ tabId,
+ popup: message.popup,
+ });
+ break;
+
+ case "setPopupCheckRestrictions":
+ browser.browserAction
+ .setPopup({
+ tabId,
+ popup: message.popup,
+ })
+ .then(
+ () => {
+ port.postMessage({
+ resultFor: "setPopup",
+ type: "browserAction",
+ success: true,
+ });
+ },
+ err => {
+ port.postMessage({
+ resultFor: "setPopup",
+ type: "browserAction",
+ success: false,
+ error: String(err),
+ });
+ }
+ );
+ break;
+
+ case "setTitle":
+ browser.browserAction.setTitle({
+ tabId,
+ title: message.title,
+ });
+ break;
+
+ case "setIcon":
+ browser.browserAction.setIcon({
+ tabId,
+ imageData: message.imageData,
+ path: message.path,
+ });
+ break;
+
+ default:
+ throw new Error(`Browser Action does not support ${message.action}`);
+ }
+}
+
+function handleMessage(message, tabId) {
+ switch (message.type) {
+ case "ping":
+ port.postMessage({ method: "pong" });
+ return;
+
+ case "load":
+ browser.tabs.update(tabId, {
+ url: message.url,
+ });
+ return;
+
+ case "browserAction":
+ handleBrowserActionMessage(message, tabId);
+ return;
+
+ case "pageAction":
+ handlePageActionMessage(message, tabId);
+ return;
+
+ default:
+ throw new Error(`Unsupported message type ${message.type}`);
+ }
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/button/beasts-32-light.png b/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/button/beasts-32-light.png
new file mode 100644
index 0000000000..dbed714c56
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/button/beasts-32-light.png
Binary files differ
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/button/beasts-32.png b/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/button/beasts-32.png
new file mode 100644
index 0000000000..89863ccec7
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/button/beasts-32.png
Binary files differ
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/button/expected.png b/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/button/expected.png
new file mode 100644
index 0000000000..aea2c19784
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/button/expected.png
Binary files differ
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/button/geo-19.png b/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/button/geo-19.png
new file mode 100644
index 0000000000..90687de26d
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/button/geo-19.png
Binary files differ
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/button/geo-38.png b/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/button/geo-38.png
new file mode 100644
index 0000000000..90687de26d
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/button/geo-38.png
Binary files differ
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/button/icon.svg b/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/button/icon.svg
new file mode 100644
index 0000000000..dd1fae7d15
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/button/icon.svg
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg enable-background="new 0 0 500 500" height="500px" id="Layer_1" version="1.1" viewBox="0 0 500 500" width="500px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path clip-rule="evenodd" d="M131.889,150.061v63.597h-27.256 c-20.079,0-36.343,16.263-36.343,36.342v181.711c0,20.078,16.264,36.34,36.343,36.34h290.734c20.078,0,36.345-16.262,36.345-36.34 V250c0-20.079-16.267-36.342-36.345-36.342h-27.254v-63.597c0-65.232-52.882-118.111-118.112-118.111 S131.889,84.828,131.889,150.061z M177.317,213.658v-63.597c0-40.157,32.525-72.685,72.683-72.685 c40.158,0,72.685,32.528,72.685,72.685v63.597H177.317z M213.658,313.599c0-20.078,16.263-36.341,36.342-36.341 s36.341,16.263,36.341,36.341c0,12.812-6.634,24.079-16.625,30.529c0,0,3.55,21.446,7.542,46.699 c0,7.538-6.087,13.625-13.629,13.625h-27.258c-7.541,0-13.627-6.087-13.627-13.625l7.542-46.699 C220.294,337.678,213.658,326.41,213.658,313.599z" fill="#010101" fill-rule="evenodd"/></svg> \ No newline at end of file
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/content.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/content.js
new file mode 100644
index 0000000000..eaa2467df0
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/content.js
@@ -0,0 +1,4 @@
+const port = browser.runtime.connectNative("browser");
+port.onMessage.addListener(message => {
+ browser.runtime.sendMessage(message);
+});
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/manifest.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/manifest.json
new file mode 100644
index 0000000000..21ca7c7e07
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/manifest.json
@@ -0,0 +1,43 @@
+{
+ "manifest_version": 2,
+ "name": "actions",
+ "version": "1.0",
+ "description": "Defines Page and Browser actions",
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "actions@tests.mozilla.org"
+ }
+ },
+ "browser_action": {
+ "default_title": "Test action default",
+ "theme_icons": [
+ {
+ "light": "button/beasts-32-light.png",
+ "dark": "button/beasts-32.png",
+ "size": 32
+ }
+ ]
+ },
+ "page_action": {
+ "default_title": "Test action default",
+ "default_icon": {
+ "19": "button/geo-19.png",
+ "38": "button/geo-38.png"
+ }
+ },
+ "background": {
+ "scripts": ["background.js"]
+ },
+ "content_scripts": [
+ {
+ "matches": ["<all_urls>"],
+ "js": ["content.js"]
+ }
+ ],
+ "permissions": [
+ "tabs",
+ "geckoViewAddons",
+ "nativeMessaging",
+ "nativeMessagingFromContent"
+ ]
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/test-open-popup-browser-action.html b/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/test-open-popup-browser-action.html
new file mode 100644
index 0000000000..dc388b8a7f
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/test-open-popup-browser-action.html
@@ -0,0 +1,14 @@
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <script
+ type="text/javascript"
+ src="test-open-popup-browser-action.js"
+ ></script>
+ </head>
+ <body>
+ <body style="height: 100%">
+ <p>Hello, world!</p>
+ </body>
+ </body>
+</html>
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/test-open-popup-browser-action.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/test-open-popup-browser-action.js
new file mode 100644
index 0000000000..cde31235ac
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/test-open-popup-browser-action.js
@@ -0,0 +1,7 @@
+window.addEventListener("DOMContentLoaded", init);
+
+function init() {
+ document.body.addEventListener("click", event => {
+ browser.browserAction.openPopup();
+ });
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/test-open-popup-page-action.html b/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/test-open-popup-page-action.html
new file mode 100644
index 0000000000..3fe42d0b2e
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/test-open-popup-page-action.html
@@ -0,0 +1,14 @@
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <script
+ type="text/javascript"
+ src="test-open-popup-page-action.js"
+ ></script>
+ </head>
+ <body>
+ <body style="height: 100%">
+ <p>Hello, world!</p>
+ </body>
+ </body>
+</html>
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/test-open-popup-page-action.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/test-open-popup-page-action.js
new file mode 100644
index 0000000000..f16d96333f
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/test-open-popup-page-action.js
@@ -0,0 +1,7 @@
+window.addEventListener("DOMContentLoaded", init);
+
+function init() {
+ document.body.addEventListener("click", event => {
+ browser.pageAction.openPopup();
+ });
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/test-popup-messaging.html b/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/test-popup-messaging.html
new file mode 100644
index 0000000000..f0fff977d8
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/test-popup-messaging.html
@@ -0,0 +1,9 @@
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <script src="test-popup-messaging.js"></script>
+ </head>
+ <body>
+ <h1>HELLO</h1>
+ </body>
+</html>
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/test-popup-messaging.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/test-popup-messaging.js
new file mode 100644
index 0000000000..479f957564
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/test-popup-messaging.js
@@ -0,0 +1,24 @@
+browser.runtime.sendNativeMessage("badNativeApi", "errorerrorerror");
+
+async function runTest() {
+ const response = await browser.runtime.sendNativeMessage(
+ "browser",
+ "testPopupMessage"
+ );
+
+ browser.runtime.sendNativeMessage("browser", `response: ${response}`);
+
+ const port = browser.runtime.connectNative("browser");
+ port.onMessage.addListener(response => {
+ if (response.action === "disconnect") {
+ port.disconnect();
+ return;
+ }
+
+ port.postMessage(`response: ${response.message}`);
+ });
+
+ port.postMessage("testPopupPortMessage");
+}
+
+runTest();
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/test-popup.html b/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/test-popup.html
new file mode 100644
index 0000000000..dd98313e59
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/test-popup.html
@@ -0,0 +1,9 @@
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <script src="test-popup.js"></script>
+ </head>
+ <body>
+ <h1>HELLO</h1>
+ </body>
+</html>
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/test-popup.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/test-popup.js
new file mode 100644
index 0000000000..47271e744c
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/actions/test-popup.js
@@ -0,0 +1,3 @@
+window.addEventListener("DOMContentLoaded", () => {
+ window.close();
+});
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/borderify-missing-id.xpi b/mobile/android/geckoview/src/androidTest/assets/web_extensions/borderify-missing-id.xpi
new file mode 100644
index 0000000000..19ce0d7f0f
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/borderify-missing-id.xpi
Binary files differ
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/borderify-unsigned.xpi b/mobile/android/geckoview/src/androidTest/assets/web_extensions/borderify-unsigned.xpi
new file mode 100644
index 0000000000..fd395d13df
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/borderify-unsigned.xpi
Binary files differ
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/borderify-unsigned.zip b/mobile/android/geckoview/src/androidTest/assets/web_extensions/borderify-unsigned.zip
new file mode 100644
index 0000000000..fd395d13df
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/borderify-unsigned.zip
Binary files differ
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/borderify.xpi b/mobile/android/geckoview/src/androidTest/assets/web_extensions/borderify.xpi
new file mode 100644
index 0000000000..1ed97f1047
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/borderify.xpi
Binary files differ
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/borderify/borderify.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/borderify/borderify.js
new file mode 100644
index 0000000000..9c3728b381
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/borderify/borderify.js
@@ -0,0 +1 @@
+document.body.style.border = "5px solid red";
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/borderify/icons/border-48.png b/mobile/android/geckoview/src/androidTest/assets/web_extensions/borderify/icons/border-48.png
new file mode 100644
index 0000000000..90687de26d
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/borderify/icons/border-48.png
Binary files differ
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/borderify/icons/icon.svg b/mobile/android/geckoview/src/androidTest/assets/web_extensions/borderify/icons/icon.svg
new file mode 100644
index 0000000000..dd1fae7d15
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/borderify/icons/icon.svg
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg enable-background="new 0 0 500 500" height="500px" id="Layer_1" version="1.1" viewBox="0 0 500 500" width="500px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path clip-rule="evenodd" d="M131.889,150.061v63.597h-27.256 c-20.079,0-36.343,16.263-36.343,36.342v181.711c0,20.078,16.264,36.34,36.343,36.34h290.734c20.078,0,36.345-16.262,36.345-36.34 V250c0-20.079-16.267-36.342-36.345-36.342h-27.254v-63.597c0-65.232-52.882-118.111-118.112-118.111 S131.889,84.828,131.889,150.061z M177.317,213.658v-63.597c0-40.157,32.525-72.685,72.683-72.685 c40.158,0,72.685,32.528,72.685,72.685v63.597H177.317z M213.658,313.599c0-20.078,16.263-36.341,36.342-36.341 s36.341,16.263,36.341,36.341c0,12.812-6.634,24.079-16.625,30.529c0,0,3.55,21.446,7.542,46.699 c0,7.538-6.087,13.625-13.629,13.625h-27.258c-7.541,0-13.627-6.087-13.627-13.625l7.542-46.699 C220.294,337.678,213.658,326.41,213.658,313.599z" fill="#010101" fill-rule="evenodd"/></svg> \ No newline at end of file
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/borderify/manifest.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/borderify/manifest.json
new file mode 100644
index 0000000000..4e3daf6708
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/borderify/manifest.json
@@ -0,0 +1,23 @@
+{
+ "manifest_version": 2,
+ "name": "Borderify",
+ "version": "1.0",
+ "description": "Adds a red border to all webpages matching example.com.",
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "borderify@tests.mozilla.org"
+ }
+ },
+ "icons": {
+ "48": "icons/border-48.png"
+ },
+ "content_scripts": [
+ {
+ "matches": ["*://*.example.com/*"],
+ "js": ["borderify.js"]
+ }
+ ],
+ "options_ui": {
+ "page": "dummy.html"
+ }
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/browsing-data-built-in/background.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/browsing-data-built-in/background.js
new file mode 100644
index 0000000000..d0ae54b3dd
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/browsing-data-built-in/background.js
@@ -0,0 +1,44 @@
+const port = browser.runtime.connectNative("browser");
+
+async function apiCall(message) {
+ const { type, since, removalOptions, dataTypes } = message;
+ switch (type) {
+ case "clear-downloads":
+ await browser.browsingData.removeDownloads({ since });
+ break;
+ case "clear-form-data":
+ await browser.browsingData.removeFormData({ since });
+ break;
+ case "clear-history":
+ await browser.browsingData.removeHistory({ since });
+ break;
+ case "clear-passwords":
+ await browser.browsingData.removePasswords({ since });
+ break;
+ case "clear":
+ await browser.browsingData.remove(removalOptions, dataTypes);
+ break;
+ case "get-settings":
+ return browser.browsingData.settings();
+ }
+ return null;
+}
+
+port.onMessage.addListener(async message => {
+ const { uuid } = message;
+ try {
+ const result = await apiCall(message);
+ port.postMessage({
+ type: "response",
+ result,
+ uuid,
+ });
+ } catch (exception) {
+ const { message } = exception;
+ port.postMessage({
+ type: "error",
+ error: message,
+ uuid,
+ });
+ }
+});
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/browsing-data-built-in/manifest.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/browsing-data-built-in/manifest.json
new file mode 100644
index 0000000000..23df4d8338
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/browsing-data-built-in/manifest.json
@@ -0,0 +1,15 @@
+{
+ "manifest_version": 2,
+ "name": "BrowsingData",
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "browsing-data-settings@tests.mozilla.org"
+ }
+ },
+ "version": "1.0",
+ "description": "Tests the browsingData API",
+ "background": {
+ "scripts": ["background.js"]
+ },
+ "permissions": ["browsingData", "geckoViewAddons", "nativeMessaging"]
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/browsing-data/background.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/browsing-data/background.js
new file mode 100644
index 0000000000..4597e3328b
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/browsing-data/background.js
@@ -0,0 +1,8 @@
+browser.browsingData.removeDownloads({ since: 10001 });
+browser.browsingData.removeFormData({ since: 10002 });
+browser.browsingData.removeHistory({ since: 10003 });
+browser.browsingData.removePasswords({ since: 10004 });
+browser.browsingData.remove(
+ { since: 10005 },
+ { downloads: true, cookies: true }
+);
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/browsing-data/manifest.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/browsing-data/manifest.json
new file mode 100644
index 0000000000..f7af03c25e
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/browsing-data/manifest.json
@@ -0,0 +1,15 @@
+{
+ "manifest_version": 2,
+ "name": "BrowsingData",
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "browsing-data@tests.mozilla.org"
+ }
+ },
+ "version": "1.0",
+ "description": "Tests the browsingData API",
+ "background": {
+ "scripts": ["background.js"]
+ },
+ "permissions": ["browsingData"]
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/download-flags-false/download.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/download-flags-false/download.js
new file mode 100644
index 0000000000..68f51ea5d8
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/download-flags-false/download.js
@@ -0,0 +1,3 @@
+browser.downloads.download({
+ url: "http://localhost:4245/assets/www/images/test.gif",
+});
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/download-flags-false/manifest.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/download-flags-false/manifest.json
new file mode 100644
index 0000000000..77b1cb5179
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/download-flags-false/manifest.json
@@ -0,0 +1,15 @@
+{
+ "manifest_version": 2,
+ "name": "Download",
+ "version": "1.0",
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "download-flags-false@tests.mozilla.org"
+ }
+ },
+ "description": "Downloads a file",
+ "background": {
+ "scripts": ["download.js"]
+ },
+ "permissions": ["downloads"]
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/download-flags-true/download.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/download-flags-true/download.js
new file mode 100644
index 0000000000..4bb06a5cbb
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/download-flags-true/download.js
@@ -0,0 +1,16 @@
+browser.downloads.download({
+ url: "http://localhost:4245/assets/www/images/test.gif",
+ filename: "banana.gif",
+ method: "POST",
+ body: "postbody",
+ headers: [
+ {
+ name: "User-Agent",
+ value: "Mozilla Firefox",
+ },
+ ],
+ allowHttpErrors: true,
+ conflictAction: "overwrite",
+ saveAs: true,
+ incognito: true,
+});
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/download-flags-true/manifest.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/download-flags-true/manifest.json
new file mode 100644
index 0000000000..c0170dafd4
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/download-flags-true/manifest.json
@@ -0,0 +1,15 @@
+{
+ "manifest_version": 2,
+ "name": "Download",
+ "version": "1.0",
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "download-flags-true@tests.mozilla.org"
+ }
+ },
+ "description": "Downloads a file",
+ "background": {
+ "scripts": ["download.js"]
+ },
+ "permissions": ["downloads"]
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/download-onChanged/download.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/download-onChanged/download.js
new file mode 100644
index 0000000000..01cd377cef
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/download-onChanged/download.js
@@ -0,0 +1,18 @@
+async function test() {
+ browser.downloads.onChanged.addListener(async delta => {
+ const changes = { current: {}, previous: {} };
+ changes.id = delta.id;
+ delete delta.id;
+ for (const prop in delta) {
+ changes.current[prop] = delta[prop].current;
+ changes.previous[prop] = delta[prop].previous;
+ }
+ await browser.runtime.sendNativeMessage("browser", changes);
+ });
+
+ await browser.downloads.download({
+ url: "http://localhost:4245/assets/www/images/test.gif",
+ });
+}
+
+test();
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/download-onChanged/manifest.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/download-onChanged/manifest.json
new file mode 100644
index 0000000000..1c1ad4cc5e
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/download-onChanged/manifest.json
@@ -0,0 +1,15 @@
+{
+ "manifest_version": 2,
+ "name": "Download",
+ "version": "1.0",
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "download-onChanged@tests.mozilla.org"
+ }
+ },
+ "description": "Downloads a file",
+ "background": {
+ "scripts": ["download.js"]
+ },
+ "permissions": ["downloads", "geckoViewAddons", "nativeMessaging"]
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/dummy-incompatible.xpi b/mobile/android/geckoview/src/androidTest/assets/web_extensions/dummy-incompatible.xpi
new file mode 100644
index 0000000000..93c5dbd3b2
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/dummy-incompatible.xpi
Binary files differ
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/dummy.xpi b/mobile/android/geckoview/src/androidTest/assets/web_extensions/dummy.xpi
new file mode 100644
index 0000000000..0e0f549ceb
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/dummy.xpi
Binary files differ
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/dummy/dummy.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/dummy/dummy.js
new file mode 100644
index 0000000000..2a49c0d665
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/dummy/dummy.js
@@ -0,0 +1 @@
+console.log("Hi, I'm a dummy.");
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/dummy/manifest.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/dummy/manifest.json
new file mode 100644
index 0000000000..f1f9b93a91
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/dummy/manifest.json
@@ -0,0 +1,21 @@
+{
+ "manifest_version": 2,
+ "name": "Dummy",
+ "version": "1.0",
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "dummy@tests.mozilla.org"
+ }
+ },
+ "description": "Doesn't do anything.",
+ "options_ui": {
+ "open_in_tab": true,
+ "page": "options.html"
+ },
+ "content_scripts": [
+ {
+ "matches": ["*://*.example.com/*"],
+ "js": ["dummy.js"]
+ }
+ ]
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/extension-page-restore/manifest.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/extension-page-restore/manifest.json
new file mode 100644
index 0000000000..0fcb48bc8f
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/extension-page-restore/manifest.json
@@ -0,0 +1,11 @@
+{
+ "manifest_version": 2,
+ "name": "Test messages sent from extensions when restoring",
+ "version": "1.0",
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "extension-page-restoring@tests.mozilla.org"
+ }
+ },
+ "permissions": ["geckoViewAddons", "nativeMessaging"]
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/extension-page-restore/tab-script.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/extension-page-restore/tab-script.js
new file mode 100644
index 0000000000..66866bbd37
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/extension-page-restore/tab-script.js
@@ -0,0 +1,5 @@
+browser.runtime.sendNativeMessage("browser1", "HELLO_FROM_PAGE_1");
+browser.runtime.sendNativeMessage("browser2", "HELLO_FROM_PAGE_2");
+
+const port = browser.runtime.connectNative("browser1");
+port.postMessage("HELLO_FROM_PORT");
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/extension-page-restore/tab.html b/mobile/android/geckoview/src/androidTest/assets/web_extensions/extension-page-restore/tab.html
new file mode 100644
index 0000000000..d99a610c0c
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/extension-page-restore/tab.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ </head>
+ <body>
+ <h1>Hello World!</h1>
+ <script src="tab-script.js"></script>
+ </body>
+</html>
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/extension-page-update/background-script.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/extension-page-update/background-script.js
new file mode 100644
index 0000000000..43e2b44f96
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/extension-page-update/background-script.js
@@ -0,0 +1,7 @@
+browser.runtime.onMessage.addListener(notify);
+
+function notify(message) {
+ if (message.action == "showTab") {
+ browser.tabs.update({ url: "/tab.html" });
+ }
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/extension-page-update/manifest.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/extension-page-update/manifest.json
new file mode 100644
index 0000000000..c64115e07c
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/extension-page-update/manifest.json
@@ -0,0 +1,21 @@
+{
+ "manifest_version": 2,
+ "name": "Mozilla Android Components - Tabs Update Test",
+ "version": "1.0",
+ "background": {
+ "scripts": ["background-script.js"]
+ },
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "extension-page-update@tests.mozilla.org"
+ }
+ },
+ "content_scripts": [
+ {
+ "matches": ["*://*.example.com/*"],
+ "js": ["tabs.js"],
+ "run_at": "document_idle"
+ }
+ ],
+ "permissions": ["geckoViewAddons", "nativeMessaging", "tabs", "<all_urls>"]
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/extension-page-update/tab-script.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/extension-page-update/tab-script.js
new file mode 100644
index 0000000000..011f3bb301
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/extension-page-update/tab-script.js
@@ -0,0 +1,2 @@
+// Let's test privileged APIs
+browser.runtime.sendNativeMessage("browser", "HELLO_FROM_PAGE");
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/extension-page-update/tab.html b/mobile/android/geckoview/src/androidTest/assets/web_extensions/extension-page-update/tab.html
new file mode 100644
index 0000000000..d99a610c0c
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/extension-page-update/tab.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ </head>
+ <body>
+ <h1>Hello World!</h1>
+ <script src="tab-script.js"></script>
+ </body>
+</html>
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/extension-page-update/tabs.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/extension-page-update/tabs.js
new file mode 100644
index 0000000000..ef5fbf6ce3
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/extension-page-update/tabs.js
@@ -0,0 +1 @@
+browser.runtime.sendMessage({ action: "showTab" });
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/langpack_signed.xpi b/mobile/android/geckoview/src/androidTest/assets/web_extensions/langpack_signed.xpi
new file mode 100644
index 0000000000..f60d00348e
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/langpack_signed.xpi
Binary files differ
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/messaging-content/manifest.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/messaging-content/manifest.json
new file mode 100644
index 0000000000..9a687dafbe
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/messaging-content/manifest.json
@@ -0,0 +1,22 @@
+{
+ "manifest_version": 2,
+ "name": "messaging",
+ "version": "1.0",
+ "description": "Test messaging between app and web extension",
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "messaging-content@tests.mozilla.org"
+ }
+ },
+ "content_scripts": [
+ {
+ "matches": ["*://*.example.com/*"],
+ "js": ["messaging.js"]
+ }
+ ],
+ "permissions": [
+ "geckoViewAddons",
+ "nativeMessaging",
+ "nativeMessagingFromContent"
+ ]
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/messaging-content/messaging.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/messaging-content/messaging.js
new file mode 100644
index 0000000000..1c8323df53
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/messaging-content/messaging.js
@@ -0,0 +1,29 @@
+// This message should not be handled
+browser.runtime.sendNativeMessage("badNativeApi", "errorerrorerror");
+
+async function runTest() {
+ const response = await browser.runtime.sendNativeMessage(
+ "browser",
+ "testContentBrowserMessage"
+ );
+
+ browser.runtime.sendNativeMessage("browser", `response: ${response}`);
+
+ const port = browser.runtime.connectNative("browser");
+ port.onMessage.addListener(response => {
+ if (response.action === "disconnect") {
+ port.disconnect();
+ return;
+ }
+
+ port.postMessage(`response: ${response.message}`);
+ });
+
+ port.onDisconnect.addListener(() =>
+ browser.runtime.sendNativeMessage("browser", { type: "portDisconnected" })
+ );
+
+ port.postMessage("testContentPortMessage");
+}
+
+runTest();
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/messaging-iframe/manifest.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/messaging-iframe/manifest.json
new file mode 100644
index 0000000000..f9039fd2e8
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/messaging-iframe/manifest.json
@@ -0,0 +1,23 @@
+{
+ "manifest_version": 2,
+ "name": "messaging",
+ "version": "1.0",
+ "description": "Test messaging between app and web extension",
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "messaging-iframe@tests.mozilla.org"
+ }
+ },
+ "content_scripts": [
+ {
+ "matches": ["*://localhost/*"],
+ "js": ["messaging.js"],
+ "all_frames": true
+ }
+ ],
+ "permissions": [
+ "geckoViewAddons",
+ "nativeMessaging",
+ "nativeMessagingFromContent"
+ ]
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/messaging-iframe/messaging.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/messaging-iframe/messaging.js
new file mode 100644
index 0000000000..eb4ad3d8f9
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/messaging-iframe/messaging.js
@@ -0,0 +1,11 @@
+browser.runtime.sendNativeMessage("badNativeApi", "errorerrorerror");
+
+async function runTest() {
+ await browser.runtime.sendNativeMessage(
+ "browser",
+ "testContentBrowserMessage"
+ );
+ browser.runtime.connectNative("browser");
+}
+
+runTest();
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/messaging/background.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/messaging/background.js
new file mode 100644
index 0000000000..20deb53ae7
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/messaging/background.js
@@ -0,0 +1,28 @@
+browser.runtime.sendNativeMessage("badNativeApi", "errorerrorerror");
+
+async function runTest() {
+ const response = await browser.runtime.sendNativeMessage(
+ "browser",
+ "testBackgroundBrowserMessage"
+ );
+
+ browser.runtime.sendNativeMessage("browser", `response: ${response}`);
+
+ const port = browser.runtime.connectNative("browser");
+ port.onMessage.addListener(response => {
+ if (response.action === "disconnect") {
+ port.disconnect();
+ return;
+ }
+
+ port.postMessage(`response: ${response.message}`);
+ });
+
+ port.onDisconnect.addListener(() =>
+ browser.runtime.sendNativeMessage("browser", { type: "portDisconnected" })
+ );
+
+ port.postMessage("testBackgroundPortMessage");
+}
+
+runTest();
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/messaging/icons/border-48.png b/mobile/android/geckoview/src/androidTest/assets/web_extensions/messaging/icons/border-48.png
new file mode 100644
index 0000000000..90687de26d
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/messaging/icons/border-48.png
Binary files differ
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/messaging/manifest.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/messaging/manifest.json
new file mode 100644
index 0000000000..d25b692f63
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/messaging/manifest.json
@@ -0,0 +1,18 @@
+{
+ "manifest_version": 2,
+ "name": "messaging",
+ "version": "1.0",
+ "description": "Test messaging between app and web extension",
+ "icons": {
+ "48": "icons/border-48.png"
+ },
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "messaging@tests.mozilla.org"
+ }
+ },
+ "background": {
+ "scripts": ["background.js"]
+ },
+ "permissions": ["geckoViewAddons", "nativeMessaging"]
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/notification-test/background.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/notification-test/background.js
new file mode 100644
index 0000000000..cdd3a7a523
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/notification-test/background.js
@@ -0,0 +1,6 @@
+browser.notifications.create("cake-notification", {
+ type: "basic",
+ title: "Time for cake!",
+ iconUrl: "https://example.com/img.svg",
+ message: "Something something cake",
+});
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/notification-test/manifest.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/notification-test/manifest.json
new file mode 100644
index 0000000000..963fb51e3f
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/notification-test/manifest.json
@@ -0,0 +1,15 @@
+{
+ "manifest_version": 2,
+ "name": "Notification test",
+ "version": "1.0",
+ "description": "Send a notification.",
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "notification@example.com"
+ }
+ },
+ "background": {
+ "scripts": ["background.js"]
+ },
+ "permissions": ["notifications"]
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/openoptionspage-1/background.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/openoptionspage-1/background.js
new file mode 100644
index 0000000000..1872c48d00
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/openoptionspage-1/background.js
@@ -0,0 +1 @@
+browser.runtime.openOptionsPage();
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/openoptionspage-1/manifest.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/openoptionspage-1/manifest.json
new file mode 100644
index 0000000000..487fb0fb3d
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/openoptionspage-1/manifest.json
@@ -0,0 +1,20 @@
+{
+ "manifest_version": 2,
+ "name": "openOptionsPage-1",
+ "version": "1.0",
+ "description": "Opens options page in a new tab.",
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "openoptionspage1@tests.mozilla.org"
+ }
+ },
+ "background": {
+ "scripts": ["background.js"]
+ },
+ "permissions": ["tabs"],
+ "options_ui": {
+ "page": "options.html",
+ "browser_style": true,
+ "open_in_tab": true
+ }
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/openoptionspage-2/background.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/openoptionspage-2/background.js
new file mode 100644
index 0000000000..1872c48d00
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/openoptionspage-2/background.js
@@ -0,0 +1 @@
+browser.runtime.openOptionsPage();
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/openoptionspage-2/manifest.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/openoptionspage-2/manifest.json
new file mode 100644
index 0000000000..3378050197
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/openoptionspage-2/manifest.json
@@ -0,0 +1,20 @@
+{
+ "manifest_version": 2,
+ "name": "openOptionsPage-2",
+ "version": "1.0",
+ "description": "Opens options page via delegate.",
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "openoptionspage2@tests.mozilla.org"
+ }
+ },
+ "background": {
+ "scripts": ["background.js"]
+ },
+ "permissions": ["tabs"],
+ "options_ui": {
+ "page": "options.html",
+ "browser_style": true,
+ "open_in_tab": false
+ }
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/page-history/manifest.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/page-history/manifest.json
new file mode 100644
index 0000000000..9d411c8dd6
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/page-history/manifest.json
@@ -0,0 +1,11 @@
+{
+ "manifest_version": 2,
+ "name": "Page History",
+ "version": "1.0",
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "page-history@tests.mozilla.org"
+ }
+ },
+ "description": "Can load a WebExtension Page."
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/page-history/page.html b/mobile/android/geckoview/src/androidTest/assets/web_extensions/page-history/page.html
new file mode 100644
index 0000000000..b16a98f74b
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/page-history/page.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ </head>
+ <body>
+ <h1>Hello, World!</h1>
+ </body>
+</html>
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/permission-request/clickToRequestPermission.html b/mobile/android/geckoview/src/androidTest/assets/web_extensions/permission-request/clickToRequestPermission.html
new file mode 100644
index 0000000000..e6ddcb8c8d
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/permission-request/clickToRequestPermission.html
@@ -0,0 +1,11 @@
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>Hello, world!</title>
+ <meta name="viewport" content="initial-scale=1.0" />
+ <script type="text/javascript" src="request-permission.js"></script>
+ </head>
+ <body style="height: 100%">
+ <p>Hello, world!</p>
+ </body>
+</html>
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/permission-request/manifest.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/permission-request/manifest.json
new file mode 100644
index 0000000000..d2cd405cd1
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/permission-request/manifest.json
@@ -0,0 +1,13 @@
+{
+ "manifest_version": 2,
+ "name": "permissions",
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "permissions@example.com"
+ }
+ },
+ "version": "1.0",
+ "description": "Request optional extension permissions.",
+ "permissions": ["nativeMessaging", "geckoViewAddons"],
+ "optional_permissions": ["geolocation", "*://example.com/*"]
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/permission-request/request-permission.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/permission-request/request-permission.js
new file mode 100644
index 0000000000..d50bff4126
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/permission-request/request-permission.js
@@ -0,0 +1,11 @@
+window.onload = () => {
+ document.body.addEventListener("click", requestPermissions);
+ async function requestPermissions() {
+ const perms = {
+ permissions: ["geolocation"],
+ origins: ["*://example.com/*"],
+ };
+ const response = await browser.permissions.request(perms);
+ browser.runtime.sendNativeMessage("browser", `${response}`);
+ }
+};
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/redirect-to-android-resource/background.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/redirect-to-android-resource/background.js
new file mode 100644
index 0000000000..fdf088a505
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/redirect-to-android-resource/background.js
@@ -0,0 +1,39 @@
+"use strict";
+
+function setupRedirect(fromUrl, redirectUrl) {
+ browser.webRequest.onBeforeRequest.addListener(
+ details => {
+ console.log(`Extension redirects from ${fromUrl} to ${redirectUrl}`);
+ return { redirectUrl };
+ },
+ { urls: [fromUrl] },
+ ["blocking"]
+ );
+}
+
+// Intercepts all script requests from androidTest/assets/www/trackers.html.
+// Scripts are executed in order of appearance in the page and block the
+// page's "load" event, so the test runner can just wait for the page to
+// have loaded and then check the page content to verify that the requests
+// were intercepted as expected.
+setupRedirect(
+ "http://trackertest.org/tracker.js",
+ "data:text/javascript,document.body.textContent='start'"
+);
+setupRedirect(
+ "https://tracking.example.com/tracker.js",
+ browser.runtime.getURL("web-accessible-script.js")
+);
+setupRedirect(
+ "https://itisatracker.org/tracker.js",
+ `data:text/javascript,document.body.textContent+=',end'`
+);
+
+// Work around bug 1300234 to ensure that the webRequest listener has been
+// registered before we resume the test. API result doesn't matter, we just
+// want to make a roundtrip.
+var listenerReady = browser.webRequest.getSecurityInfo("").catch(() => {});
+
+listenerReady.then(() => {
+ browser.runtime.sendNativeMessage("browser", "setupReadyStartTest");
+});
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/redirect-to-android-resource/manifest.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/redirect-to-android-resource/manifest.json
new file mode 100644
index 0000000000..71d811faa3
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/redirect-to-android-resource/manifest.json
@@ -0,0 +1,25 @@
+{
+ "name": "redirect-to-android-resource",
+ "description": "Redirects script requests from trackers.html to moz-extension:-resource packaged in the APK (resource://android/...)",
+ "manifest_version": 2,
+ "version": "1",
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "redirect-to-android-resource@tests.mozilla.org"
+ }
+ },
+ "background": {
+ "scripts": ["background.js"]
+ },
+ "permissions": [
+ "geckoViewAddons",
+ "nativeMessaging",
+ "webRequest",
+ "webRequestBlocking",
+ "http://localhost/",
+ "http://trackertest.org/",
+ "https://tracking.example.com/",
+ "https://itisatracker.org/tracker.js"
+ ],
+ "web_accessible_resources": ["web-accessible-script.js"]
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/redirect-to-android-resource/web-accessible-script.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/redirect-to-android-resource/web-accessible-script.js
new file mode 100644
index 0000000000..a26c4cc91c
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/redirect-to-android-resource/web-accessible-script.js
@@ -0,0 +1,3 @@
+"use strict";
+
+document.body.textContent += ",extension-was-here";
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-activate-remove-2/background.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-activate-remove-2/background.js
new file mode 100644
index 0000000000..f8ecef0215
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-activate-remove-2/background.js
@@ -0,0 +1,16 @@
+browser.tabs.onActivated.addListener(async tabChange => {
+ const activeTabs = await browser.tabs.query({ active: true });
+ const currentWindow = await browser.tabs.query({
+ currentWindow: true,
+ active: true,
+ });
+
+ if (
+ activeTabs.length === 1 &&
+ activeTabs[0].id == tabChange.tabId &&
+ currentWindow.length === 1 &&
+ currentWindow[0].id === tabChange.tabId
+ ) {
+ browser.tabs.remove(tabChange.tabId);
+ }
+});
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-activate-remove-2/manifest.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-activate-remove-2/manifest.json
new file mode 100644
index 0000000000..784215634d
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-activate-remove-2/manifest.json
@@ -0,0 +1,15 @@
+{
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "set-tab-active-2@tests.mozilla.org"
+ }
+ },
+ "manifest_version": 2,
+ "name": "messaging",
+ "version": "1.0",
+ "description": "Removes the activated Tab.",
+ "background": {
+ "scripts": ["background.js"]
+ },
+ "permissions": ["tabs"]
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-activate-remove/background.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-activate-remove/background.js
new file mode 100644
index 0000000000..f8ecef0215
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-activate-remove/background.js
@@ -0,0 +1,16 @@
+browser.tabs.onActivated.addListener(async tabChange => {
+ const activeTabs = await browser.tabs.query({ active: true });
+ const currentWindow = await browser.tabs.query({
+ currentWindow: true,
+ active: true,
+ });
+
+ if (
+ activeTabs.length === 1 &&
+ activeTabs[0].id == tabChange.tabId &&
+ currentWindow.length === 1 &&
+ currentWindow[0].id === tabChange.tabId
+ ) {
+ browser.tabs.remove(tabChange.tabId);
+ }
+});
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-activate-remove/manifest.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-activate-remove/manifest.json
new file mode 100644
index 0000000000..03c3514bb0
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-activate-remove/manifest.json
@@ -0,0 +1,15 @@
+{
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "set-tab-active@tests.mozilla.org"
+ }
+ },
+ "manifest_version": 2,
+ "name": "messaging",
+ "version": "1.0",
+ "description": "Removes the activated Tab.",
+ "background": {
+ "scripts": ["background.js"]
+ },
+ "permissions": ["tabs"]
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-create-2/background.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-create-2/background.js
new file mode 100644
index 0000000000..8182b6a4f8
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-create-2/background.js
@@ -0,0 +1,4 @@
+browser.tabs.create({
+ url: "https://www.mozilla.org/en-US/",
+ cookieStoreId: "firefox-container-1",
+});
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-create-2/manifest.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-create-2/manifest.json
new file mode 100644
index 0000000000..2746155adf
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-create-2/manifest.json
@@ -0,0 +1,15 @@
+{
+ "manifest_version": 2,
+ "name": "messaging",
+ "version": "1.0",
+ "description": "Creates a tab with a contextual identity.",
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "tabs-create-2@tests.mozilla.org"
+ }
+ },
+ "background": {
+ "scripts": ["background.js"]
+ },
+ "permissions": ["tabs", "cookies"]
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-create-remove/background.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-create-remove/background.js
new file mode 100644
index 0000000000..a1f55a3a4f
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-create-remove/background.js
@@ -0,0 +1,3 @@
+browser.tabs.create({}).then(tab => {
+ browser.tabs.remove(tab.id);
+});
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-create-remove/manifest.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-create-remove/manifest.json
new file mode 100644
index 0000000000..10b2f454e7
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-create-remove/manifest.json
@@ -0,0 +1,15 @@
+{
+ "manifest_version": 2,
+ "name": "messaging",
+ "version": "1.0",
+ "description": "Creates and removes a tab.",
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "tabs-create-remove@tests.mozilla.org"
+ }
+ },
+ "background": {
+ "scripts": ["background.js"]
+ },
+ "permissions": []
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-create/background.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-create/background.js
new file mode 100644
index 0000000000..6fbd381e61
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-create/background.js
@@ -0,0 +1 @@
+browser.tabs.create({ url: "https://www.mozilla.org/en-US/" });
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-create/manifest.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-create/manifest.json
new file mode 100644
index 0000000000..517ddd0189
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-create/manifest.json
@@ -0,0 +1,15 @@
+{
+ "manifest_version": 2,
+ "name": "messaging",
+ "version": "1.0",
+ "description": "Creates a tab.",
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "tabs-create@tests.mozilla.org"
+ }
+ },
+ "background": {
+ "scripts": ["background.js"]
+ },
+ "permissions": ["tabs"]
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-remove/background.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-remove/background.js
new file mode 100644
index 0000000000..c6ec7aee33
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-remove/background.js
@@ -0,0 +1,3 @@
+browser.tabs.query({ url: "*://*/*?tabToClose" }).then(([tab]) => {
+ browser.tabs.remove(tab.id);
+});
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-remove/manifest.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-remove/manifest.json
new file mode 100644
index 0000000000..559512eec5
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/tabs-remove/manifest.json
@@ -0,0 +1,15 @@
+{
+ "manifest_version": 2,
+ "name": "messaging",
+ "version": "1.0",
+ "description": "Removes an existing tab.",
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "tabs-remove@tests.mozilla.org"
+ }
+ },
+ "background": {
+ "scripts": ["background.js"]
+ },
+ "permissions": ["tabs"]
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/TestSupportChild.sys.mjs b/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/TestSupportChild.sys.mjs
new file mode 100644
index 0000000000..6dd3e8eed4
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/TestSupportChild.sys.mjs
@@ -0,0 +1,83 @@
+/* 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/. */
+
+import { GeckoViewActorChild } from "resource://gre/modules/GeckoViewActorChild.sys.mjs";
+
+export class TestSupportChild extends GeckoViewActorChild {
+ receiveMessage(aMsg) {
+ debug`receiveMessage: ${aMsg.name}`;
+
+ switch (aMsg.name) {
+ case "FlushApzRepaints":
+ return new Promise(resolve => {
+ const repaintDone = () => {
+ debug`APZ flush done`;
+ Services.obs.removeObserver(repaintDone, "apz-repaints-flushed");
+ resolve();
+ };
+ Services.obs.addObserver(repaintDone, "apz-repaints-flushed");
+ if (this.contentWindow.windowUtils.flushApzRepaints()) {
+ debug`Flushed APZ repaints, waiting for callback...`;
+ } else {
+ debug`Flushing APZ repaints was a no-op, triggering callback directly...`;
+ repaintDone();
+ }
+ });
+ case "PromiseAllPaintsDone":
+ return new Promise(resolve => {
+ const window = this.contentWindow;
+ const utils = window.windowUtils;
+
+ function waitForPaints() {
+ // Wait until paint suppression has ended
+ if (utils.paintingSuppressed) {
+ dump`waiting for paint suppression to end...`;
+ window.setTimeout(waitForPaints, 0);
+ return;
+ }
+
+ if (utils.isMozAfterPaintPending) {
+ dump`waiting for paint...`;
+ window.addEventListener("MozAfterPaint", waitForPaints, {
+ once: true,
+ });
+ return;
+ }
+ resolve();
+ }
+ waitForPaints();
+ });
+ case "GetLinkColor": {
+ const { selector } = aMsg.data;
+ const element = this.document.querySelector(selector);
+ if (!element) {
+ throw new Error("No element for " + selector);
+ }
+ const color =
+ this.contentWindow.windowUtils.getVisitedDependentComputedStyle(
+ element,
+ "",
+ "color"
+ );
+ return color;
+ }
+ case "SetResolutionAndScaleTo": {
+ return new Promise(resolve => {
+ const window = this.contentWindow;
+ const { resolution } = aMsg.data;
+ window.visualViewport.addEventListener("resize", () => resolve(), {
+ once: true,
+ });
+ window.windowUtils.setResolutionAndScaleTo(resolution);
+ });
+ }
+ case "WaitForContentTransformsReceived": {
+ return this.contentWindow.docShell.browserChild.contentTransformsReceived();
+ }
+ }
+ return null;
+ }
+}
+
+const { debug } = TestSupportChild.initLogging("GeckoViewTestSupport");
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/TestSupportProcessChild.sys.mjs b/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/TestSupportProcessChild.sys.mjs
new file mode 100644
index 0000000000..0684ef0967
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/TestSupportProcessChild.sys.mjs
@@ -0,0 +1,22 @@
+/* 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/. */
+
+import { GeckoViewUtils } from "resource://gre/modules/GeckoViewUtils.sys.mjs";
+
+const ProcessTools = Cc["@mozilla.org/processtools-service;1"].getService(
+ Ci.nsIProcessToolsService
+);
+
+export class TestSupportProcessChild extends JSProcessActorChild {
+ receiveMessage(aMsg) {
+ debug`receiveMessage: ${aMsg.name}`;
+
+ switch (aMsg.name) {
+ case "KillContentProcess":
+ ProcessTools.kill(Services.appinfo.processID);
+ }
+ }
+}
+
+const { debug } = GeckoViewUtils.initLogging("TestSupportProcess[C]");
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/background.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/background.js
new file mode 100644
index 0000000000..181764859a
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/background.js
@@ -0,0 +1,127 @@
+/* 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/. */
+
+const port = browser.runtime.connectNative("browser");
+
+const APIS = {
+ AddHistogram({ id, value }) {
+ browser.test.addHistogram(id, value);
+ },
+ Eval({ code }) {
+ // eslint-disable-next-line no-eval
+ return eval(`(async () => {
+ ${code}
+ })()`);
+ },
+ SetScalar({ id, value }) {
+ browser.test.setScalar(id, value);
+ },
+ GetRequestedLocales() {
+ return browser.test.getRequestedLocales();
+ },
+ GetLinkColor({ tab, selector }) {
+ return browser.test.getLinkColor(tab.id, selector);
+ },
+ GetPidForTab({ tab }) {
+ return browser.test.getPidForTab(tab.id);
+ },
+ WaitForContentTransformsReceived({ tab }) {
+ return browser.test.waitForContentTransformsReceived(tab.id);
+ },
+ GetProfilePath() {
+ return browser.test.getProfilePath();
+ },
+ GetAllBrowserPids() {
+ return browser.test.getAllBrowserPids();
+ },
+ KillContentProcess({ pid }) {
+ return browser.test.killContentProcess(pid);
+ },
+ GetPrefs({ prefs }) {
+ return browser.test.getPrefs(prefs);
+ },
+ GetActive({ tab }) {
+ return browser.test.getActive(tab.id);
+ },
+ RemoveAllCertOverrides() {
+ browser.test.removeAllCertOverrides();
+ },
+ RestorePrefs({ oldPrefs }) {
+ return browser.test.restorePrefs(oldPrefs);
+ },
+ SetPrefs({ oldPrefs, newPrefs }) {
+ return browser.test.setPrefs(oldPrefs, newPrefs);
+ },
+ SetResolutionAndScaleTo({ tab, resolution }) {
+ return browser.test.setResolutionAndScaleTo(tab.id, resolution);
+ },
+ FlushApzRepaints({ tab }) {
+ return browser.test.flushApzRepaints(tab.id);
+ },
+ PromiseAllPaintsDone({ tab }) {
+ return browser.test.promiseAllPaintsDone(tab.id);
+ },
+ UsingGpuProcess() {
+ return browser.test.usingGpuProcess();
+ },
+ KillGpuProcess() {
+ return browser.test.killGpuProcess();
+ },
+ CrashGpuProcess() {
+ return browser.test.crashGpuProcess();
+ },
+ ClearHSTSState() {
+ return browser.test.clearHSTSState();
+ },
+ TriggerCookieBannerDetected({ tab }) {
+ return browser.test.triggerCookieBannerDetected(tab.id);
+ },
+ TriggerCookieBannerHandled({ tab }) {
+ return browser.test.triggerCookieBannerHandled(tab.id);
+ },
+ TriggerTranslationsOffer({ tab }) {
+ return browser.test.triggerTranslationsOffer(tab.id);
+ },
+ TriggerLanguageStateChange({ tab, languageState }) {
+ return browser.test.triggerLanguageStateChange(tab.id, languageState);
+ },
+};
+
+port.onMessage.addListener(async message => {
+ const impl = APIS[message.type];
+ apiCall(message, impl);
+});
+
+browser.runtime.onConnect.addListener(contentPort => {
+ contentPort.onMessage.addListener(message => {
+ message.args.tab = contentPort.sender.tab;
+
+ const impl = APIS[message.type];
+ apiCall(message, impl);
+ });
+});
+
+function apiCall(message, impl) {
+ const { id, args } = message;
+ try {
+ sendResponse(id, impl(args));
+ } catch (error) {
+ sendResponse(id, Promise.reject(error));
+ }
+}
+
+function sendResponse(id, response) {
+ Promise.resolve(response).then(
+ value => sendSyncResponse(id, value),
+ reason => sendSyncResponse(id, null, reason)
+ );
+}
+
+function sendSyncResponse(id, response, exception) {
+ port.postMessage({
+ id,
+ response: JSON.stringify(response),
+ exception: exception && exception.toString(),
+ });
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/manifest.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/manifest.json
new file mode 100644
index 0000000000..fea5add0de
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/manifest.json
@@ -0,0 +1,42 @@
+{
+ "manifest_version": 2,
+ "name": "Test support",
+ "version": "1.0",
+ "description": "Helper script for GeckoView tests",
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "test-support@tests.mozilla.org"
+ }
+ },
+ "content_scripts": [
+ {
+ "matches": ["<all_urls>"],
+ "match_about_blank": true,
+ "js": ["test-support.js"],
+ "run_at": "document_start"
+ }
+ ],
+ "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self';",
+ "background": {
+ "scripts": ["background.js"]
+ },
+ "experiment_apis": {
+ "test": {
+ "schema": "test-schema.json",
+ "parent": {
+ "scopes": ["addon_parent"],
+ "script": "test-api.js",
+ "events": ["startup"],
+ "paths": [["test"]]
+ }
+ }
+ },
+ "options_ui": {
+ "page": "dummy.html"
+ },
+ "permissions": [
+ "geckoViewAddons",
+ "nativeMessaging",
+ "nativeMessagingFromContent"
+ ]
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-api.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-api.js
new file mode 100644
index 0000000000..1868d25c84
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-api.js
@@ -0,0 +1,256 @@
+/* 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";
+
+/* globals Services */
+
+const { E10SUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/E10SUtils.sys.mjs"
+);
+const { Preferences } = ChromeUtils.importESModule(
+ "resource://gre/modules/Preferences.sys.mjs"
+);
+
+// eslint-disable-next-line mozilla/reject-importGlobalProperties
+Cu.importGlobalProperties(["PathUtils"]);
+
+this.test = class extends ExtensionAPI {
+ onStartup() {
+ ChromeUtils.registerWindowActor("TestSupport", {
+ child: {
+ esModuleURI:
+ "resource://android/assets/web_extensions/test-support/TestSupportChild.sys.mjs",
+ },
+ allFrames: true,
+ });
+ ChromeUtils.registerProcessActor("TestSupportProcess", {
+ child: {
+ esModuleURI:
+ "resource://android/assets/web_extensions/test-support/TestSupportProcessChild.sys.mjs",
+ },
+ });
+ }
+
+ onShutdown(isAppShutdown) {
+ if (isAppShutdown) {
+ return;
+ }
+ ChromeUtils.unregisterWindowActor("TestSupport");
+ ChromeUtils.unregisterProcessActor("TestSupportProcess");
+ }
+
+ getAPI(context) {
+ /**
+ * Helper function for getting window or process actors.
+ *
+ * @param tabId - id of the tab; required
+ * @param actorName - a string; the name of the actor
+ * Default: "TestSupport" which is our test framework actor
+ * (you can still pass the second parameter when getting the TestSupport actor, for readability)
+ *
+ * @returns actor
+ */
+ function getActorForTab(tabId, actorName = "TestSupport") {
+ const tab = context.extension.tabManager.get(tabId);
+ const { browsingContext } = tab.browser;
+ return browsingContext.currentWindowGlobal.getActor(actorName);
+ }
+
+ return {
+ test: {
+ /* Set prefs and returns set of saved prefs */
+ async setPrefs(oldPrefs, newPrefs) {
+ // Save old prefs
+ Object.assign(
+ oldPrefs,
+ ...Object.keys(newPrefs)
+ .filter(key => !(key in oldPrefs))
+ .map(key => ({ [key]: Preferences.get(key, null) }))
+ );
+
+ // Set new prefs
+ Preferences.set(newPrefs);
+ return oldPrefs;
+ },
+
+ /* Restore prefs to old value. */
+ async restorePrefs(oldPrefs) {
+ for (const [name, value] of Object.entries(oldPrefs)) {
+ if (value === null) {
+ Preferences.reset(name);
+ } else {
+ Preferences.set(name, value);
+ }
+ }
+ },
+
+ /* Get pref values. */
+ async getPrefs(prefs) {
+ return Preferences.get(prefs);
+ },
+
+ /* Gets link color for a given selector. */
+ async getLinkColor(tabId, selector) {
+ return getActorForTab(tabId, "TestSupport").sendQuery(
+ "GetLinkColor",
+ { selector }
+ );
+ },
+
+ async getRequestedLocales() {
+ return Services.locale.requestedLocales;
+ },
+
+ async getPidForTab(tabId) {
+ const tab = context.extension.tabManager.get(tabId);
+ const pids = E10SUtils.getBrowserPids(tab.browser);
+ return pids[0];
+ },
+
+ async waitForContentTransformsReceived(tabId) {
+ return getActorForTab(tabId).sendQuery(
+ "WaitForContentTransformsReceived"
+ );
+ },
+
+ async getAllBrowserPids() {
+ const pids = [];
+ const processes = ChromeUtils.getAllDOMProcesses();
+ for (const process of processes) {
+ if (process.remoteType && process.remoteType.startsWith("web")) {
+ pids.push(process.osPid);
+ }
+ }
+ return pids;
+ },
+
+ async killContentProcess(pid) {
+ const procs = ChromeUtils.getAllDOMProcesses();
+ for (const proc of procs) {
+ if (pid === proc.osPid) {
+ proc
+ .getActor("TestSupportProcess")
+ .sendAsyncMessage("KillContentProcess");
+ }
+ }
+ },
+
+ async addHistogram(id, value) {
+ return Services.telemetry.getHistogramById(id).add(value);
+ },
+
+ removeAllCertOverrides() {
+ const overrideService = Cc[
+ "@mozilla.org/security/certoverride;1"
+ ].getService(Ci.nsICertOverrideService);
+ overrideService.clearAllOverrides();
+ },
+
+ async setScalar(id, value) {
+ return Services.telemetry.scalarSet(id, value);
+ },
+
+ async setResolutionAndScaleTo(tabId, resolution) {
+ return getActorForTab(tabId, "TestSupport").sendQuery(
+ "SetResolutionAndScaleTo",
+ {
+ resolution,
+ }
+ );
+ },
+
+ async getActive(tabId) {
+ const tab = context.extension.tabManager.get(tabId);
+ return tab.browser.docShellIsActive;
+ },
+
+ async getProfilePath() {
+ return PathUtils.profileDir;
+ },
+
+ async flushApzRepaints(tabId) {
+ // TODO: Note that `waitUntilApzStable` in apz_test_utils.js does
+ // flush APZ repaints in the parent process (i.e. calling
+ // nsIDOMWindowUtils.flushApzRepaints for the parent process) before
+ // flushApzRepaints is called for the target content document, if we
+ // still meet intermittent failures, we might want to do it here as
+ // well.
+ await getActorForTab(tabId, "TestSupport").sendQuery(
+ "FlushApzRepaints"
+ );
+ },
+
+ async promiseAllPaintsDone(tabId) {
+ await getActorForTab(tabId, "TestSupport").sendQuery(
+ "PromiseAllPaintsDone"
+ );
+ },
+
+ async usingGpuProcess() {
+ const gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(
+ Ci.nsIGfxInfo
+ );
+ return gfxInfo.usingGPUProcess;
+ },
+
+ async killGpuProcess() {
+ const gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(
+ Ci.nsIGfxInfo
+ );
+ return gfxInfo.killGPUProcessForTests();
+ },
+
+ async crashGpuProcess() {
+ const gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(
+ Ci.nsIGfxInfo
+ );
+ return gfxInfo.crashGPUProcessForTests();
+ },
+
+ async clearHSTSState() {
+ const sss = Cc["@mozilla.org/ssservice;1"].getService(
+ Ci.nsISiteSecurityService
+ );
+ return sss.clearAll();
+ },
+
+ async triggerCookieBannerDetected(tabId) {
+ const actor = getActorForTab(tabId, "CookieBanner");
+ return actor.receiveMessage({
+ name: "CookieBanner::DetectedBanner",
+ });
+ },
+
+ async triggerCookieBannerHandled(tabId) {
+ const actor = getActorForTab(tabId, "CookieBanner");
+ return actor.receiveMessage({
+ name: "CookieBanner::HandledBanner",
+ });
+ },
+
+ async triggerTranslationsOffer(tabId) {
+ const browser = context.extension.tabManager.get(tabId).browser;
+ const { CustomEvent } = browser.ownerGlobal;
+ return browser.dispatchEvent(
+ new CustomEvent("TranslationsParent:OfferTranslation", {
+ bubbles: true,
+ })
+ );
+ },
+
+ async triggerLanguageStateChange(tabId, languageState) {
+ const browser = context.extension.tabManager.get(tabId).browser;
+ const { CustomEvent } = browser.ownerGlobal;
+ return browser.dispatchEvent(
+ new CustomEvent("TranslationsParent:LanguageState", {
+ bubbles: true,
+ detail: languageState,
+ })
+ );
+ },
+ },
+ };
+ }
+};
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-schema.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-schema.json
new file mode 100644
index 0000000000..94e4b3bd9b
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-schema.json
@@ -0,0 +1,308 @@
+[
+ {
+ "namespace": "test",
+ "description": "Additional APIs for test support in GeckoView.",
+ "functions": [
+ {
+ "name": "setPrefs",
+ "type": "function",
+ "async": true,
+ "description": "Set prefs and return a set of saved prefs",
+ "parameters": [
+ {
+ "name": "oldPrefs",
+ "type": "object",
+ "properties": {},
+ "additionalProperties": { "type": "any" }
+ },
+ {
+ "name": "newPrefs",
+ "type": "object",
+ "properties": {},
+ "additionalProperties": { "type": "any" }
+ }
+ ]
+ },
+ {
+ "name": "restorePrefs",
+ "type": "function",
+ "async": true,
+ "description": "Restore prefs to old value",
+ "parameters": [
+ {
+ "type": "any",
+ "name": "oldPrefs"
+ }
+ ]
+ },
+ {
+ "name": "getPrefs",
+ "type": "function",
+ "async": true,
+ "description": "Get pref values.",
+ "parameters": [
+ {
+ "name": "prefs",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ ]
+ },
+ {
+ "name": "getLinkColor",
+ "type": "function",
+ "async": true,
+ "description": "Get resolved color for the link resolved by a given selector.",
+ "parameters": [
+ {
+ "type": "number",
+ "name": "tabId"
+ },
+ {
+ "type": "string",
+ "name": "selector"
+ }
+ ]
+ },
+ {
+ "name": "getRequestedLocales",
+ "type": "function",
+ "async": true,
+ "description": "Gets the requested locales.",
+ "parameters": []
+ },
+ {
+ "name": "addHistogram",
+ "type": "function",
+ "async": true,
+ "description": "Add a sample with the given value to the histogram with the given id.",
+ "parameters": [
+ {
+ "type": "string",
+ "name": "id"
+ },
+ {
+ "type": "any",
+ "name": "value"
+ }
+ ]
+ },
+ {
+ "name": "removeAllCertOverrides",
+ "type": "function",
+ "async": true,
+ "description": "Revokes SSL certificate overrides.",
+ "parameters": []
+ },
+ {
+ "name": "setScalar",
+ "type": "function",
+ "async": true,
+ "description": "Set the given value to the scalar with the given id.",
+ "parameters": [
+ {
+ "type": "string",
+ "name": "id"
+ },
+ {
+ "type": "any",
+ "name": "value"
+ }
+ ]
+ },
+ {
+ "name": "setResolutionAndScaleTo",
+ "type": "function",
+ "async": true,
+ "description": "Invokes nsIDOMWindowUtils.setResolutionAndScaleTo.",
+ "parameters": [
+ {
+ "type": "number",
+ "name": "tabId"
+ },
+ {
+ "type": "number",
+ "name": "resolution"
+ }
+ ]
+ },
+ {
+ "name": "getActive",
+ "type": "function",
+ "async": true,
+ "description": "Returns true if the docShell is active for given tab.",
+ "parameters": [
+ {
+ "type": "number",
+ "name": "tabId"
+ }
+ ]
+ },
+ {
+ "name": "getPidForTab",
+ "type": "function",
+ "async": true,
+ "description": "Gets the top-level pid belonging to tabId.",
+ "parameters": [
+ {
+ "type": "number",
+ "name": "tabId"
+ }
+ ]
+ },
+ {
+ "name": "waitForContentTransformsReceived",
+ "type": "function",
+ "async": true,
+ "description": "If we want to test screen coordinates, we need to wait for the updated data which is what this function allows us to do",
+ "parameters": [
+ {
+ "type": "number",
+ "name": "tabId"
+ }
+ ]
+ },
+ {
+ "name": "getAllBrowserPids",
+ "type": "function",
+ "async": true,
+ "description": "Gets the list of pids of the running browser processes",
+ "parameters": []
+ },
+ {
+ "name": "getProfilePath",
+ "type": "function",
+ "async": true,
+ "description": "Gets the path of the current profile",
+ "parameters": []
+ },
+ {
+ "name": "killContentProcess",
+ "type": "function",
+ "async": true,
+ "description": "Crash all content processes",
+ "parameters": [
+ {
+ "type": "number",
+ "name": "pid"
+ }
+ ]
+ },
+ {
+ "name": "flushApzRepaints",
+ "type": "function",
+ "async": true,
+ "description": "Invokes nsIDOMWindowUtils.flushApzRepaints for the document of the tabId.",
+ "parameters": [
+ {
+ "type": "number",
+ "name": "tabId"
+ }
+ ]
+ },
+ {
+ "name": "promiseAllPaintsDone",
+ "type": "function",
+ "async": true,
+ "description": "A simplified version of promiseAllPaintsDone in paint_listeners.js.",
+ "parameters": [
+ {
+ "type": "number",
+ "name": "tabId"
+ }
+ ]
+ },
+ {
+ "name": "usingGpuProcess",
+ "type": "function",
+ "async": true,
+ "description": "Returns true if Gecko is using a GPU process.",
+ "parameters": []
+ },
+
+ {
+ "name": "killGpuProcess",
+ "type": "function",
+ "async": true,
+ "description": "Kills the GPU process cleanly without generating a crash report.",
+ "parameters": []
+ },
+
+ {
+ "name": "crashGpuProcess",
+ "type": "function",
+ "async": true,
+ "description": "Causes the GPU process to crash.",
+ "parameters": []
+ },
+
+ {
+ "name": "clearHSTSState",
+ "type": "function",
+ "async": true,
+ "description": "Clears the sites on the HSTS list.",
+ "parameters": []
+ },
+
+ {
+ "name": "triggerCookieBannerDetected",
+ "type": "function",
+ "async": true,
+ "description": "Simulates a cookie banner detection",
+ "parameters": [
+ {
+ "type": "number",
+ "name": "tabId"
+ }
+ ]
+ },
+
+ {
+ "name": "triggerCookieBannerHandled",
+ "type": "function",
+ "async": true,
+ "description": "Simulates a cookie banner handling",
+ "parameters": [
+ {
+ "type": "number",
+ "name": "tabId"
+ }
+ ]
+ },
+
+ {
+ "name": "triggerTranslationsOffer",
+ "type": "function",
+ "async": true,
+ "description": "Simulates offering a translation.",
+ "parameters": [
+ {
+ "type": "number",
+ "name": "tabId"
+ }
+ ]
+ },
+
+ {
+ "name": "triggerLanguageStateChange",
+ "type": "function",
+ "async": true,
+ "description": "Simulates expecting a translation.",
+ "parameters": [
+ {
+ "type": "number",
+ "name": "tabId"
+ },
+ {
+ "name": "languageState",
+ "type": "object",
+ "properties": {},
+ "additionalProperties": { "type": "any" }
+ }
+ ]
+ }
+ ]
+ }
+]
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-support.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-support.js
new file mode 100644
index 0000000000..18e047ca1a
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-support.js
@@ -0,0 +1,60 @@
+/* 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/. */
+
+let backgroundPort = null;
+let nativePort = null;
+
+function connectNativePort() {
+ if (nativePort) {
+ return;
+ }
+
+ backgroundPort = browser.runtime.connect();
+ nativePort = browser.runtime.connectNative("browser");
+
+ nativePort.onMessage.addListener(message => {
+ if (message.type) {
+ // This is a session-specific webExtensionApiCall.
+ // Forward to the background script.
+ backgroundPort.postMessage(message);
+ } else if (message.eval) {
+ try {
+ // Using eval here is the whole point of this WebExtension so we can
+ // safely ignore the eslint warning.
+ const response = window.eval(message.eval); // eslint-disable-line no-eval
+ sendResponse(message.id, response);
+ } catch (ex) {
+ sendSyncResponse(message.id, null, ex);
+ }
+ }
+ });
+
+ function sendResponse(id, response, exception) {
+ Promise.resolve(response).then(
+ value => sendSyncResponse(id, value),
+ reason => sendSyncResponse(id, null, reason)
+ );
+ }
+
+ function sendSyncResponse(id, response, exception) {
+ nativePort.postMessage({
+ id,
+ response: JSON.stringify(response),
+ exception: exception && exception.toString(),
+ });
+ }
+}
+
+function disconnectNativePort() {
+ backgroundPort?.disconnect();
+ nativePort?.disconnect();
+ backgroundPort = null;
+ nativePort = null;
+}
+
+window.addEventListener("pageshow", connectNativePort);
+window.addEventListener("pagehide", disconnectNativePort);
+
+// If loading error page, pageshow mightn't fired.
+connectNativePort();
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-1/borderify.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-1/borderify.js
new file mode 100644
index 0000000000..9c3728b381
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-1/borderify.js
@@ -0,0 +1 @@
+document.body.style.border = "5px solid red";
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-1/manifest.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-1/manifest.json
new file mode 100644
index 0000000000..8e54cc4586
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-1/manifest.json
@@ -0,0 +1,18 @@
+{
+ "manifest_version": 2,
+ "name": "update",
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "update@example.com",
+ "update_url": "https://example.org/tests/junit/update_manifest.json"
+ }
+ },
+ "version": "1.0",
+ "description": "Adds a red border to all webpages matching example.com.",
+ "content_scripts": [
+ {
+ "matches": ["*://*.example.com/*"],
+ "js": ["borderify.js"]
+ }
+ ]
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-2/borderify.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-2/borderify.js
new file mode 100644
index 0000000000..3529928d82
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-2/borderify.js
@@ -0,0 +1 @@
+document.body.style.border = "5px solid blue";
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-2/manifest.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-2/manifest.json
new file mode 100644
index 0000000000..19570ea5e5
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-2/manifest.json
@@ -0,0 +1,17 @@
+{
+ "manifest_version": 2,
+ "name": "update",
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "update@example.com"
+ }
+ },
+ "version": "2.0",
+ "description": "Adds a blue border to all webpages matching example.com.",
+ "content_scripts": [
+ {
+ "matches": ["*://*.example.com/*"],
+ "js": ["borderify.js"]
+ }
+ ]
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-postpone-1/background.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-postpone-1/background.js
new file mode 100644
index 0000000000..a301506ca7
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-postpone-1/background.js
@@ -0,0 +1,3 @@
+browser.runtime.onUpdateAvailable.addListener(details => {
+ // Do nothing, this is just here to prevent auto update.
+});
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-postpone-1/borderify.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-postpone-1/borderify.js
new file mode 100644
index 0000000000..9c3728b381
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-postpone-1/borderify.js
@@ -0,0 +1 @@
+document.body.style.border = "5px solid red";
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-postpone-1/manifest.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-postpone-1/manifest.json
new file mode 100644
index 0000000000..5011e1ea05
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-postpone-1/manifest.json
@@ -0,0 +1,21 @@
+{
+ "manifest_version": 2,
+ "name": "update",
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "update-postpone@example.com",
+ "update_url": "https://example.org/tests/junit/update_manifest.json"
+ }
+ },
+ "background": {
+ "scripts": ["background.js"]
+ },
+ "version": "1.0",
+ "description": "Adds a red border to all webpages matching example.com.",
+ "content_scripts": [
+ {
+ "matches": ["*://*.example.com/*"],
+ "js": ["borderify.js"]
+ }
+ ]
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-postpone-2/borderify.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-postpone-2/borderify.js
new file mode 100644
index 0000000000..3529928d82
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-postpone-2/borderify.js
@@ -0,0 +1 @@
+document.body.style.border = "5px solid blue";
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-postpone-2/manifest.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-postpone-2/manifest.json
new file mode 100644
index 0000000000..720d9ef898
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-postpone-2/manifest.json
@@ -0,0 +1,17 @@
+{
+ "manifest_version": 2,
+ "name": "update",
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "update-postpone@example.com"
+ }
+ },
+ "version": "2.0",
+ "description": "Adds a blue border to all webpages matching example.com.",
+ "content_scripts": [
+ {
+ "matches": ["*://*.example.com/*"],
+ "js": ["borderify.js"]
+ }
+ ]
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-with-perms-1/borderify.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-with-perms-1/borderify.js
new file mode 100644
index 0000000000..9c3728b381
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-with-perms-1/borderify.js
@@ -0,0 +1 @@
+document.body.style.border = "5px solid red";
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-with-perms-1/manifest.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-with-perms-1/manifest.json
new file mode 100644
index 0000000000..71b6a1eab9
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-with-perms-1/manifest.json
@@ -0,0 +1,18 @@
+{
+ "manifest_version": 2,
+ "name": "update",
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "update-with-perms@example.com",
+ "update_url": "https://example.org/tests/junit/update_manifest.json"
+ }
+ },
+ "version": "1.0",
+ "description": "Adds a red border to all webpages matching example.com.",
+ "content_scripts": [
+ {
+ "matches": ["*://*.example.com/*"],
+ "js": ["borderify.js"]
+ }
+ ]
+}
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-with-perms-2/borderify.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-with-perms-2/borderify.js
new file mode 100644
index 0000000000..3529928d82
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-with-perms-2/borderify.js
@@ -0,0 +1 @@
+document.body.style.border = "5px solid blue";
diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-with-perms-2/manifest.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-with-perms-2/manifest.json
new file mode 100644
index 0000000000..9571bdabb2
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/update-with-perms-2/manifest.json
@@ -0,0 +1,18 @@
+{
+ "manifest_version": 2,
+ "name": "update",
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "update-with-perms@example.com"
+ }
+ },
+ "version": "2.0",
+ "description": "Adds a blue border to all webpages matching example.com.",
+ "content_scripts": [
+ {
+ "matches": ["*://*.example.com/*"],
+ "js": ["borderify.js"]
+ }
+ ],
+ "permissions": ["tabs"]
+}