diff options
Diffstat (limited to 'browser/extensions/webcompat/experiment-apis')
16 files changed, 798 insertions, 0 deletions
diff --git a/browser/extensions/webcompat/experiment-apis/aboutConfigPrefs.js b/browser/extensions/webcompat/experiment-apis/aboutConfigPrefs.js new file mode 100644 index 0000000000..08a4c3d091 --- /dev/null +++ b/browser/extensions/webcompat/experiment-apis/aboutConfigPrefs.js @@ -0,0 +1,57 @@ +/* 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"; + +/* global ExtensionAPI, ExtensionCommon, Services, XPCOMUtils */ + +XPCOMUtils.defineLazyModuleGetters(this, { + Services: "resource://gre/modules/Services.jsm", +}); + +this.aboutConfigPrefs = class extends ExtensionAPI { + getAPI(context) { + const EventManager = ExtensionCommon.EventManager; + const extensionIDBase = context.extension.id.split("@")[0]; + const extensionPrefNameBase = `extensions.${extensionIDBase}.`; + + return { + aboutConfigPrefs: { + onPrefChange: new EventManager({ + context, + name: "aboutConfigPrefs.onUAOverridesPrefChange", + register: (fire, name) => { + const prefName = `${extensionPrefNameBase}${name}`; + const callback = () => { + fire.async(name).catch(() => {}); // ignore Message Manager disconnects + }; + Services.prefs.addObserver(prefName, callback); + return () => { + Services.prefs.removeObserver(prefName, callback); + }; + }, + }).api(), + async getBranch(branchName) { + const branch = `${extensionPrefNameBase}${branchName}.`; + return Services.prefs.getChildList(branch).map(pref => { + const name = pref.replace(branch, ""); + return { name, value: Services.prefs.getBoolPref(pref) }; + }); + }, + async getPref(name) { + try { + return Services.prefs.getBoolPref( + `${extensionPrefNameBase}${name}` + ); + } catch (_) { + return undefined; + } + }, + async setPref(name, value) { + Services.prefs.setBoolPref(`${extensionPrefNameBase}${name}`, value); + }, + }, + }; + } +}; diff --git a/browser/extensions/webcompat/experiment-apis/aboutConfigPrefs.json b/browser/extensions/webcompat/experiment-apis/aboutConfigPrefs.json new file mode 100644 index 0000000000..44284f199c --- /dev/null +++ b/browser/extensions/webcompat/experiment-apis/aboutConfigPrefs.json @@ -0,0 +1,72 @@ +[ + { + "namespace": "aboutConfigPrefs", + "description": "experimental API extension to allow access to about:config preferences", + "events": [ + { + "name": "onPrefChange", + "type": "function", + "parameters": [ + { + "name": "name", + "type": "string", + "description": "The preference which changed" + } + ], + "extraParameters": [ + { + "name": "name", + "type": "string", + "description": "The preference to monitor" + } + ] + } + ], + "functions": [ + { + "name": "getBranch", + "type": "function", + "description": "Get all child prefs for a branch", + "parameters": [ + { + "name": "branchName", + "type": "string", + "description": "The branch name" + } + ], + "async": true + }, + { + "name": "getPref", + "type": "function", + "description": "Get a preference's value", + "parameters": [ + { + "name": "name", + "type": "string", + "description": "The preference name" + } + ], + "async": true + }, + { + "name": "setPref", + "type": "function", + "description": "Set a preference's value", + "parameters": [ + { + "name": "name", + "type": "string", + "description": "The preference name" + }, + { + "name": "value", + "type": "boolean", + "description": "The new value" + } + ], + "async": true + } + ] + } +] diff --git a/browser/extensions/webcompat/experiment-apis/appConstants.js b/browser/extensions/webcompat/experiment-apis/appConstants.js new file mode 100644 index 0000000000..7019eb6215 --- /dev/null +++ b/browser/extensions/webcompat/experiment-apis/appConstants.js @@ -0,0 +1,32 @@ +/* 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"; + +/* global ExtensionAPI, XPCOMUtils */ + +XPCOMUtils.defineLazyModuleGetters(this, { + AppConstants: "resource://gre/modules/AppConstants.jsm", +}); + +this.appConstants = class extends ExtensionAPI { + getAPI(context) { + return { + appConstants: { + getReleaseBranch: () => { + if (AppConstants.NIGHTLY_BUILD) { + return "nightly"; + } else if (AppConstants.MOZ_DEV_EDITION) { + return "dev_edition"; + } else if (AppConstants.EARLY_BETA_OR_EARLIER) { + return "early_beta_or_earlier"; + } else if (AppConstants.BETA_OR_RELEASE) { + return "beta_or_release"; + } + return "unknown"; + }, + }, + }; + } +}; diff --git a/browser/extensions/webcompat/experiment-apis/appConstants.json b/browser/extensions/webcompat/experiment-apis/appConstants.json new file mode 100644 index 0000000000..cf04915eca --- /dev/null +++ b/browser/extensions/webcompat/experiment-apis/appConstants.json @@ -0,0 +1,15 @@ +[ + { + "namespace": "appConstants", + "description": "experimental API to expose some app constants", + "functions": [ + { + "name": "getReleaseBranch", + "type": "function", + "description": "", + "async": true, + "parameters": [] + } + ] + } +] diff --git a/browser/extensions/webcompat/experiment-apis/experiments.js b/browser/extensions/webcompat/experiment-apis/experiments.js new file mode 100644 index 0000000000..d1ab77c312 --- /dev/null +++ b/browser/extensions/webcompat/experiment-apis/experiments.js @@ -0,0 +1,34 @@ +/* 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"; + +/* global ExtensionAPI, Services, XPCOMUtils */ + +XPCOMUtils.defineLazyModuleGetters(this, { + EventDispatcher: "resource://gre/modules/Messaging.jsm", + Services: "resource://gre/modules/Services.jsm", +}); + +this.experiments = class extends ExtensionAPI { + getAPI(context) { + function promiseActiveExperiments() { + return EventDispatcher.instance.sendRequestForResult({ + type: "Experiments:GetActive", + }); + } + return { + experiments: { + async isActive(name) { + if (!Services.androidBridge || !Services.androidBridge.isFennec) { + return undefined; + } + return promiseActiveExperiments().then(experiments => { + return experiments.includes(name); + }); + }, + }, + }; + } +}; diff --git a/browser/extensions/webcompat/experiment-apis/experiments.json b/browser/extensions/webcompat/experiment-apis/experiments.json new file mode 100644 index 0000000000..44f833215c --- /dev/null +++ b/browser/extensions/webcompat/experiment-apis/experiments.json @@ -0,0 +1,21 @@ +[ + { + "namespace": "experiments", + "description": "experimental API extension to allow checking the status of Fennec experiments via Switchboard", + "functions": [ + { + "name": "isActive", + "type": "function", + "description": "Determine if a given experiment is active.", + "parameters": [ + { + "name": "name", + "type": "string", + "description": "The experiment's name" + } + ], + "async": true + } + ] + } +] diff --git a/browser/extensions/webcompat/experiment-apis/matchPatterns.js b/browser/extensions/webcompat/experiment-apis/matchPatterns.js new file mode 100644 index 0000000000..422cba5fc4 --- /dev/null +++ b/browser/extensions/webcompat/experiment-apis/matchPatterns.js @@ -0,0 +1,30 @@ +/* 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"; + +/* global ExtensionAPI */ + +this.matchPatterns = class extends ExtensionAPI { + getAPI(context) { + return { + matchPatterns: { + getMatcher(patterns) { + const set = new MatchPatternSet(patterns); + return Cu.cloneInto( + { + matches: url => { + return set.matches(url); + }, + }, + context.cloneScope, + { + cloneFunctions: true, + } + ); + }, + }, + }; + } +}; diff --git a/browser/extensions/webcompat/experiment-apis/matchPatterns.json b/browser/extensions/webcompat/experiment-apis/matchPatterns.json new file mode 100644 index 0000000000..6fb4dc10fc --- /dev/null +++ b/browser/extensions/webcompat/experiment-apis/matchPatterns.json @@ -0,0 +1,29 @@ +[ + { + "namespace": "matchPatterns", + "description": "experimental API extension to expose MatchPattern functionality", + "functions": [ + { + "name": "getMatcher", + "type": "function", + "description": "get a MatchPatternSet", + "parameters": [ + { + "name": "patterns", + "description": "Array of string URL patterns to match", + "type": "array", + "items": { + "type": "string" + } + } + ], + "returns": { + "type": "object", + "properties": { + "matches": { "type": "function" } + } + } + } + ] + } +] diff --git a/browser/extensions/webcompat/experiment-apis/pictureInPicture.js b/browser/extensions/webcompat/experiment-apis/pictureInPicture.js new file mode 100644 index 0000000000..e78ddbf315 --- /dev/null +++ b/browser/extensions/webcompat/experiment-apis/pictureInPicture.js @@ -0,0 +1,90 @@ +/* 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"; + +/* global ChromeUtils, ExtensionAPI, Services */ +ChromeUtils.defineModuleGetter( + this, + "Services", + "resource://gre/modules/Services.jsm" +); + +ChromeUtils.defineModuleGetter( + this, + "KEYBOARD_CONTROLS", + "resource://gre/modules/PictureInPictureControls.jsm" +); + +ChromeUtils.defineModuleGetter( + this, + "TOGGLE_POLICIES", + "resource://gre/modules/PictureInPictureControls.jsm" +); + +ChromeUtils.defineModuleGetter( + this, + "AppConstants", + "resource://gre/modules/AppConstants.jsm" +); + +const TOGGLE_ENABLED_PREF = + "media.videocontrols.picture-in-picture.video-toggle.enabled"; + +/** + * This API is expected to be running in the parent process. + */ +this.pictureInPictureParent = class extends ExtensionAPI { + getAPI(context) { + return { + pictureInPictureParent: { + setOverrides(overrides) { + // The Picture-in-Picture toggle is only implemented for Desktop, so make + // this a no-op for non-Desktop builds. + if (AppConstants.platform == "android") { + return; + } + + Services.ppmm.sharedData.set( + "PictureInPicture:SiteOverrides", + overrides + ); + }, + }, + }; + } +}; + +/** + * This API is expected to be running in a content process - specifically, + * the WebExtension content process that the background scripts run in. We + * split these out so that they can return values synchronously to the + * background scripts. + */ +this.pictureInPictureChild = class extends ExtensionAPI { + getAPI(context) { + return { + pictureInPictureChild: { + getKeyboardControls() { + // The Picture-in-Picture toggle is only implemented for Desktop, so make + // this return nothing for non-Desktop builds. + if (AppConstants.platform == "android") { + return Cu.cloneInto({}, context.cloneScope); + } + + return Cu.cloneInto(KEYBOARD_CONTROLS, context.cloneScope); + }, + getPolicies() { + // The Picture-in-Picture toggle is only implemented for Desktop, so make + // this return nothing for non-Desktop builds. + if (AppConstants.platform == "android") { + return Cu.cloneInto({}, context.cloneScope); + } + + return Cu.cloneInto(TOGGLE_POLICIES, context.cloneScope); + }, + }, + }; + } +}; diff --git a/browser/extensions/webcompat/experiment-apis/pictureInPicture.json b/browser/extensions/webcompat/experiment-apis/pictureInPicture.json new file mode 100644 index 0000000000..5f34616b6e --- /dev/null +++ b/browser/extensions/webcompat/experiment-apis/pictureInPicture.json @@ -0,0 +1,51 @@ +[ + { + "namespace": "pictureInPictureParent", + "description": "Parent process methods for controlling the Picture-in-Picture feature.", + "functions": [ + { + "name": "setOverrides", + "type": "function", + "description": "Set Picture-in-Picture toggle position overrides", + "parameters": [ + { + "name": "overrides", + "type": "object", + "additionalProperties": { "type": "any" }, + "description": "The Picture-in-Picture toggle position overrides to set" + } + ] + } + ] + }, + { + "namespace": "pictureInPictureChild", + "description": "WebExtension process methods for querying the Picture-in-Picture feature.", + "functions": [ + { + "name": "getKeyboardControls", + "type": "function", + "description": "Get the Picture-in-Picture keyboard control override constants", + "parameters": [], + "returns": { + "type": "object", + "properties": {}, + "additionalProperties": { "type": "any" }, + "description": "The Picture-in-Picture keyboard control override constants" + } + }, + { + "name": "getPolicies", + "type": "function", + "description": "Get the Picture-in-Picture toggle position override constants", + "parameters": [], + "returns": { + "type": "object", + "properties": {}, + "additionalProperties": { "type": "any" }, + "description": "The Picture-in-Picture toggle position override constants" + } + } + ] + } +] diff --git a/browser/extensions/webcompat/experiment-apis/sharedPreferences.js b/browser/extensions/webcompat/experiment-apis/sharedPreferences.js new file mode 100644 index 0000000000..b2a19b3a6c --- /dev/null +++ b/browser/extensions/webcompat/experiment-apis/sharedPreferences.js @@ -0,0 +1,33 @@ +/* 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"; + +/* global ExtensionAPI, Services, XPCOMUtils */ + +XPCOMUtils.defineLazyModuleGetters(this, { + Services: "resource://gre/modules/Services.jsm", + SharedPreferences: "resource://gre/modules/SharedPreferences.jsm", +}); + +this.sharedPreferences = class extends ExtensionAPI { + getAPI(context) { + return { + sharedPreferences: { + async setCharPref(name, value) { + if (!Services.androidBridge || !Services.androidBridge.isFennec) { + return; + } + SharedPreferences.forApp().setCharPref(name, value); + }, + async setBoolPref(name, value) { + if (!Services.androidBridge || !Services.androidBridge.isFennec) { + return; + } + SharedPreferences.forApp().setBoolPref(name, value); + }, + }, + }; + } +}; diff --git a/browser/extensions/webcompat/experiment-apis/sharedPreferences.json b/browser/extensions/webcompat/experiment-apis/sharedPreferences.json new file mode 100644 index 0000000000..cf73414cf4 --- /dev/null +++ b/browser/extensions/webcompat/experiment-apis/sharedPreferences.json @@ -0,0 +1,44 @@ +[ + { + "namespace": "sharedPreferences", + "description": "experimental API extension to allow setting SharedPreferences on Fennec", + "functions": [ + { + "name": "setBoolPref", + "type": "function", + "description": "Set the value of a boolean Fennec SharedPreference", + "parameters": [ + { + "name": "name", + "type": "string", + "description": "The key name" + }, + { + "name": "value", + "type": "boolean", + "description": "The new value" + } + ], + "async": true + }, + { + "name": "setCharPref", + "type": "function", + "description": "Set the value of a string Fennec SharedPreference", + "parameters": [ + { + "name": "name", + "type": "string", + "description": "The key name" + }, + { + "name": "value", + "type": "string", + "description": "The new value" + } + ], + "async": true + } + ] + } +] diff --git a/browser/extensions/webcompat/experiment-apis/systemManufacturer.js b/browser/extensions/webcompat/experiment-apis/systemManufacturer.js new file mode 100644 index 0000000000..c3819c1128 --- /dev/null +++ b/browser/extensions/webcompat/experiment-apis/systemManufacturer.js @@ -0,0 +1,27 @@ +/* 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"; + +/* global ExtensionAPI, Services, XPCOMUtils */ + +XPCOMUtils.defineLazyModuleGetters(this, { + Services: "resource://gre/modules/Services.jsm", +}); + +this.systemManufacturer = class extends ExtensionAPI { + getAPI(context) { + return { + systemManufacturer: { + getManufacturer() { + try { + return Services.sysinfo.getProperty("manufacturer"); + } catch (_) { + return undefined; + } + }, + }, + }; + } +}; diff --git a/browser/extensions/webcompat/experiment-apis/systemManufacturer.json b/browser/extensions/webcompat/experiment-apis/systemManufacturer.json new file mode 100644 index 0000000000..c64fccc46d --- /dev/null +++ b/browser/extensions/webcompat/experiment-apis/systemManufacturer.json @@ -0,0 +1,20 @@ +[ + { + "namespace": "systemManufacturer", + "description": "experimental API extension to allow reading the device's manufacturer", + "functions": [ + { + "name": "getManufacturer", + "type": "function", + "description": "Get the device's manufacturer", + "parameters": [], + "returns": { + "type": "string", + "properties": {}, + "additionalProperties": { "type": "any" }, + "description": "The manufacturer's name." + } + } + ] + } +] diff --git a/browser/extensions/webcompat/experiment-apis/trackingProtection.js b/browser/extensions/webcompat/experiment-apis/trackingProtection.js new file mode 100644 index 0000000000..557090e974 --- /dev/null +++ b/browser/extensions/webcompat/experiment-apis/trackingProtection.js @@ -0,0 +1,168 @@ +/* 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"; + +/* global ExtensionAPI, ExtensionCommon, ExtensionParent, Services, XPCOMUtils */ + +XPCOMUtils.defineLazyModuleGetters(this, { + Services: "resource://gre/modules/Services.jsm", +}); + +XPCOMUtils.defineLazyGlobalGetters(this, ["URL", "ChannelWrapper"]); + +class Manager { + constructor() { + this._allowLists = new Map(); + } + + _ensureStarted() { + if (this._classifierObserver) { + return; + } + + this._unblockedChannelIds = new Set(); + this._channelClassifier = Cc[ + "@mozilla.org/url-classifier/channel-classifier-service;1" + ].getService(Ci.nsIChannelClassifierService); + this._classifierObserver = {}; + this._classifierObserver.observe = (subject, topic, data) => { + switch (topic) { + case "http-on-stop-request": { + const { channelId } = subject.QueryInterface(Ci.nsIIdentChannel); + this._unblockedChannelIds.delete(channelId); + break; + } + case "urlclassifier-before-block-channel": { + const channel = subject.QueryInterface( + Ci.nsIUrlClassifierBlockedChannel + ); + const { channelId, url } = channel; + let topHost; + try { + topHost = new URL(channel.topLevelUrl).hostname; + } catch (_) { + return; + } + for (const allowList of this._allowLists.values()) { + for (const entry of allowList.values()) { + const { matcher, hosts, notHosts } = entry; + if (matcher.matches(url)) { + if ( + !notHosts?.has(topHost) && + (hosts === true || hosts.has(topHost)) + ) { + this._unblockedChannelIds.add(channelId); + channel.unblock(); + return; + } + } + } + } + break; + } + } + }; + Services.obs.addObserver(this._classifierObserver, "http-on-stop-request"); + this._channelClassifier.addListener(this._classifierObserver); + } + + stop() { + if (!this._classifierObserver) { + return; + } + + Services.obs.removeObserver( + this._classifierObserver, + "http-on-stop-request" + ); + this._channelClassifier.removeListener(this._classifierObserver); + delete this._channelClassifier; + delete this._classifierObserver; + } + + wasChannelIdUnblocked(channelId) { + return this._unblockedChannelIds.has(channelId); + } + + allow(allowListId, patterns, { hosts, notHosts }) { + this._ensureStarted(); + + if (!this._allowLists.has(allowListId)) { + this._allowLists.set(allowListId, new Map()); + } + const allowList = this._allowLists.get(allowListId); + for (const pattern of patterns) { + if (!allowList.has(pattern)) { + allowList.set(pattern, { + matcher: new MatchPattern(pattern), + }); + } + const allowListPattern = allowList.get(pattern); + if (!hosts) { + allowListPattern.hosts = true; + } else { + if (!allowListPattern.hosts) { + allowListPattern.hosts = new Set(); + } + for (const host of hosts) { + allowListPattern.hosts.add(host); + } + } + if (notHosts) { + if (!allowListPattern.notHosts) { + allowListPattern.notHosts = new Set(); + } + for (const notHost of notHosts) { + allowListPattern.notHosts.add(notHost); + } + } + } + } + + revoke(allowListId) { + this._allowLists.delete(allowListId); + } +} +var manager = new Manager(); + +function getChannelId(context, requestId) { + const wrapper = ChannelWrapper.getRegisteredChannel( + requestId, + context.extension.policy, + context.xulBrowser.frameLoader.remoteTab + ); + return wrapper?.channel?.QueryInterface(Ci.nsIIdentChannel)?.channelId; +} + +this.trackingProtection = class extends ExtensionAPI { + onShutdown(isAppShutdown) { + if (manager) { + manager.stop(); + } + } + + getAPI(context) { + return { + trackingProtection: { + async allow(allowListId, patterns, options) { + manager.allow(allowListId, patterns, options); + }, + async revoke(allowListId) { + manager.revoke(allowListId); + }, + async wasRequestUnblocked(requestId) { + if (!manager) { + return false; + } + const channelId = getChannelId(context, requestId); + if (!channelId) { + return false; + } + return manager.wasChannelIdUnblocked(channelId); + }, + }, + }; + } +}; diff --git a/browser/extensions/webcompat/experiment-apis/trackingProtection.json b/browser/extensions/webcompat/experiment-apis/trackingProtection.json new file mode 100644 index 0000000000..2627d1ea0f --- /dev/null +++ b/browser/extensions/webcompat/experiment-apis/trackingProtection.json @@ -0,0 +1,75 @@ +[ + { + "namespace": "trackingProtection", + "description": "experimental API allow requests through ETP", + "functions": [ + { + "name": "allow", + "type": "function", + "description": "Add specific requests to a given allow-list", + "parameters": [ + { + "name": "allowlistId", + "type": "string" + }, + { + "name": "patterns", + "description": "Array of match patterns", + "type": "array", + "items": { + "type": "string" + } + }, + { + "name": "options", + "type": "object", + "optional": true, + "properties": { + "hosts": { + "description": "Hosts to limit this bypass to (optional)", + "type": "array", + "items": { + "type": "string" + }, + "optional": true + }, + "notHosts": { + "description": "Hosts to not allow this bypass for (optional)", + "type": "array", + "items": { + "type": "string" + }, + "optional": true + } + } + } + ], + "async": true + }, + { + "name": "revoke", + "type": "function", + "description": "Revokes the given allow-list", + "parameters": [ + { + "name": "allowListId", + "type": "string" + } + ], + "async": true + }, + { + "name": "wasRequestUnblocked", + "type": "function", + "description": "Whether the given requestId was unblocked by any allowList", + "parameters": [ + { + "name": "requestId", + "type": "string" + } + ], + "async": true + } + ] + } +] |