commit 6a66b9a44f7a247499c247b9209f1b77b690b9cb
parent 17f1b13b340513313b090ad6c0e2db308ea281fe
Author: Thomas Wisniewski <twisniewski@mozilla.com>
Date: Mon, 27 Oct 2025 17:52:23 +0000
Bug 1996081 - Add the ability to enable/disable individual webcompat interventions with about:config prefs; r=denschub,webcompat-reviewers
Differential Revision: https://phabricator.services.mozilla.com/D269880
Diffstat:
4 files changed, 326 insertions(+), 13 deletions(-)
diff --git a/browser/extensions/webcompat/lib/interventions.js b/browser/extensions/webcompat/lib/interventions.js
@@ -16,6 +16,8 @@ class Interventions {
this._readyPromise = new Promise(done => (this._resolveReady = done));
+ this._disabledPrefListeners = {};
+
this._availableInterventions = this._reformatSourceJSON(
availableInterventions
);
@@ -96,19 +98,6 @@ class Interventions {
});
}
- checkOverridePref() {
- navigator.locks.request("pref_check_lock", async () => {
- const value = await browser.aboutConfigPrefs.getPref(this.OVERRIDE_PREF);
- if (value === undefined) {
- await browser.aboutConfigPrefs.setPref(this.OVERRIDE_PREF, true);
- } else if (value === false) {
- await this.unregisterUAOverrides();
- } else {
- await this.registerUAOverrides();
- }
- });
- }
-
getAvailableInterventions() {
return this._availableInterventions;
}
@@ -132,6 +121,14 @@ class Interventions {
) {
return navigator.locks.request("intervention_lock", async () => {
for (const config of whichInterventions) {
+ const disabling_pref_listener = this._disabledPrefListeners[config.id];
+ if (disabling_pref_listener) {
+ browser.aboutConfigPrefs.onPrefChange.removeListener(
+ disabling_pref_listener
+ );
+ delete this._disabledPrefListeners[config.id];
+ }
+
await this._disableInterventionNow(config);
}
@@ -205,6 +202,39 @@ class Interventions {
continue;
}
+ config.DISABLING_PREF = `disabled_interventions.${config.id}`;
+ const disabledPrefListener = () => {
+ navigator.locks.request("pref_check_lock", async () => {
+ const value = await browser.aboutConfigPrefs.getPref(
+ config.DISABLING_PREF
+ );
+ if (value === true) {
+ await this.disableIntervention(config);
+ console.debug(
+ `Webcompat intervention for ${config.label} disabled by pref`
+ );
+ } else {
+ await this.enableIntervention(config);
+ console.debug(
+ `Webcompat intervention for ${config.label} enabled by pref`
+ );
+ }
+ this._aboutCompatBroker.portsToAboutCompatTabs.broadcast({
+ interventionsChanged:
+ this._aboutCompatBroker.filterInterventions(whichInterventions),
+ });
+ });
+ };
+ this._disabledPrefListeners[config.id] = disabledPrefListener;
+ browser.aboutConfigPrefs.onPrefChange.addListener(
+ disabledPrefListener,
+ config.DISABLING_PREF
+ );
+
+ const disablingPrefValue = await browser.aboutConfigPrefs.getPref(
+ config.DISABLING_PREF
+ );
+
for (const intervention of config.interventions) {
intervention.enabled = false;
if (!(await this._check_for_needed_prefs(intervention))) {
@@ -246,6 +276,10 @@ class Interventions {
skipped.push(config.label);
continue;
}
+ if (disablingPrefValue === true) {
+ skipped.push(config.label);
+ continue;
+ }
try {
await this._enableInterventionNow(config);
diff --git a/browser/extensions/webcompat/tests/browser/browser.toml b/browser/extensions/webcompat/tests/browser/browser.toml
@@ -11,6 +11,7 @@ support-files = [
"smartblock_embed_test.html",
"embed_test.js",
]
+prefs = ["extensions.webcompat.disabled_interventions.test6=true","extensions.webcompat.disabled_interventions.test7=false"]
["browser_aboutcompat.js"]
skip-if = ["debug"] # disabled until bug 1961939 is fixed.
diff --git a/browser/extensions/webcompat/tests/browser/browser_aboutcompat.js b/browser/extensions/webcompat/tests/browser/browser_aboutcompat.js
@@ -1,6 +1,9 @@
"use strict";
add_task(async function test_about_compat_loads_properly() {
+ // wait for all interventions to load before testing (can be quite slow on tsan builds).
+ await WebCompatExtension.noOngoingInterventionChanges();
+
const tab = await BrowserTestUtils.openNewForegroundTab({
gBrowser,
opening: "about:compat",
diff --git a/browser/extensions/webcompat/tests/browser/browser_intervention_gating.js b/browser/extensions/webcompat/tests/browser/browser_intervention_gating.js
@@ -250,3 +250,278 @@ add_task(async function test_disabling_by_default() {
await WebCompatExtension.disableInterventions(["test3", "test4"]);
});
+
+add_task(async function test_individual_interventions_prefs() {
+ const config5 = getConfig("test5", [
+ {
+ js: ["lib/run.js"],
+ },
+ ]);
+ // the pref for this one is set to true before app-startup
+ const config6 = getConfig("test6", [
+ {
+ js: ["lib/shims.js"],
+ },
+ ]);
+ // the pref for this one is set to false before app-startup
+ const config7 = getConfig("test7", [
+ {
+ js: ["lib/custom_functions.js"],
+ },
+ ]);
+ let configs = await WebCompatExtension.updateInterventions([
+ config5,
+ config6,
+ config7,
+ ]);
+ Assert.deepEqual(
+ configs.map(c => c.active),
+ [true, false, true],
+ "The correct interventions were activated on startup"
+ );
+ Assert.deepEqual(
+ configs.map(c => c.interventions.map(i => i.enabled)),
+ [[true], [true], [true]],
+ "The correct interventions were made available on startup"
+ );
+ let reg = await WebCompatExtension.getRegisteredContentScriptsFor(["test"]);
+ Assert.deepEqual(
+ reg.map(r => r.js).flat(),
+ ["lib/run.js", "lib/custom_functions.js"],
+ "The correct content scripts were registered on startup"
+ );
+
+ Services.prefs.setBoolPref(
+ "extensions.webcompat.disabled_interventions.test5",
+ true
+ );
+ Services.prefs.setBoolPref(
+ "extensions.webcompat.disabled_interventions.test6",
+ false
+ );
+ Services.prefs.setBoolPref(
+ "extensions.webcompat.disabled_interventions.test7",
+ true
+ );
+ await WebCompatExtension.noOngoingInterventionChanges();
+ configs = await WebCompatExtension.updateInterventions([
+ config5,
+ config6,
+ config7,
+ ]);
+ Assert.deepEqual(
+ configs.map(c => c.active),
+ [false, true, false],
+ "The correct interventions were activated by pref changes"
+ );
+ Assert.deepEqual(
+ configs.map(c => c.interventions.map(i => i.enabled)),
+ [[true], [true], [true]],
+ "The correct interventions were made available by pref changes"
+ );
+ reg = await WebCompatExtension.getRegisteredContentScriptsFor(["test"]);
+ Assert.deepEqual(
+ reg.map(r => r.js).flat(),
+ ["lib/shims.js"],
+ "The correct content scripts are registered after pref changes"
+ );
+
+ Services.prefs.setBoolPref(
+ "extensions.webcompat.disabled_interventions.test5",
+ false
+ );
+ Services.prefs.setBoolPref(
+ "extensions.webcompat.disabled_interventions.test6",
+ true
+ );
+ Services.prefs.setBoolPref(
+ "extensions.webcompat.disabled_interventions.test7",
+ false
+ );
+ await WebCompatExtension.noOngoingInterventionChanges();
+ configs = await WebCompatExtension.updateInterventions([
+ config5,
+ config6,
+ config7,
+ ]);
+ Assert.deepEqual(
+ configs.map(c => c.active),
+ [true, false, true],
+ "The correct interventions were activated by pref changes"
+ );
+ Assert.deepEqual(
+ configs.map(c => c.interventions.map(i => i.enabled)),
+ [[true], [true], [true]],
+ "The correct interventions were made available by pref changes"
+ );
+ reg = await WebCompatExtension.getRegisteredContentScriptsFor(["test"]);
+ Assert.deepEqual(
+ reg.map(r => r.js).flat(),
+ ["lib/run.js", "lib/custom_functions.js"],
+ "The correct content scripts are registered after pref changes"
+ );
+
+ const tab = await BrowserTestUtils.openNewForegroundTab({
+ gBrowser,
+ opening: "about:compat",
+ waitForLoad: true,
+ });
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async function () {
+ is(
+ content.origin,
+ "moz-extension://9a310967-e580-48bf-b3e8-4eafebbc122d",
+ "Expected origin of about:compat"
+ );
+
+ await ContentTaskUtils.waitForCondition(
+ () =>
+ content.document.querySelector(
+ "#interventions tr[data-id=test5] [data-l10n-id=label-disable]"
+ ),
+ "test5 is correctly shown as disabled"
+ );
+ await ContentTaskUtils.waitForCondition(
+ () =>
+ content.document.querySelector(
+ "#interventions tr[data-id=test6] [data-l10n-id=label-enable]"
+ ),
+ "test6 is correctly shown as enabled"
+ );
+
+ // click to disable test5 and confirm
+ content.document
+ .querySelector(
+ "#interventions tr[data-id=test5] [data-l10n-id=label-disable]"
+ )
+ .click();
+ await ContentTaskUtils.waitForCondition(
+ () =>
+ content.document.querySelector(
+ "#interventions tr[data-id=test5] [data-l10n-id=label-enable]"
+ ),
+ "test5 is correctly disabled"
+ );
+
+ // click to enable test6 and confirm
+ content.document
+ .querySelector(
+ "#interventions tr[data-id=test6] [data-l10n-id=label-enable]"
+ )
+ .click();
+ await ContentTaskUtils.waitForCondition(
+ () =>
+ content.document.querySelector(
+ "#interventions tr[data-id=test6] [data-l10n-id=label-disable]"
+ ),
+ "test6 is correctly enabled"
+ );
+
+ // click to disable test7 and confirm
+ content.document
+ .querySelector(
+ "#interventions tr[data-id=test7] [data-l10n-id=label-disable]"
+ )
+ .click();
+ await ContentTaskUtils.waitForCondition(
+ () =>
+ content.document.querySelector(
+ "#interventions tr[data-id=test5] [data-l10n-id=label-enable]"
+ ),
+ "test7 is correctly disabled"
+ );
+ });
+
+ configs = [
+ await WebCompatExtension.getInterventionById("test5"),
+ await WebCompatExtension.getInterventionById("test6"),
+ await WebCompatExtension.getInterventionById("test7"),
+ ];
+ Assert.deepEqual(
+ configs.map(c => c.active),
+ [false, true, false],
+ "The correct interventions were activated by about:compat changes"
+ );
+ Assert.deepEqual(
+ configs.map(c => c.interventions.map(i => i.enabled)),
+ [[true], [true], [true]],
+ "The correct interventions were made available by about:compat changes"
+ );
+ reg = await WebCompatExtension.getRegisteredContentScriptsFor(["test"]);
+ Assert.deepEqual(
+ reg.map(r => r.js).flat(),
+ ["lib/shims.js"],
+ "The correct content scripts are registered after about:compat changes"
+ );
+
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async function () {
+ // click to re-enable test5 and confirm
+ content.document
+ .querySelector(
+ "#interventions tr[data-id=test5] [data-l10n-id=label-enable]"
+ )
+ .click();
+ await ContentTaskUtils.waitForCondition(
+ () =>
+ content.document.querySelector(
+ "#interventions tr[data-id=test5] [data-l10n-id=label-disable]"
+ ),
+ "test5 is correctly disabled"
+ );
+
+ // click to re-disable test6 and confirm
+ content.document
+ .querySelector(
+ "#interventions tr[data-id=test6] [data-l10n-id=label-disable]"
+ )
+ .click();
+ await ContentTaskUtils.waitForCondition(
+ () =>
+ content.document.querySelector(
+ "#interventions tr[data-id=test6] [data-l10n-id=label-enable]"
+ ),
+ "test6 is correctly disabled"
+ );
+
+ // click to re-enable test7 and confirm
+ content.document
+ .querySelector(
+ "#interventions tr[data-id=test7] [data-l10n-id=label-enable]"
+ )
+ .click();
+ await ContentTaskUtils.waitForCondition(
+ () =>
+ content.document.querySelector(
+ "#interventions tr[data-id=test7] [data-l10n-id=label-disable]"
+ ),
+ "test7 is correctly disabled"
+ );
+ });
+ await BrowserTestUtils.removeTab(tab);
+
+ configs = [
+ await WebCompatExtension.getInterventionById("test5"),
+ await WebCompatExtension.getInterventionById("test6"),
+ await WebCompatExtension.getInterventionById("test7"),
+ ];
+ Assert.deepEqual(
+ configs.map(c => c.active),
+ [true, false, true],
+ "The correct interventions were activated by about:compat changes"
+ );
+ Assert.deepEqual(
+ configs.map(c => c.interventions.map(i => i.enabled)),
+ [[true], [true], [true]],
+ "The correct interventions were made available by about:compat changes"
+ );
+ reg = await WebCompatExtension.getRegisteredContentScriptsFor(["test"]);
+ Assert.deepEqual(
+ reg.map(r => r.js).flat(),
+ ["lib/run.js", "lib/custom_functions.js"],
+ "The correct content scripts are registered after about:compat changes"
+ );
+
+ await WebCompatExtension.disableInterventions(["test5", "test6", "test7"]);
+ Services.prefs.clearUserPref(
+ "extensions.webcompat.disabled_interventions.test5"
+ );
+});