forked from mirrors/gecko-dev
Differential Revision: https://phabricator.services.mozilla.com/D18981 --HG-- extra : moz-landing-system : lando
909 lines
35 KiB
JavaScript
909 lines
35 KiB
JavaScript
/* eslint-env webextensions */
|
|
|
|
const PROXY_PREF = "network.proxy.type";
|
|
|
|
ChromeUtils.defineModuleGetter(this, "ExtensionSettingsStore",
|
|
"resource://gre/modules/ExtensionSettingsStore.jsm");
|
|
XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
|
|
"@mozilla.org/browser/aboutnewtab-service;1",
|
|
"nsIAboutNewTabService");
|
|
XPCOMUtils.defineLazyPreferenceGetter(this, "proxyType", PROXY_PREF);
|
|
|
|
const TEST_DIR = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
|
|
const CHROME_URL_ROOT = TEST_DIR + "/";
|
|
const PERMISSIONS_URL = "chrome://browser/content/preferences/sitePermissions.xul";
|
|
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 installAddon(xpiName) {
|
|
let filePath = getSupportsFile(`addons/${xpiName}`).file;
|
|
return new Promise(async (resolve, reject) => {
|
|
let install = await AddonManager.getInstallForFile(filePath);
|
|
if (!install) {
|
|
throw new Error(`An install was not created for ${filePath}`);
|
|
}
|
|
install.addListener({
|
|
onDownloadFailed: reject,
|
|
onDownloadCancelled: reject,
|
|
onInstallFailed: reject,
|
|
onInstallCancelled: reject,
|
|
onInstallEnded: resolve,
|
|
});
|
|
install.install();
|
|
});
|
|
}
|
|
|
|
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 ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
|
|
let doc = content.document;
|
|
let settingsButton = doc.getElementById("notificationSettingsButton");
|
|
settingsButton.click();
|
|
});
|
|
|
|
sitePermissionsDialog = await dialogOpened;
|
|
await sitePermissionsDialog.document.mozSubdialogReady;
|
|
}
|
|
|
|
add_task(async function testExtensionControlledHomepage() {
|
|
await openPreferencesViaOpenPreferencesAPI("paneHome", {leaveOpen: true});
|
|
// eslint-disable-next-line mozilla/no-cpows-in-tests
|
|
let doc = gBrowser.contentDocument;
|
|
is(gBrowser.currentURI.spec, "about:preferences#home",
|
|
"#home should be in the URI for about:preferences");
|
|
let homepagePref = () => Services.prefs.getCharPref("browser.startup.homepage");
|
|
let originalHomepagePref = homepagePref();
|
|
let extensionHomepage = "https://developer.mozilla.org/";
|
|
let controlledContent = doc.getElementById("browserHomePageExtensionContent");
|
|
|
|
let homeModeEl = doc.getElementById("homeMode");
|
|
let customSettingsSection = doc.getElementById("customSettings");
|
|
|
|
// The homepage is set to the default and the custom settings section is hidden
|
|
ok(originalHomepagePref != extensionHomepage, "homepage is empty by default");
|
|
is(homeModeEl.disabled, false, "The homepage menulist is enabled");
|
|
is(customSettingsSection.hidden, true, "The custom settings element is hidden");
|
|
is(controlledContent.hidden, true, "The extension controlled row is hidden");
|
|
|
|
// Install an extension that will set the homepage.
|
|
let promise = waitForMessageShown("browserHomePageExtensionContent");
|
|
await installAddon("set_homepage.xpi");
|
|
await promise;
|
|
|
|
// The homepage has been set by the extension, the user is notified and it isn't editable.
|
|
let controlledLabel = controlledContent.querySelector("description");
|
|
is(homepagePref(), extensionHomepage, "homepage is set by extension");
|
|
Assert.deepEqual(doc.l10n.getAttributes(controlledLabel), {
|
|
id: "extension-controlled-homepage-override",
|
|
args: {
|
|
name: "set_homepage",
|
|
},
|
|
}, "The user is notified that an extension is controlling the homepage");
|
|
is(controlledContent.hidden, false, "The extension controlled row is hidden");
|
|
is(homeModeEl.disabled, true, "The homepage input is disabled");
|
|
|
|
// Disable the extension.
|
|
let enableMessageShown = waitForEnableMessage(controlledContent.id);
|
|
doc.getElementById("disableHomePageExtension").click();
|
|
await enableMessageShown;
|
|
|
|
// The user is notified how to enable the extension.
|
|
is(doc.l10n.getAttributes(controlledLabel.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("browserHomePageExtensionContent");
|
|
controlledLabel.querySelector("image:last-of-type").click();
|
|
await hidden;
|
|
|
|
// 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");
|
|
is(controlledContent.hidden, true, "The extension controlled row is hidden");
|
|
|
|
// Cleanup the add-on and tab.
|
|
let addon = await AddonManager.getAddonByID("@set_homepage");
|
|
// Enable the extension so we get the UNINSTALL event, which is needed by
|
|
// ExtensionPreferencesManager to clean up properly.
|
|
// FIXME: See https://bugzilla.mozilla.org/show_bug.cgi?id=1408226.
|
|
promise = waitForMessageShown("browserHomePageExtensionContent");
|
|
await addon.enable();
|
|
await promise;
|
|
// Do the uninstall now that the enable code has been run.
|
|
await addon.uninstall();
|
|
BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
|
});
|
|
|
|
add_task(async function testPrefLockedHomepage() {
|
|
await openPreferencesViaOpenPreferencesAPI("paneHome", {leaveOpen: true});
|
|
// eslint-disable-next-line mozilla/no-cpows-in-tests
|
|
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"]};
|
|
let controlledContent = doc.getElementById("browserHomePageExtensionContent");
|
|
|
|
// 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);
|
|
};
|
|
|
|
ok(originalHomepage != extensionHomepage, "The extension will change the homepage");
|
|
|
|
// Install an extension that sets the homepage to MDN.
|
|
let promise = waitForMessageShown(controlledContent.id);
|
|
await installAddon("set_homepage.xpi");
|
|
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");
|
|
is(homePageInput.disabled, true, "Homepage custom input is disabled when set by extension");
|
|
is(homeModeEl.disabled, true, "Homepage menulist is disabled when set by extension");
|
|
buttonPrefs.forEach(pref => {
|
|
is(getButton(pref).disabled, true, `${pref} is disabled when set by extension`);
|
|
});
|
|
is(controlledContent.hidden, false, "The extension controlled message is shown");
|
|
|
|
// Lock all of the prefs, wait for the UI to update.
|
|
let messageHidden = waitForMessageHidden(controlledContent.id);
|
|
|
|
lockPrefs();
|
|
await messageHidden;
|
|
|
|
// 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 the pref is locked");
|
|
|
|
buttonPrefs.forEach(pref => {
|
|
is(getButton(pref).disabled, true, `The ${pref} button is disabled when locked`);
|
|
});
|
|
is(controlledContent.hidden, true, "The extension controlled message is hidden when locked");
|
|
|
|
// Unlock the prefs, wait for the UI to update.
|
|
let messageShown = waitForMessageShown(controlledContent.id);
|
|
unlockPrefs();
|
|
await messageShown;
|
|
|
|
// Verify that the UI is showing the extension's settings.
|
|
is(homePageInput.value, extensionHomepage, "The homepage is set by the extension");
|
|
is(homePageInput.disabled, true, "Homepage is disabled when set by extension");
|
|
is(homeModeEl.disabled, true, "Homepage menulist is disabled when set by extension");
|
|
buttonPrefs.forEach(pref => {
|
|
is(getButton(pref).disabled, true, `${pref} is disabled when set by extension`);
|
|
});
|
|
is(controlledContent.hidden, false, "The extension controlled message is shown when unlocked");
|
|
|
|
// Uninstall the add-on.
|
|
let addon = await AddonManager.getAddonByID("@set_homepage");
|
|
promise = waitForEnableMessage(controlledContent.id);
|
|
await addon.uninstall();
|
|
await promise;
|
|
|
|
// 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.
|
|
let mutationsDone = waitForAllMutations();
|
|
lockPrefs();
|
|
await mutationsDone;
|
|
|
|
// 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 prefis locked");
|
|
buttonPrefs.forEach(pref => {
|
|
is(getButton(pref).disabled, true, `The ${pref} button is disabled when locked`);
|
|
});
|
|
|
|
// Unlock the prefs without an extension.
|
|
unlockPrefs();
|
|
await waitForAllMutations();
|
|
|
|
// 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`);
|
|
});
|
|
is(controlledContent.hidden, true,
|
|
"The extension controlled message is hidden when unlocked with no extension");
|
|
|
|
BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
|
});
|
|
|
|
add_task(async function testExtensionControlledNewTab() {
|
|
await openPreferencesViaOpenPreferencesAPI("paneHome", {leaveOpen: true});
|
|
// eslint-disable-next-line mozilla/no-cpows-in-tests
|
|
let doc = gBrowser.contentDocument;
|
|
is(gBrowser.currentURI.spec, "about:preferences#home",
|
|
"#home should be in the URI for about:preferences");
|
|
|
|
let controlledContent = doc.getElementById("browserNewTabExtensionContent");
|
|
|
|
// The new tab is set to the default and message is hidden.
|
|
ok(!aboutNewTabService.newTabURL.startsWith("moz-extension:"), "new tab is not set");
|
|
is(controlledContent.hidden, true, "The extension controlled row is hidden");
|
|
|
|
// Install an extension that will set the new tab page.
|
|
let promise = waitForMessageShown("browserNewTabExtensionContent");
|
|
await installAddon("set_newtab.xpi");
|
|
await promise;
|
|
|
|
// The new tab page has been set by the extension and the user is notified.
|
|
let controlledLabel = controlledContent.querySelector("description");
|
|
ok(aboutNewTabService.newTabURL.startsWith("moz-extension:"), "new tab url is set by extension");
|
|
Assert.deepEqual(doc.l10n.getAttributes(controlledLabel), {
|
|
id: "extension-controlled-new-tab-url",
|
|
args: {
|
|
name: "set_newtab",
|
|
},
|
|
}, "The user is notified that an extension is controlling the new tab page");
|
|
is(controlledContent.hidden, false, "The extension controlled row is hidden");
|
|
|
|
// Disable the extension.
|
|
doc.getElementById("disableNewTabExtension").click();
|
|
|
|
// Verify the user is notified how to enable the extension.
|
|
await waitForEnableMessage(controlledContent.id);
|
|
is(doc.l10n.getAttributes(controlledLabel.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(controlledContent.id);
|
|
let dismissButton = controlledLabel.querySelector("image:last-of-type");
|
|
dismissButton.click();
|
|
await hidden;
|
|
|
|
// Ensure the New Tab page has been reset and there is no message.
|
|
ok(!aboutNewTabService.newTabURL.startsWith("moz-extension:"), "new tab page is set back to default");
|
|
is(controlledContent.hidden, true, "The extension controlled row is shown");
|
|
|
|
// Cleanup the tab and add-on.
|
|
BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
|
let addon = await AddonManager.getAddonByID("@set_newtab");
|
|
await addon.uninstall();
|
|
});
|
|
|
|
add_task(async function testExtensionControlledWebNotificationsPermission() {
|
|
let manifest = {
|
|
manifest_version: 2,
|
|
name: "TestExtension",
|
|
version: "1.0",
|
|
description: "Testing WebNotificationsDisable",
|
|
applications: {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-controlled-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 testExtensionControlledDefaultSearch() {
|
|
await openPreferencesViaOpenPreferencesAPI("paneSearch", {leaveOpen: true});
|
|
let doc = gBrowser.contentDocument;
|
|
let extensionId = "@set_default_search";
|
|
let manifest = {
|
|
manifest_version: 2,
|
|
name: "set_default_search",
|
|
applications: {gecko: {id: extensionId}},
|
|
description: "set_default_search description",
|
|
permissions: [],
|
|
chrome_settings_overrides: {
|
|
search_provider: {
|
|
name: "DuckDuckGo",
|
|
search_url: "https://duckduckgo.com/?q={searchTerms}",
|
|
is_default: true,
|
|
},
|
|
},
|
|
};
|
|
|
|
// This test is comparing nsISearchEngines by reference, so we need to initialize
|
|
// the SearchService here.
|
|
await Services.search.init();
|
|
|
|
function setEngine(engine) {
|
|
doc.querySelector(`#defaultEngine menuitem[label="${engine.name}"]`)
|
|
.doCommand();
|
|
}
|
|
|
|
is(gBrowser.currentURI.spec, "about:preferences#search",
|
|
"#search should be in the URI for about:preferences");
|
|
|
|
let controlledContent = doc.getElementById("browserDefaultSearchExtensionContent");
|
|
let initialEngine = Services.search.defaultEngine;
|
|
|
|
// Ensure the controlled content is hidden when not controlled.
|
|
is(controlledContent.hidden, true, "The extension controlled row is hidden");
|
|
|
|
// Install an extension that will set the default search engine.
|
|
let originalExtension = ExtensionTestUtils.loadExtension({
|
|
useAddonManager: "permanent",
|
|
manifest: Object.assign({}, manifest, {version: "1.0"}),
|
|
});
|
|
|
|
let messageShown = waitForMessageShown("browserDefaultSearchExtensionContent");
|
|
await originalExtension.startup();
|
|
await messageShown;
|
|
|
|
let addon = await AddonManager.getAddonByID(extensionId);
|
|
is(addon.version, "1.0", "The addon has the expected version.");
|
|
|
|
// The default search engine has been set by the extension and the user is notified.
|
|
let controlledLabel = controlledContent.querySelector("description");
|
|
let extensionEngine = Services.search.defaultEngine;
|
|
ok(initialEngine != extensionEngine, "The default engine has changed.");
|
|
Assert.deepEqual(doc.l10n.getAttributes(controlledLabel), {
|
|
id: "extension-controlled-default-search",
|
|
args: {
|
|
name: "set_default_search",
|
|
},
|
|
}, "The user is notified that an extension is controlling the default search engine");
|
|
is(controlledContent.hidden, false, "The extension controlled row is shown");
|
|
|
|
// Set the engine back to the initial one, ensure the message is hidden.
|
|
setEngine(initialEngine);
|
|
await waitForMessageHidden(controlledContent.id);
|
|
|
|
is(initialEngine, Services.search.defaultEngine,
|
|
"default search engine is set back to default");
|
|
is(controlledContent.hidden, true, "The extension controlled row is hidden");
|
|
|
|
// Setting the engine back to the extension's engine does not show the message.
|
|
setEngine(extensionEngine);
|
|
// Wait a tick for the Search Service's promises to resolve.
|
|
await new Promise(resolve => executeSoon(resolve));
|
|
|
|
is(extensionEngine, Services.search.defaultEngine,
|
|
"default search engine is set back to extension");
|
|
is(controlledContent.hidden, true, "The extension controlled row is still hidden");
|
|
|
|
// Set the engine to the initial one and verify an upgrade doesn't change it.
|
|
setEngine(initialEngine);
|
|
await waitForMessageHidden(controlledContent.id);
|
|
|
|
// Update the extension and wait for "ready".
|
|
let updatedExtension = ExtensionTestUtils.loadExtension({
|
|
useAddonManager: "permanent",
|
|
manifest: Object.assign({}, manifest, {version: "2.0"}),
|
|
});
|
|
await updatedExtension.startup();
|
|
addon = await AddonManager.getAddonByID(extensionId);
|
|
|
|
// Verify the extension is updated and search engine didn't change.
|
|
is(addon.version, "2.0", "The updated addon has the expected version");
|
|
is(controlledContent.hidden, true, "The extension controlled row is hidden after update");
|
|
is(initialEngine, Services.search.defaultEngine,
|
|
"default search engine is still the initial engine after update");
|
|
|
|
await originalExtension.unload();
|
|
await updatedExtension.unload();
|
|
BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
|
});
|
|
|
|
add_task(async function testExtensionControlledHomepageUninstalledAddon() {
|
|
async function checkHomepageEnabled() {
|
|
await openPreferencesViaOpenPreferencesAPI("paneHome", {leaveOpen: true});
|
|
// eslint-disable-next-line mozilla/no-cpows-in-tests
|
|
let doc = gBrowser.contentDocument;
|
|
is(gBrowser.currentURI.spec, "about:preferences#home",
|
|
"#home should be in the URI for about:preferences");
|
|
let controlledContent = doc.getElementById("browserHomePageExtensionContent");
|
|
|
|
// 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");
|
|
is(controlledContent.hidden, true, "The extension controlled row is hidden");
|
|
|
|
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,
|
|
value: "https://developer.mozilla.org",
|
|
enabled: true,
|
|
}],
|
|
},
|
|
},
|
|
};
|
|
let jsonFileName = "extension-settings.json";
|
|
let storePath = OS.Path.join(OS.Constants.Path.profileDir, jsonFileName);
|
|
await OS.File.writeAtomic(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,
|
|
"https://developer.mozilla.org",
|
|
"The homepage_override appears to be set");
|
|
await checkHomepageEnabled();
|
|
|
|
// Remove the bad store file that we used.
|
|
await OS.File.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";
|
|
const DISABLE_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-controlled-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.");
|
|
}
|
|
|
|
async function disableViaClick() {
|
|
let labelId = CONTROLLED_LABEL_ID;
|
|
let disableId = DISABLE_BUTTON_ID;
|
|
let controlledLabel = doc.getElementById(labelId);
|
|
|
|
let enableMessageShown = waitForEnableMessage(labelId);
|
|
doc.getElementById(disableId).click();
|
|
await enableMessageShown;
|
|
|
|
// The user is notified how to enable the extension.
|
|
let controlledDesc = controlledLabel.querySelector("description");
|
|
is(doc.l10n.getAttributes(controlledDesc.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);
|
|
controlledLabel.querySelector("image:last-of-type").click();
|
|
await hidden;
|
|
}
|
|
|
|
async function reEnableExtension(addon) {
|
|
let controlledMessageShown = waitForMessageShown(CONTROLLED_LABEL_ID);
|
|
await addon.enable();
|
|
await controlledMessageShown;
|
|
}
|
|
|
|
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",
|
|
applications: {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 disableViaClick();
|
|
|
|
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);
|
|
|
|
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/connection.xul";
|
|
|
|
await SpecialPowers.pushPrefEnv({"set": [[PROXY_PREF, PROXY_DEFAULT]]});
|
|
|
|
function background() {
|
|
browser.proxy.settings.set({value: {proxyType: "none"}});
|
|
}
|
|
|
|
function expectedConnectionSettingsMessage(doc, isControlled) {
|
|
return isControlled ?
|
|
"extension-controlled-proxy-config" :
|
|
"network-proxy-connection-description";
|
|
}
|
|
|
|
function connectionSettingsMessagePromise(doc, isControlled) {
|
|
return waitForMessageContent(
|
|
CONNECTION_SETTINGS_DESC_ID,
|
|
expectedConnectionSettingsMessage(doc, isControlled),
|
|
doc
|
|
);
|
|
}
|
|
|
|
function verifyState(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-controlled-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("grid");
|
|
return {
|
|
manualControls: [
|
|
...manualControlContainer.querySelectorAll("label:not([control=networkProxyNone])"),
|
|
...manualControlContainer.querySelectorAll("textbox:not(#networkProxyNone)"),
|
|
...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 disableViaClick() {
|
|
let sectionId = CONTROLLED_SECTION_ID;
|
|
let controlledSection = panelDoc.getElementById(sectionId);
|
|
|
|
let enableMessageShown = waitForEnableMessage(sectionId, panelDoc);
|
|
panelDoc.getElementById(CONTROLLED_BUTTON_ID).click();
|
|
await enableMessageShown;
|
|
|
|
// The user is notified how to enable the extension.
|
|
let controlledDesc = controlledSection.querySelector("description");
|
|
is(panelDoc.l10n.getAttributes(controlledDesc.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(sectionId, panelDoc);
|
|
controlledSection.querySelector("image:last-of-type").click();
|
|
return hidden;
|
|
}
|
|
|
|
async function reEnableExtension(addon) {
|
|
let messageChanged = connectionSettingsMessagePromise(mainDoc, true);
|
|
await addon.enable();
|
|
await messageChanged;
|
|
}
|
|
|
|
async function openProxyPanel() {
|
|
let panel = await openAndLoadSubDialog(PANEL_URL);
|
|
let closingPromise = waitForEvent(panel.document.documentElement, "dialogclosing");
|
|
ok(panel, "Proxy panel opened.");
|
|
return {panel, closingPromise};
|
|
}
|
|
|
|
async function closeProxyPanel(panelObj) {
|
|
panelObj.panel.document.documentElement.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");
|
|
|
|
verifyState(mainDoc, false);
|
|
|
|
// Open the connections panel.
|
|
let panelObj = await openProxyPanel();
|
|
let panelDoc = panelObj.panel.document;
|
|
|
|
verifyState(panelDoc, false);
|
|
|
|
await closeProxyPanel(panelObj);
|
|
|
|
verifyState(mainDoc, false);
|
|
|
|
// Install an extension that controls proxy settings.
|
|
let extension = ExtensionTestUtils.loadExtension({
|
|
useAddonManager: "permanent",
|
|
manifest: {
|
|
name: "set_proxy",
|
|
applications: {gecko: {id: EXTENSION_ID}},
|
|
permissions: ["proxy"],
|
|
},
|
|
background,
|
|
});
|
|
|
|
let messageChanged = connectionSettingsMessagePromise(mainDoc, true);
|
|
await extension.startup();
|
|
await messageChanged;
|
|
let addon = await AddonManager.getAddonByID(EXTENSION_ID);
|
|
|
|
verifyState(mainDoc, true);
|
|
messageChanged = connectionSettingsMessagePromise(mainDoc, false);
|
|
|
|
panelObj = await openProxyPanel();
|
|
panelDoc = panelObj.panel.document;
|
|
|
|
verifyState(panelDoc, true);
|
|
|
|
await disableViaClick();
|
|
|
|
verifyState(panelDoc, false);
|
|
|
|
await closeProxyPanel(panelObj);
|
|
await messageChanged;
|
|
|
|
verifyState(mainDoc, false);
|
|
|
|
await reEnableExtension(addon);
|
|
|
|
verifyState(mainDoc, true);
|
|
messageChanged = connectionSettingsMessagePromise(mainDoc, false);
|
|
|
|
panelObj = await openProxyPanel();
|
|
panelDoc = panelObj.panel.document;
|
|
|
|
verifyState(panelDoc, true);
|
|
|
|
await disableViaClick();
|
|
|
|
verifyState(panelDoc, false);
|
|
|
|
await closeProxyPanel(panelObj);
|
|
await messageChanged;
|
|
|
|
verifyState(mainDoc, 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);
|
|
|
|
await extension.unload();
|
|
|
|
BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
|
});
|