Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

  • This test gets skipped with pattern: os == 'linux' && os_version == '18.04' && asan OR os == 'linux' && os_version == '18.04' && tsan OR win11_2009 && asan OR tsan OR ccov && os == 'linux' OR ccov && os == 'win'
  • Manifest: browser/components/preferences/tests/browser.toml
/* eslint-env webextensions */
const PROXY_PREF = "network.proxy.type";
const HOMEPAGE_URL_PREF = "browser.startup.homepage";
const HOMEPAGE_OVERRIDE_KEY = "homepage_override";
const URL_OVERRIDES_TYPE = "url_overrides";
const NEW_TAB_KEY = "newTabURL";
const PREF_SETTING_TYPE = "prefs";
ChromeUtils.defineESModuleGetters(this, {
AboutNewTab: "resource:///modules/AboutNewTab.sys.mjs",
ExtensionSettingsStore:
"resource://gre/modules/ExtensionSettingsStore.sys.mjs",
});
XPCOMUtils.defineLazyPreferenceGetter(this, "proxyType", PROXY_PREF);
const { AddonTestUtils } = ChromeUtils.importESModule(
);
AddonTestUtils.initMochitest(this);
const { ExtensionPreferencesManager } = ChromeUtils.importESModule(
"resource://gre/modules/ExtensionPreferencesManager.sys.mjs"
);
const TEST_DIR = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
const CHROME_URL_ROOT = TEST_DIR + "/";
const PERMISSIONS_URL =
"chrome://browser/content/preferences/dialogs/sitePermissions.xhtml";
let sitePermissionsDialog;
function getSupportsFile(path) {
let cr = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(
Ci.nsIChromeRegistry
);
let uri = Services.io.newURI(CHROME_URL_ROOT + path);
let fileurl = cr.convertChromeURL(uri);
return fileurl.QueryInterface(Ci.nsIFileURL);
}
function waitForMessageChange(
element,
cb,
opts = { attributes: true, attributeFilter: ["hidden"] }
) {
return waitForMutation(element, opts, cb);
}
function getElement(id, doc = gBrowser.contentDocument) {
return doc.getElementById(id);
}
function waitForMessageHidden(messageId, doc) {
return waitForMessageChange(
getElement(messageId, doc),
target => target.hidden
);
}
function waitForMessageShown(messageId, doc) {
return waitForMessageChange(
getElement(messageId, doc),
target => !target.hidden
);
}
function waitForEnableMessage(messageId, doc) {
return waitForMessageChange(
getElement(messageId, doc),
target => target.classList.contains("extension-controlled-disabled"),
{ attributeFilter: ["class"], attributes: true }
);
}
function waitForMessageContent(messageId, l10nId, doc) {
return waitForMessageChange(
getElement(messageId, doc),
target => doc.l10n.getAttributes(target).id === l10nId,
{ childList: true }
);
}
async function openNotificationsPermissionDialog() {
let dialogOpened = promiseLoadSubDialog(PERMISSIONS_URL);
await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () {
let doc = content.document;
let settingsButton = doc.getElementById("notificationSettingsButton");
settingsButton.click();
});
sitePermissionsDialog = await dialogOpened;
await sitePermissionsDialog.document.mozSubdialogReady;
}
async function disableExtensionViaClick(labelId, disableButtonId, doc) {
let controlledLabel = doc.getElementById(labelId);
let enableMessageShown = waitForEnableMessage(labelId, doc);
doc.getElementById(disableButtonId).click();
await enableMessageShown;
let controlledDescription = controlledLabel.querySelector("description");
is(
doc.l10n.getAttributes(controlledDescription.querySelector("label")).id,
"extension-controlled-enable",
"The user is notified of how to enable the extension again."
);
// The user can dismiss the enable instructions.
let hidden = waitForMessageHidden(labelId, doc);
controlledLabel.querySelector("image:last-of-type").click();
await hidden;
}
async function reEnableExtension(addon, labelId) {
let controlledMessageShown = waitForMessageShown(labelId);
await addon.enable();
await controlledMessageShown;
}
add_task(async function testExtensionControlledHomepage() {
const ADDON_ID = "@set_homepage";
const SECOND_ADDON_ID = "@second_set_homepage";
await openPreferencesViaOpenPreferencesAPI("paneHome", { leaveOpen: true });
let homepagePref = () => Services.prefs.getCharPref(HOMEPAGE_URL_PREF);
let originalHomepagePref = homepagePref();
is(
gBrowser.currentURI.spec,
"about:preferences#home",
"#home should be in the URI for about:preferences"
);
let doc = gBrowser.contentDocument;
let homeModeEl = doc.getElementById("homeMode");
let customSettingsSection = doc.getElementById("customSettings");
is(homeModeEl.itemCount, 3, "The menu list starts with 3 options");
let promise = TestUtils.waitForCondition(
() => homeModeEl.itemCount === 4,
"wait for the addon option to be added as an option in the menu list"
);
let extension = ExtensionTestUtils.loadExtension({
useAddonManager: "permanent",
manifest: {
version: "1.0",
name: "set_homepage",
browser_specific_settings: {
gecko: {
id: ADDON_ID,
},
},
chrome_settings_overrides: { homepage: "/home.html" },
},
});
await extension.startup();
await promise;
// The homepage is set to the default and the custom settings section is hidden
is(homeModeEl.disabled, false, "The homepage menulist is enabled");
is(
customSettingsSection.hidden,
true,
"The custom settings element is hidden"
);
let addon = await AddonManager.getAddonByID(ADDON_ID);
is(
homeModeEl.value,
addon.id,
"the home select menu's value is set to the addon"
);
promise = TestUtils.waitForPrefChange(HOMEPAGE_URL_PREF);
// Set the Menu to the default value
homeModeEl.value = "0";
homeModeEl.dispatchEvent(new Event("command"));
await promise;
is(homepagePref(), originalHomepagePref, "homepage is set back to default");
let levelOfControl = await ExtensionPreferencesManager.getLevelOfControl(
addon.id,
HOMEPAGE_OVERRIDE_KEY,
PREF_SETTING_TYPE
);
is(
levelOfControl,
"not_controllable",
"getLevelOfControl returns not_controllable."
);
let setting = await ExtensionPreferencesManager.getSetting(
HOMEPAGE_OVERRIDE_KEY
);
ok(!setting.value, "the setting is not set.");
promise = TestUtils.waitForPrefChange(HOMEPAGE_URL_PREF);
// Set the menu to the addon value
homeModeEl.value = ADDON_ID;
homeModeEl.dispatchEvent(new Event("command"));
await promise;
ok(
homepagePref().startsWith("moz-extension") &&
homepagePref().endsWith("home.html"),
"Home url should be provided by the extension."
);
levelOfControl = await ExtensionPreferencesManager.getLevelOfControl(
addon.id,
HOMEPAGE_OVERRIDE_KEY,
PREF_SETTING_TYPE
);
is(
levelOfControl,
"controlled_by_this_extension",
"getLevelOfControl returns controlled_by_this_extension."
);
setting = await ExtensionPreferencesManager.getSetting(HOMEPAGE_OVERRIDE_KEY);
ok(
setting.value.startsWith("moz-extension") &&
setting.value.endsWith("home.html"),
"The setting value is the same as the extension."
);
// Add a second extension, ensure it is added to the menulist and selected.
promise = TestUtils.waitForCondition(
() => homeModeEl.itemCount == 5,
"addon option is added as an option in the menu list"
);
let secondExtension = ExtensionTestUtils.loadExtension({
useAddonManager: "permanent",
manifest: {
version: "1.0",
name: "second_set_homepage",
browser_specific_settings: {
gecko: {
id: SECOND_ADDON_ID,
},
},
chrome_settings_overrides: { homepage: "/home2.html" },
},
});
await secondExtension.startup();
await promise;
let secondAddon = await AddonManager.getAddonByID(SECOND_ADDON_ID);
is(homeModeEl.value, SECOND_ADDON_ID, "home menulist is set to the add-on");
levelOfControl = await ExtensionPreferencesManager.getLevelOfControl(
secondAddon.id,
HOMEPAGE_OVERRIDE_KEY,
PREF_SETTING_TYPE
);
is(
levelOfControl,
"controlled_by_this_extension",
"getLevelOfControl returns controlled_by_this_extension."
);
setting = await ExtensionPreferencesManager.getSetting(HOMEPAGE_OVERRIDE_KEY);
ok(
setting.value.startsWith("moz-extension") &&
setting.value.endsWith("home2.html"),
"The setting value is the same as the extension."
);
promise = TestUtils.waitForCondition(
() => homeModeEl.itemCount == 4,
"addon option is no longer an option in the menu list after disable, even if it was not selected"
);
await addon.disable();
await promise;
// Ensure that re-enabling an addon adds it back to the menulist
promise = TestUtils.waitForCondition(
() => homeModeEl.itemCount == 5,
"addon option is added again to the menulist when enabled"
);
await addon.enable();
await promise;
promise = TestUtils.waitForCondition(
() => homeModeEl.itemCount == 4,
"addon option is no longer an option in the menu list after disable"
);
await secondAddon.disable();
await promise;
promise = TestUtils.waitForCondition(
() => homeModeEl.itemCount == 5,
"addon option is added again to the menulist when enabled"
);
await secondAddon.enable();
await promise;
promise = TestUtils.waitForCondition(
() => homeModeEl.itemCount == 3,
"addon options are no longer an option in the menu list after disabling all addons"
);
await secondAddon.disable();
await addon.disable();
await promise;
is(homeModeEl.value, "0", "addon option is not selected in the menu list");
levelOfControl = await ExtensionPreferencesManager.getLevelOfControl(
secondAddon.id,
HOMEPAGE_OVERRIDE_KEY,
PREF_SETTING_TYPE
);
is(
levelOfControl,
"controllable_by_this_extension",
"getLevelOfControl returns controllable_by_this_extension."
);
setting = await ExtensionPreferencesManager.getSetting(HOMEPAGE_OVERRIDE_KEY);
ok(!setting.value, "The setting value is back to default.");
// The homepage elements are reset to their original state.
is(homepagePref(), originalHomepagePref, "homepage is set back to default");
is(homeModeEl.disabled, false, "The homepage menulist is enabled");
BrowserTestUtils.removeTab(gBrowser.selectedTab);
await extension.unload();
await secondExtension.unload();
});
add_task(async function testPrefLockedHomepage() {
const ADDON_ID = "@set_homepage";
await openPreferencesViaOpenPreferencesAPI("paneHome", { leaveOpen: true });
let doc = gBrowser.contentDocument;
is(
gBrowser.currentURI.spec,
"about:preferences#home",
"#home should be in the URI for about:preferences"
);
let homePagePref = "browser.startup.homepage";
let buttonPrefs = [
"pref.browser.homepage.disable_button.current_page",
"pref.browser.homepage.disable_button.bookmark_page",
"pref.browser.homepage.disable_button.restore_default",
];
let homeModeEl = doc.getElementById("homeMode");
let homePageInput = doc.getElementById("homePageUrl");
let prefs = Services.prefs.getDefaultBranch(null);
let mutationOpts = { attributes: true, attributeFilter: ["disabled"] };
// Helper functions.
let getButton = pref =>
doc.querySelector(`.homepage-button[preference="${pref}"`);
let waitForAllMutations = () =>
Promise.all(
buttonPrefs
.map(pref => waitForMutation(getButton(pref), mutationOpts))
.concat([
waitForMutation(homeModeEl, mutationOpts),
waitForMutation(homePageInput, mutationOpts),
])
);
let getHomepage = () =>
Services.prefs.getCharPref("browser.startup.homepage");
let originalHomepage = getHomepage();
let extensionHomepage = "https://developer.mozilla.org/";
let lockedHomepage = "http://www.yahoo.com";
let lockPrefs = () => {
buttonPrefs.forEach(pref => {
prefs.setBoolPref(pref, true);
prefs.lockPref(pref);
});
// Do the homepage last since that's the only pref that triggers a UI update.
prefs.setCharPref(homePagePref, lockedHomepage);
prefs.lockPref(homePagePref);
};
let unlockPrefs = () => {
buttonPrefs.forEach(pref => {
prefs.unlockPref(pref);
prefs.setBoolPref(pref, false);
});
// Do the homepage last since that's the only pref that triggers a UI update.
prefs.unlockPref(homePagePref);
prefs.setCharPref(homePagePref, originalHomepage);
};
// Lock or unlock prefs then wait for all mutations to finish.
// Expects a bool indicating if we should lock or unlock.
let waitForLockMutations = lock => {
let mutationsDone = waitForAllMutations();
if (lock) {
lockPrefs();
} else {
unlockPrefs();
}
return mutationsDone;
};
Assert.notEqual(
originalHomepage,
extensionHomepage,
"The extension will change the homepage"
);
// Install an extension that sets the homepage to MDN.
let promise = TestUtils.waitForPrefChange(HOMEPAGE_URL_PREF);
let extension = ExtensionTestUtils.loadExtension({
useAddonManager: "permanent",
manifest: {
version: "1.0",
name: "set_homepage",
browser_specific_settings: {
gecko: {
id: ADDON_ID,
},
},
chrome_settings_overrides: { homepage: "https://developer.mozilla.org/" },
},
});
await extension.startup();
await promise;
// Check that everything is still disabled, homepage didn't change.
is(
getHomepage(),
extensionHomepage,
"The reported homepage is set by the extension"
);
is(
homePageInput.value,
extensionHomepage,
"The homepage is set by the extension"
);
// Lock all of the prefs, wait for the UI to update.
await waitForLockMutations(true);
// Check that everything is now disabled.
is(getHomepage(), lockedHomepage, "The reported homepage is set by the pref");
is(homePageInput.value, lockedHomepage, "The homepage is set by the pref");
is(
homePageInput.disabled,
true,
"The homepage is disabed when the pref is locked"
);
buttonPrefs.forEach(pref => {
is(
getButton(pref).disabled,
true,
`The ${pref} button is disabled when locked`
);
});
let levelOfControl = await ExtensionPreferencesManager.getLevelOfControl(
ADDON_ID,
HOMEPAGE_OVERRIDE_KEY,
PREF_SETTING_TYPE
);
is(
levelOfControl,
"not_controllable",
"getLevelOfControl returns not_controllable, the pref is locked."
);
// Verify that the UI is selecting the extension's Id in the menulist
let unlockedPromise = TestUtils.waitForCondition(
() => homeModeEl.value == ADDON_ID,
"Homepage menulist value is equal to the extension ID"
);
// Unlock the prefs, wait for the UI to update.
unlockPrefs();
await unlockedPromise;
is(
homeModeEl.disabled,
false,
"the home select element is not disabled when the pref is not locked"
);
is(
homePageInput.disabled,
false,
"The homepage is enabled when the pref is unlocked"
);
is(
getHomepage(),
extensionHomepage,
"The homepage is reset to extension page"
);
levelOfControl = await ExtensionPreferencesManager.getLevelOfControl(
ADDON_ID,
HOMEPAGE_OVERRIDE_KEY,
PREF_SETTING_TYPE
);
is(
levelOfControl,
"controlled_by_this_extension",
"getLevelOfControl returns controlled_by_this_extension after prefs are unlocked."
);
let setting = await ExtensionPreferencesManager.getSetting(
HOMEPAGE_OVERRIDE_KEY
);
is(
setting.value,
extensionHomepage,
"The setting value is equal to the extensionHomepage."
);
// Uninstall the add-on.
promise = TestUtils.waitForPrefChange(HOMEPAGE_URL_PREF);
await extension.unload();
await promise;
setting = await ExtensionPreferencesManager.getSetting(HOMEPAGE_OVERRIDE_KEY);
ok(!setting, "The setting is gone after the addon is uninstalled.");
// Check that everything is now enabled again.
is(
getHomepage(),
originalHomepage,
"The reported homepage is reset to original value"
);
is(homePageInput.value, "", "The homepage is empty");
is(
homePageInput.disabled,
false,
"The homepage is enabled after clearing lock"
);
is(
homeModeEl.disabled,
false,
"Homepage menulist is enabled after clearing lock"
);
buttonPrefs.forEach(pref => {
is(
getButton(pref).disabled,
false,
`The ${pref} button is enabled when unlocked`
);
});
// Lock the prefs without an extension.
await waitForLockMutations(true);
// Check that everything is now disabled.
is(getHomepage(), lockedHomepage, "The reported homepage is set by the pref");
is(homePageInput.value, lockedHomepage, "The homepage is set by the pref");
is(
homePageInput.disabled,
true,
"The homepage is disabed when the pref is locked"
);
is(
homeModeEl.disabled,
true,
"Homepage menulist is disabled when pref is locked"
);
buttonPrefs.forEach(pref => {
is(
getButton(pref).disabled,
true,
`The ${pref} button is disabled when locked`
);
});
// Unlock the prefs without an extension.
await waitForLockMutations(false);
// Check that everything is enabled again.
is(
getHomepage(),
originalHomepage,
"The homepage is reset to the original value"
);
is(homePageInput.value, "", "The homepage is clear after being unlocked");
is(
homePageInput.disabled,
false,
"The homepage is enabled after clearing lock"
);
is(
homeModeEl.disabled,
false,
"Homepage menulist is enabled after clearing lock"
);
buttonPrefs.forEach(pref => {
is(
getButton(pref).disabled,
false,
`The ${pref} button is enabled when unlocked`
);
});
BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
add_task(async function testExtensionControlledNewTab() {
const ADDON_ID = "@set_newtab";
const SECOND_ADDON_ID = "@second_set_newtab";
const DEFAULT_NEWTAB = "about:newtab";
const NEWTAB_CONTROLLED_PREF = "browser.newtab.extensionControlled";
await openPreferencesViaOpenPreferencesAPI("paneHome", { leaveOpen: true });
is(
gBrowser.currentURI.spec,
"about:preferences#home",
"#home should be in the URI for about:preferences"
);
let doc = gBrowser.contentDocument;
let newTabMenuList = doc.getElementById("newTabMode");
// The new tab page is set to the default.
is(AboutNewTab.newTabURL, DEFAULT_NEWTAB, "new tab is set to default");
let promise = TestUtils.waitForCondition(
() => newTabMenuList.itemCount == 3,
"addon option is added as an option in the menu list"
);
// Install an extension that will set the new tab page.
let extension = ExtensionTestUtils.loadExtension({
useAddonManager: "permanent",
manifest: {
version: "1.0",
name: "set_newtab",
browser_specific_settings: {
gecko: {
id: ADDON_ID,
},
},
chrome_url_overrides: { newtab: "/newtab.html" },
},
});
await extension.startup();
await promise;
let addon = await AddonManager.getAddonByID(ADDON_ID);
is(newTabMenuList.value, ADDON_ID, "New tab menulist is set to the add-on");
let levelOfControl = await ExtensionPreferencesManager.getLevelOfControl(
addon.id,
NEW_TAB_KEY,
URL_OVERRIDES_TYPE
);
is(
levelOfControl,
"controlled_by_this_extension",
"getLevelOfControl returns controlled_by_this_extension."
);
let setting = ExtensionSettingsStore.getSetting(
URL_OVERRIDES_TYPE,
NEW_TAB_KEY
);
ok(
setting.value.startsWith("moz-extension") &&
setting.value.endsWith("newtab.html"),
"The url_overrides is set by this extension"
);
promise = TestUtils.waitForPrefChange(NEWTAB_CONTROLLED_PREF);
// Set the menu to the default value
newTabMenuList.value = "0";
newTabMenuList.dispatchEvent(new Event("command"));
await promise;
let newTabPref = Services.prefs.getBoolPref(NEWTAB_CONTROLLED_PREF, false);
is(newTabPref, false, "the new tab is not controlled");
levelOfControl = await ExtensionPreferencesManager.getLevelOfControl(
addon.id,
NEW_TAB_KEY,
URL_OVERRIDES_TYPE
);
is(
levelOfControl,
"not_controllable",
"getLevelOfControl returns not_controllable."
);
setting = ExtensionSettingsStore.getSetting(URL_OVERRIDES_TYPE, NEW_TAB_KEY);
ok(!setting.value, "The url_overrides is not set by this extension");
promise = TestUtils.waitForPrefChange(NEWTAB_CONTROLLED_PREF);
// Set the menu to a the addon value
newTabMenuList.value = ADDON_ID;
newTabMenuList.dispatchEvent(new Event("command"));
await promise;
newTabPref = Services.prefs.getBoolPref(NEWTAB_CONTROLLED_PREF, false);
is(newTabPref, true, "the new tab is controlled");
// Add a second extension, ensure it is added to the menulist and selected.
promise = TestUtils.waitForCondition(
() => newTabMenuList.itemCount == 4,
"addon option is added as an option in the menu list"
);
let secondExtension = ExtensionTestUtils.loadExtension({
useAddonManager: "permanent",
manifest: {
version: "1.0",
name: "second_set_newtab",
browser_specific_settings: {
gecko: {
id: SECOND_ADDON_ID,
},
},
chrome_url_overrides: { newtab: "/newtab2.html" },
},
});
await secondExtension.startup();
await promise;
let secondAddon = await AddonManager.getAddonByID(SECOND_ADDON_ID);
is(
newTabMenuList.value,
SECOND_ADDON_ID,
"New tab menulist is set to the add-on"
);
levelOfControl = await ExtensionPreferencesManager.getLevelOfControl(
secondAddon.id,
NEW_TAB_KEY,
URL_OVERRIDES_TYPE
);
is(
levelOfControl,
"controlled_by_this_extension",
"getLevelOfControl returns controlled_by_this_extension."
);
setting = ExtensionSettingsStore.getSetting(URL_OVERRIDES_TYPE, NEW_TAB_KEY);
ok(
setting.value.startsWith("moz-extension") &&
setting.value.endsWith("newtab2.html"),
"The url_overrides is set by the second extension"
);
promise = TestUtils.waitForCondition(
() => newTabMenuList.itemCount == 3,
"addon option is no longer an option in the menu list after disable, even if it was not selected"
);
await addon.disable();
await promise;
// Ensure that re-enabling an addon adds it back to the menulist
promise = TestUtils.waitForCondition(
() => newTabMenuList.itemCount == 4,
"addon option is added again to the menulist when enabled"
);
await addon.enable();
await promise;
promise = TestUtils.waitForCondition(
() => newTabMenuList.itemCount == 3,
"addon option is no longer an option in the menu list after disable"
);
await secondAddon.disable();
await promise;
promise = TestUtils.waitForCondition(
() => newTabMenuList.itemCount == 4,
"addon option is added again to the menulist when enabled"
);
await secondAddon.enable();
await promise;
promise = TestUtils.waitForCondition(
() => newTabMenuList.itemCount == 2,
"addon options are all removed after disabling all"
);
await addon.disable();
await secondAddon.disable();
await promise;
is(
AboutNewTab.newTabURL,
DEFAULT_NEWTAB,
"new tab page is set back to default"
);
// Cleanup the tabs and add-on.
BrowserTestUtils.removeTab(gBrowser.selectedTab);
await extension.unload();
await secondExtension.unload();
});
add_task(async function testExtensionControlledWebNotificationsPermission() {
let manifest = {
manifest_version: 2,
name: "TestExtension",
version: "1.0",
description: "Testing WebNotificationsDisable",
browser_specific_settings: { gecko: { id: "@web_notifications_disable" } },
permissions: ["browserSettings"],
browser_action: {
default_title: "Testing",
},
};
await openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true });
await openNotificationsPermissionDialog();
let doc = sitePermissionsDialog.document;
let extensionControlledContent = doc.getElementById(
"browserNotificationsPermissionExtensionContent"
);
// Test that extension content is initially hidden.
ok(
extensionControlledContent.hidden,
"Extension content is initially hidden"
);
// Install an extension that will disable web notifications permission.
let messageShown = waitForMessageShown(
"browserNotificationsPermissionExtensionContent",
doc
);
let extension = ExtensionTestUtils.loadExtension({
manifest,
useAddonManager: "permanent",
background() {
browser.browserSettings.webNotificationsDisabled.set({ value: true });
browser.test.sendMessage("load-extension");
},
});
await extension.startup();
await extension.awaitMessage("load-extension");
await messageShown;
let controlledDesc = extensionControlledContent.querySelector("description");
Assert.deepEqual(
doc.l10n.getAttributes(controlledDesc),
{
id: "extension-controlling-web-notifications",
args: {
name: "TestExtension",
},
},
"The user is notified that an extension is controlling the web notifications permission"
);
is(
extensionControlledContent.hidden,
false,
"The extension controlled row is not hidden"
);
// Disable the extension.
doc.getElementById("disableNotificationsPermissionExtension").click();
// Verify the user is notified how to enable the extension.
await waitForEnableMessage(extensionControlledContent.id, doc);
is(
doc.l10n.getAttributes(controlledDesc.querySelector("label")).id,
"extension-controlled-enable",
"The user is notified of how to enable the extension again"
);
// Verify the enable message can be dismissed.
let hidden = waitForMessageHidden(extensionControlledContent.id, doc);
let dismissButton = controlledDesc.querySelector("image:last-of-type");
dismissButton.click();
await hidden;
// Verify that the extension controlled content in hidden again.
is(
extensionControlledContent.hidden,
true,
"The extension controlled row is now hidden"
);
await extension.unload();
BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
add_task(async function testExtensionControlledHomepageUninstalledAddon() {
async function checkHomepageEnabled() {
await openPreferencesViaOpenPreferencesAPI("paneHome", { leaveOpen: true });
let doc = gBrowser.contentDocument;
is(
gBrowser.currentURI.spec,
"about:preferences#home",
"#home should be in the URI for about:preferences"
);
// The homepage is enabled.
let homepageInput = doc.getElementById("homePageUrl");
is(homepageInput.disabled, false, "The homepage input is enabled");
is(homepageInput.value, "", "The homepage input is empty");
BrowserTestUtils.removeTab(gBrowser.selectedTab);
}
await ExtensionSettingsStore.initialize();
// Verify the setting isn't reported as controlled and the inputs are enabled.
is(
ExtensionSettingsStore.getSetting("prefs", "homepage_override"),
null,
"The homepage_override is not set"
);
await checkHomepageEnabled();
// Disarm any pending writes before we modify the JSONFile directly.
await ExtensionSettingsStore._reloadFile(false);
// Write out a bad store file.
let storeData = {
prefs: {
homepage_override: {
initialValue: "",
precedenceList: [
{
id: "bad@mochi.test",
installDate: 1508802672,
enabled: true,
},
],
},
},
};
let jsonFileName = "extension-settings.json";
let storePath = PathUtils.join(PathUtils.profileDir, jsonFileName);
await IOUtils.writeUTF8(storePath, JSON.stringify(storeData));
// Reload the ExtensionSettingsStore so it will read the file on disk. Don't
// finalize the current store since it will overwrite our file.
await ExtensionSettingsStore._reloadFile(false);
// Verify that the setting is reported as set, but the homepage is still enabled
// since there is no matching installed extension.
is(
ExtensionSettingsStore.getSetting("prefs", "homepage_override").value,
"The homepage_override appears to be set"
);
await checkHomepageEnabled();
// Remove the bad store file that we used.
await IOUtils.remove(storePath);
// Reload the ExtensionSettingsStore again so it clears the data we added.
// Don't finalize the current store since it will write out the bad data.
await ExtensionSettingsStore._reloadFile(false);
is(
ExtensionSettingsStore.getSetting("prefs", "homepage_override"),
null,
"The ExtensionSettingsStore is left empty."
);
});
add_task(async function testExtensionControlledTrackingProtection() {
const TP_PREF = "privacy.trackingprotection.enabled";
const TP_DEFAULT = false;
const EXTENSION_ID = "@set_tp";
const CONTROLLED_LABEL_ID =
"contentBlockingTrackingProtectionExtensionContentLabel";
const CONTROLLED_BUTTON_ID =
"contentBlockingDisableTrackingProtectionExtension";
let tpEnabledPref = () => Services.prefs.getBoolPref(TP_PREF);
await SpecialPowers.pushPrefEnv({ set: [[TP_PREF, TP_DEFAULT]] });
function background() {
browser.privacy.websites.trackingProtectionMode.set({ value: "always" });
}
function verifyState(isControlled) {
is(tpEnabledPref(), isControlled, "TP pref is set to the expected value.");
let controlledLabel = doc.getElementById(CONTROLLED_LABEL_ID);
let controlledButton = doc.getElementById(CONTROLLED_BUTTON_ID);
is(
controlledLabel.hidden,
!isControlled,
"The extension controlled row's visibility is as expected."
);
is(
controlledButton.hidden,
!isControlled,
"The disable extension button's visibility is as expected."
);
if (isControlled) {
let controlledDesc = controlledLabel.querySelector("description");
Assert.deepEqual(
doc.l10n.getAttributes(controlledDesc),
{
id: "extension-controlling-websites-content-blocking-all-trackers",
args: {
name: "set_tp",
},
},
"The user is notified that an extension is controlling TP."
);
}
is(
doc.getElementById("trackingProtectionMenu").disabled,
isControlled,
"TP control is enabled."
);
}
await openPreferencesViaOpenPreferencesAPI("panePrivacy", {
leaveOpen: true,
});
let doc = gBrowser.contentDocument;
is(
gBrowser.currentURI.spec,
"about:preferences#privacy",
"#privacy should be in the URI for about:preferences"
);
verifyState(false);
// Install an extension that sets Tracking Protection.
let extension = ExtensionTestUtils.loadExtension({
useAddonManager: "permanent",
manifest: {
name: "set_tp",
browser_specific_settings: { gecko: { id: EXTENSION_ID } },
permissions: ["privacy"],
},
background,
});
let messageShown = waitForMessageShown(CONTROLLED_LABEL_ID);
await extension.startup();
await messageShown;
let addon = await AddonManager.getAddonByID(EXTENSION_ID);
verifyState(true);
await disableExtensionViaClick(
CONTROLLED_LABEL_ID,
CONTROLLED_BUTTON_ID,
doc
);
verifyState(false);
// Enable the extension so we get the UNINSTALL event, which is needed by
// ExtensionPreferencesManager to clean up properly.
// TODO: BUG 1408226
await reEnableExtension(addon, CONTROLLED_LABEL_ID);
await extension.unload();
BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
add_task(async function testExtensionControlledPasswordManager() {
const PASSWORD_MANAGER_ENABLED_PREF = "signon.rememberSignons";
const PASSWORD_MANAGER_ENABLED_DEFAULT = true;
const CONTROLLED_BUTTON_ID = "disablePasswordManagerExtension";
const CONTROLLED_LABEL_ID = "passwordManagerExtensionContent";
const EXTENSION_ID = "@remember_signons";
let manifest = {
manifest_version: 2,
name: "testPasswordManagerExtension",
version: "1.0",
description: "Testing rememberSignons",
browser_specific_settings: { gecko: { id: EXTENSION_ID } },
permissions: ["privacy"],
browser_action: {
default_title: "Testing rememberSignons",
},
};
let passwordManagerEnabledPref = () =>
Services.prefs.getBoolPref(PASSWORD_MANAGER_ENABLED_PREF);
await SpecialPowers.pushPrefEnv({
set: [[PASSWORD_MANAGER_ENABLED_PREF, PASSWORD_MANAGER_ENABLED_DEFAULT]],
});
is(
passwordManagerEnabledPref(),
true,
"Password manager is enabled by default."
);
function verifyState(isControlled) {
is(
passwordManagerEnabledPref(),
!isControlled,
"Password manager pref is set to the expected value."
);
let controlledLabel =
gBrowser.contentDocument.getElementById(CONTROLLED_LABEL_ID);
let controlledButton =
gBrowser.contentDocument.getElementById(CONTROLLED_BUTTON_ID);
is(
controlledLabel.hidden,
!isControlled,
"The extension's controlled row visibility is as expected."
);
is(
controlledButton.hidden,
!isControlled,
"The extension's controlled button visibility is as expected."
);
if (isControlled) {
let controlledDesc = controlledLabel.querySelector("description");
Assert.deepEqual(
gBrowser.contentDocument.l10n.getAttributes(controlledDesc),
{
id: "extension-controlling-password-saving",
args: {
name: "testPasswordManagerExtension",
},
},
"The user is notified that an extension is controlling the remember signons pref."
);
}
}
await openPreferencesViaOpenPreferencesAPI("panePrivacy", {
leaveOpen: true,
});
info("Verify that no extension is controlling the password manager pref.");
verifyState(false);
let extension = ExtensionTestUtils.loadExtension({
manifest,
useAddonManager: "permanent",
background() {
browser.privacy.services.passwordSavingEnabled.set({ value: false });
},
});
let messageShown = waitForMessageShown(CONTROLLED_LABEL_ID);
await extension.startup();
await messageShown;
info(
"Verify that the test extension is controlling the password manager pref."
);
verifyState(true);
info("Verify that the extension shows as controlled when loaded again.");
BrowserTestUtils.removeTab(gBrowser.selectedTab);
await openPreferencesViaOpenPreferencesAPI("panePrivacy", {
leaveOpen: true,
});
verifyState(true);
await disableExtensionViaClick(
CONTROLLED_LABEL_ID,
CONTROLLED_BUTTON_ID,
gBrowser.contentDocument
);
info(
"Verify that disabling the test extension removes the lock on the password manager pref."
);
verifyState(false);
await extension.unload();
BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
add_task(async function testExtensionControlledProxyConfig() {
const proxySvc = Ci.nsIProtocolProxyService;
const PROXY_DEFAULT = proxySvc.PROXYCONFIG_SYSTEM;
const EXTENSION_ID = "@set_proxy";
const CONTROLLED_SECTION_ID = "proxyExtensionContent";
const CONTROLLED_BUTTON_ID = "disableProxyExtension";
const CONNECTION_SETTINGS_DESC_ID = "connectionSettingsDescription";
const PANEL_URL =
"chrome://browser/content/preferences/dialogs/connection.xhtml";
await SpecialPowers.pushPrefEnv({ set: [[PROXY_PREF, PROXY_DEFAULT]] });
function background() {
browser.proxy.settings.set({ value: { proxyType: "none" } });
}
function expectedConnectionSettingsMessage(doc, isControlled) {
return isControlled
? "extension-controlling-proxy-config"
: "network-proxy-connection-description";
}
function connectionSettingsMessagePromise(doc, isControlled) {
return waitForMessageContent(
CONNECTION_SETTINGS_DESC_ID,
expectedConnectionSettingsMessage(doc, isControlled),
doc
);
}
function verifyProxyState(doc, isControlled) {
let isPanel = doc.getElementById(CONTROLLED_BUTTON_ID);
is(
proxyType === proxySvc.PROXYCONFIG_DIRECT,
isControlled,
"Proxy pref is set to the expected value."
);
if (isPanel) {
let controlledSection = doc.getElementById(CONTROLLED_SECTION_ID);
is(
controlledSection.hidden,
!isControlled,
"The extension controlled row's visibility is as expected."
);
if (isPanel) {
is(
doc.getElementById(CONTROLLED_BUTTON_ID).hidden,
!isControlled,
"The disable extension button's visibility is as expected."
);
}
if (isControlled) {
let controlledDesc = controlledSection.querySelector("description");
Assert.deepEqual(
doc.l10n.getAttributes(controlledDesc),
{
id: "extension-controlling-proxy-config",
args: {
name: "set_proxy",
},
},
"The user is notified that an extension is controlling proxy settings."
);
}
function getProxyControls() {
let controlGroup = doc.getElementById("networkProxyType");
let manualControlContainer = controlGroup.querySelector("#proxy-grid");
return {
manualControls: [
...manualControlContainer.querySelectorAll(
"label[data-l10n-id]:not([control=networkProxyNone])"
),
...manualControlContainer.querySelectorAll("input"),
...manualControlContainer.querySelectorAll("checkbox"),
...doc.querySelectorAll("#networkProxySOCKSVersion > radio"),
],
pacControls: [doc.getElementById("networkProxyAutoconfigURL")],
otherControls: [
doc.querySelector("label[control=networkProxyNone]"),
doc.getElementById("networkProxyNone"),
...controlGroup.querySelectorAll(":scope > radio"),
...doc.querySelectorAll("#ConnectionsDialogPane > checkbox"),
],
};
}
let controlState = isControlled ? "disabled" : "enabled";
let controls = getProxyControls();
for (let element of controls.manualControls) {
let disabled =
isControlled || proxyType !== proxySvc.PROXYCONFIG_MANUAL;
is(
element.disabled,
disabled,
`Manual proxy controls should be ${controlState} - control: ${element.outerHTML}.`
);
}
for (let element of controls.pacControls) {
let disabled = isControlled || proxyType !== proxySvc.PROXYCONFIG_PAC;
is(
element.disabled,
disabled,
`PAC proxy controls should be ${controlState} - control: ${element.outerHTML}.`
);
}
for (let element of controls.otherControls) {
is(
element.disabled,
isControlled,
`Other proxy controls should be ${controlState} - control: ${element.outerHTML}.`
);
}
} else {
let elem = doc.getElementById(CONNECTION_SETTINGS_DESC_ID);
is(
doc.l10n.getAttributes(elem).id,
expectedConnectionSettingsMessage(doc, isControlled),
"The connection settings description is as expected."
);
}
}
async function reEnableProxyExtension(addon) {
let messageChanged = connectionSettingsMessagePromise(mainDoc, true);
await addon.enable();
await messageChanged;
}
async function openProxyPanel() {
let panel = await openAndLoadSubDialog(PANEL_URL);
let closingPromise = BrowserTestUtils.waitForEvent(
panel.document.getElementById("ConnectionsDialog"),
"dialogclosing"
);
ok(panel, "Proxy panel opened.");
return { panel, closingPromise };
}
async function closeProxyPanel(panelObj) {
let dialog = panelObj.panel.document.getElementById("ConnectionsDialog");
dialog.cancelDialog();
let panelClosingEvent = await panelObj.closingPromise;
ok(panelClosingEvent, "Proxy panel closed.");
}
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {
leaveOpen: true,
});
let mainDoc = gBrowser.contentDocument;
is(
gBrowser.currentURI.spec,
"about:preferences#general",
"#general should be in the URI for about:preferences"
);
verifyProxyState(mainDoc, false);
// Open the connections panel.
let panelObj = await openProxyPanel();
let panelDoc = panelObj.panel.document;
verifyProxyState(panelDoc, false);
await closeProxyPanel(panelObj);
verifyProxyState(mainDoc, false);
// Install an extension that controls proxy settings. The extension needs
// incognitoOverride because controlling the proxy.settings requires private
// browsing access.
let extension = ExtensionTestUtils.loadExtension({
incognitoOverride: "spanning",
useAddonManager: "permanent",
manifest: {
name: "set_proxy",
browser_specific_settings: { gecko: { id: EXTENSION_ID } },
permissions: ["proxy"],
},
background,
});
let messageChanged = connectionSettingsMessagePromise(mainDoc, true);
await extension.startup();
await messageChanged;
let addon = await AddonManager.getAddonByID(EXTENSION_ID);
verifyProxyState(mainDoc, true);
messageChanged = connectionSettingsMessagePromise(mainDoc, false);
panelObj = await openProxyPanel();
panelDoc = panelObj.panel.document;
verifyProxyState(panelDoc, true);
await disableExtensionViaClick(
CONTROLLED_SECTION_ID,
CONTROLLED_BUTTON_ID,
panelDoc
);
verifyProxyState(panelDoc, false);
await closeProxyPanel(panelObj);
await messageChanged;
verifyProxyState(mainDoc, false);
await reEnableProxyExtension(addon);
verifyProxyState(mainDoc, true);
messageChanged = connectionSettingsMessagePromise(mainDoc, false);
panelObj = await openProxyPanel();
panelDoc = panelObj.panel.document;
verifyProxyState(panelDoc, true);
await disableExtensionViaClick(
CONTROLLED_SECTION_ID,
CONTROLLED_BUTTON_ID,
panelDoc
);
verifyProxyState(panelDoc, false);
await closeProxyPanel(panelObj);
await messageChanged;
verifyProxyState(mainDoc, false);
// Enable the extension so we get the UNINSTALL event, which is needed by
// ExtensionPreferencesManager to clean up properly.
// TODO: BUG 1408226
await reEnableProxyExtension(addon);
await extension.unload();
BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
// Test that the newtab menu selection is correct when loading about:preferences
add_task(async function testMenuSyncFromPrefs() {
const DEFAULT_NEWTAB = "about:newtab";
await openPreferencesViaOpenPreferencesAPI("paneHome", { leaveOpen: true });
is(
gBrowser.currentURI.spec,
"about:preferences#home",
"#home should be in the URI for about:preferences"
);
let doc = gBrowser.contentDocument;
let newTabMenuList = doc.getElementById("newTabMode");
// The new tab page is set to the default.
is(AboutNewTab.newTabURL, DEFAULT_NEWTAB, "new tab is set to default");
is(newTabMenuList.value, "0", "New tab menulist is set to the default");
newTabMenuList.value = "1";
newTabMenuList.dispatchEvent(new Event("command"));
is(newTabMenuList.value, "1", "New tab menulist is set to blank");
gBrowser.reloadTab(gBrowser.selectedTab);
await TestUtils.waitForCondition(
() => gBrowser.contentDocument.getElementById("newTabMode"),
"wait until element exists in new contentDoc"
);
is(
gBrowser.contentDocument.getElementById("newTabMode").value,
"1",
"New tab menulist is still set to blank"
);
// Cleanup
newTabMenuList.value = "0";
newTabMenuList.dispatchEvent(new Event("command"));
is(AboutNewTab.newTabURL, DEFAULT_NEWTAB, "new tab is set to default");
BrowserTestUtils.removeTab(gBrowser.selectedTab);
});