/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* vim: set sts=2 sw=2 et tw=80: */ "use strict"; ChromeUtils.defineESModuleGetters(this, { Preferences: "resource://gre/modules/Preferences.sys.mjs", }); const { ExtensionPermissions } = ChromeUtils.importESModule( "resource://gre/modules/ExtensionPermissions.sys.mjs" ); AddonTestUtils.init(this); AddonTestUtils.overrideCertDB(); AddonTestUtils.createAppInfo( "xpcshell@tests.mozilla.org", "XPCShell", "1", "42" ); // Start a server for `pac.example.com` to intercept attempts to connect to it // to load a PAC URL. We won't serve anything, but this prevents attempts at // non-local connections if this domain is registered. AddonTestUtils.createHttpServer({ hosts: ["pac.example.com"] }); add_setup(async function () { // Bug 1646182: Force ExtensionPermissions to run in rkv mode, the legacy // storage mode will run in xpcshell-legacy-ep.toml await ExtensionPermissions._uninit(); Services.prefs.setBoolPref( "extensions.webextOptionalPermissionPrompts", false ); registerCleanupFunction(() => { Services.prefs.clearUserPref("extensions.webextOptionalPermissionPrompts"); }); await AddonTestUtils.promiseStartupManager(); }); add_task(async function test_browser_settings() { // TODO bug 1725981: proxy.settings is not supported on Android. if (AppConstants.platform === "android") { info("proxy.settings not supported on Android; skipping"); return; } const proxySvc = Ci.nsIProtocolProxyService; // Create an object to hold the values to which we will initialize the prefs. const PREFS = { "network.proxy.type": proxySvc.PROXYCONFIG_SYSTEM, "network.proxy.http": "", "network.proxy.http_port": 0, "network.proxy.share_proxy_settings": false, "network.proxy.ssl": "", "network.proxy.ssl_port": 0, "network.proxy.socks": "", "network.proxy.socks_port": 0, "network.proxy.socks_version": 5, "network.proxy.socks_remote_dns": false, "network.proxy.no_proxies_on": "", "network.proxy.autoconfig_url": "", "signon.autologin.proxy": false, }; async function background() { browser.test.onMessage.addListener(async (msg, value) => { let apiObj = browser.proxy.settings; let result = await apiObj.set({ value }); if (msg === "set") { browser.test.assertTrue(result, "set returns true."); browser.test.sendMessage("settingData", await apiObj.get({})); } else { browser.test.assertFalse(result, "set returns false for a no-op."); browser.test.sendMessage("no-op set"); } }); } // Set prefs to our initial values. for (let pref in PREFS) { Preferences.set(pref, PREFS[pref]); } registerCleanupFunction(() => { // Reset the prefs. for (let pref in PREFS) { Preferences.reset(pref); } }); let extension = ExtensionTestUtils.loadExtension({ background, manifest: { permissions: ["proxy"], }, incognitoOverride: "spanning", useAddonManager: "temporary", }); await extension.startup(); async function testSetting(value, expected, expectedValue = value) { extension.sendMessage("set", value); let data = await extension.awaitMessage("settingData"); deepEqual(data.value, expectedValue, `The setting has the expected value.`); equal( data.levelOfControl, "controlled_by_this_extension", `The setting has the expected levelOfControl.` ); for (let pref in expected) { equal( Preferences.get(pref), expected[pref], `${pref} set correctly for ${value}` ); } } async function testProxy(config, expectedPrefs, expectedConfig = config) { let proxyConfig = { proxyType: "none", autoConfigUrl: "", autoLogin: false, proxyDNS: false, httpProxyAll: false, socksVersion: 5, passthrough: "", http: "", ssl: "", socks: "", respectBeConservative: true, }; expectedConfig.proxyType = expectedConfig.proxyType || "system"; return testSetting( config, expectedPrefs, Object.assign(proxyConfig, expectedConfig) ); } await testProxy( { proxyType: "none" }, { "network.proxy.type": proxySvc.PROXYCONFIG_DIRECT } ); await testProxy( { proxyType: "autoDetect", autoLogin: true, proxyDNS: true, }, { "network.proxy.type": proxySvc.PROXYCONFIG_WPAD, "signon.autologin.proxy": true, "network.proxy.socks_remote_dns": true, } ); await testProxy( { proxyType: "system", autoLogin: false, proxyDNS: false, }, { "network.proxy.type": proxySvc.PROXYCONFIG_SYSTEM, "signon.autologin.proxy": false, "network.proxy.socks_remote_dns": false, } ); // Verify that proxyType is optional and it defaults to "system". await testProxy( { autoLogin: false, proxyDNS: false, }, { "network.proxy.type": proxySvc.PROXYCONFIG_SYSTEM, "signon.autologin.proxy": false, "network.proxy.socks_remote_dns": false, "network.http.proxy.respect-be-conservative": true, } ); await testProxy( { proxyType: "autoConfig", autoConfigUrl: "http://pac.example.com", }, { "network.proxy.type": proxySvc.PROXYCONFIG_PAC, "network.proxy.autoconfig_url": "http://pac.example.com", "network.http.proxy.respect-be-conservative": true, } ); await testProxy( { proxyType: "manual", http: "http://www.mozilla.org", autoConfigUrl: "", }, { "network.proxy.type": proxySvc.PROXYCONFIG_MANUAL, "network.proxy.http": "www.mozilla.org", "network.proxy.http_port": 80, "network.proxy.autoconfig_url": "", }, { proxyType: "manual", http: "www.mozilla.org:80", autoConfigUrl: "", } ); // When using proxyAll, we expect all proxies to be set to // be the same as http. await testProxy( { proxyType: "manual", http: "http://www.mozilla.org:8080", httpProxyAll: true, }, { "network.proxy.type": proxySvc.PROXYCONFIG_MANUAL, "network.proxy.http": "www.mozilla.org", "network.proxy.http_port": 8080, "network.proxy.ssl": "www.mozilla.org", "network.proxy.ssl_port": 8080, "network.proxy.share_proxy_settings": true, }, { proxyType: "manual", http: "www.mozilla.org:8080", ssl: "www.mozilla.org:8080", socks: "", httpProxyAll: true, } ); await testProxy( { proxyType: "manual", http: "www.mozilla.org:8080", httpProxyAll: false, ftp: "www.mozilla.org:8081", ssl: "www.mozilla.org:8082", socks: "mozilla.org:8083", socksVersion: 4, passthrough: ".mozilla.org", respectBeConservative: true, }, { "network.proxy.type": proxySvc.PROXYCONFIG_MANUAL, "network.proxy.http": "www.mozilla.org", "network.proxy.http_port": 8080, "network.proxy.share_proxy_settings": false, "network.proxy.ssl": "www.mozilla.org", "network.proxy.ssl_port": 8082, "network.proxy.socks": "mozilla.org", "network.proxy.socks_port": 8083, "network.proxy.socks_version": 4, "network.proxy.no_proxies_on": ".mozilla.org", "network.http.proxy.respect-be-conservative": true, }, { proxyType: "manual", http: "www.mozilla.org:8080", httpProxyAll: false, // ftp: "www.mozilla.org:8081", // This line should not be sent back ssl: "www.mozilla.org:8082", socks: "mozilla.org:8083", socksVersion: 4, passthrough: ".mozilla.org", respectBeConservative: true, } ); await testProxy( { proxyType: "manual", http: "http://www.mozilla.org", ssl: "https://www.mozilla.org", socks: "mozilla.org", socksVersion: 4, passthrough: ".mozilla.org", respectBeConservative: false, }, { "network.proxy.type": proxySvc.PROXYCONFIG_MANUAL, "network.proxy.http": "www.mozilla.org", "network.proxy.http_port": 80, "network.proxy.share_proxy_settings": false, "network.proxy.ssl": "www.mozilla.org", "network.proxy.ssl_port": 443, "network.proxy.socks": "mozilla.org", "network.proxy.socks_port": 1080, "network.proxy.socks_version": 4, "network.proxy.no_proxies_on": ".mozilla.org", "network.http.proxy.respect-be-conservative": false, }, { proxyType: "manual", http: "www.mozilla.org:80", httpProxyAll: false, ssl: "www.mozilla.org:443", socks: "mozilla.org:1080", socksVersion: 4, passthrough: ".mozilla.org", respectBeConservative: false, } ); await testProxy( { proxyType: "manual", http: "http://www.mozilla.org:80", ssl: "https://www.mozilla.org:443", socks: "mozilla.org:1080", socksVersion: 4, passthrough: ".mozilla.org", respectBeConservative: true, }, { "network.proxy.type": proxySvc.PROXYCONFIG_MANUAL, "network.proxy.http": "www.mozilla.org", "network.proxy.http_port": 80, "network.proxy.share_proxy_settings": false, "network.proxy.ssl": "www.mozilla.org", "network.proxy.ssl_port": 443, "network.proxy.socks": "mozilla.org", "network.proxy.socks_port": 1080, "network.proxy.socks_version": 4, "network.proxy.no_proxies_on": ".mozilla.org", "network.http.proxy.respect-be-conservative": true, }, { proxyType: "manual", http: "www.mozilla.org:80", httpProxyAll: false, ssl: "www.mozilla.org:443", socks: "mozilla.org:1080", socksVersion: 4, passthrough: ".mozilla.org", respectBeConservative: true, } ); await testProxy( { proxyType: "manual", http: "http://www.mozilla.org:80", ssl: "https://www.mozilla.org:80", socks: "mozilla.org:80", socksVersion: 4, passthrough: ".mozilla.org", respectBeConservative: false, }, { "network.proxy.type": proxySvc.PROXYCONFIG_MANUAL, "network.proxy.http": "www.mozilla.org", "network.proxy.http_port": 80, "network.proxy.share_proxy_settings": false, "network.proxy.ssl": "www.mozilla.org", "network.proxy.ssl_port": 80, "network.proxy.socks": "mozilla.org", "network.proxy.socks_port": 80, "network.proxy.socks_version": 4, "network.proxy.no_proxies_on": ".mozilla.org", "network.http.proxy.respect-be-conservative": false, }, { proxyType: "manual", http: "www.mozilla.org:80", httpProxyAll: false, ssl: "www.mozilla.org:80", socks: "mozilla.org:80", socksVersion: 4, passthrough: ".mozilla.org", respectBeConservative: false, } ); // Test resetting values. await testProxy( { proxyType: "none", http: "", ssl: "", socks: "", socksVersion: 5, passthrough: "", respectBeConservative: true, }, { "network.proxy.type": proxySvc.PROXYCONFIG_DIRECT, "network.proxy.http": "", "network.proxy.http_port": 0, "network.proxy.ssl": "", "network.proxy.ssl_port": 0, "network.proxy.socks": "", "network.proxy.socks_port": 0, "network.proxy.socks_version": 5, "network.proxy.no_proxies_on": "", "network.http.proxy.respect-be-conservative": true, } ); await extension.unload(); }); add_task(async function test_bad_value_proxy_config() { let background = AppConstants.platform === "android" ? async () => { await browser.test.assertRejects( browser.proxy.settings.set({ value: { proxyType: "none", }, }), /proxy.settings is not supported on android/, "proxy.settings.set rejects on Android." ); await browser.test.assertRejects( browser.proxy.settings.get({}), /proxy.settings is not supported on android/, "proxy.settings.get rejects on Android." ); await browser.test.assertRejects( browser.proxy.settings.clear({}), /proxy.settings is not supported on android/, "proxy.settings.clear rejects on Android." ); browser.test.sendMessage("done"); } : async () => { await browser.test.assertRejects( browser.proxy.settings.set({ value: { proxyType: "abc", }, }), /abc is not a valid value for proxyType/, "proxy.settings.set rejects with an invalid proxyType value." ); await browser.test.assertRejects( browser.proxy.settings.set({ value: { proxyType: "autoConfig", }, }), /undefined is not a valid value for autoConfigUrl/, "proxy.settings.set for type autoConfig rejects with an empty autoConfigUrl value." ); await browser.test.assertRejects( browser.proxy.settings.set({ value: { proxyType: "autoConfig", autoConfigUrl: "abc", }, }), /abc is not a valid value for autoConfigUrl/, "proxy.settings.set rejects with an invalid autoConfigUrl value." ); await browser.test.assertRejects( browser.proxy.settings.set({ value: { proxyType: "manual", socksVersion: "abc", }, }), /abc is not a valid value for socksVersion/, "proxy.settings.set rejects with an invalid socksVersion value." ); await browser.test.assertRejects( browser.proxy.settings.set({ value: { proxyType: "manual", socksVersion: 3, }, }), /3 is not a valid value for socksVersion/, "proxy.settings.set rejects with an invalid socksVersion value." ); browser.test.sendMessage("done"); }; let extension = ExtensionTestUtils.loadExtension({ background, manifest: { permissions: ["proxy"], }, incognitoOverride: "spanning", }); await extension.startup(); await extension.awaitMessage("done"); await extension.unload(); }); // Verify proxy prefs are unset on permission removal. add_task(async function test_proxy_settings_permissions() { // TODO bug 1725981: proxy.settings is not supported on Android. if (AppConstants.platform === "android") { info("proxy.settings not supported on Android; skipping"); return; } async function background() { const permObj = { permissions: ["proxy"] }; browser.test.onMessage.addListener(async (msg, value) => { if (msg === "request") { browser.test.log("requesting proxy permission"); await browser.permissions.request(permObj); browser.test.log("setting proxy values"); await browser.proxy.settings.set({ value }); browser.test.sendMessage("set"); } else if (msg === "remove") { await browser.permissions.remove(permObj); browser.test.sendMessage("removed"); } }); } let prefNames = [ "network.proxy.type", "network.proxy.http", "network.proxy.http_port", "network.proxy.ssl", "network.proxy.ssl_port", "network.proxy.socks", "network.proxy.socks_port", "network.proxy.socks_version", "network.proxy.no_proxies_on", ]; function checkSettings(msg, expectUserValue = false) { info(msg); for (let pref of prefNames) { equal( expectUserValue, Services.prefs.prefHasUserValue(pref), `${pref} set as expected ${Preferences.get(pref)}` ); } } let extension = ExtensionTestUtils.loadExtension({ background, manifest: { optional_permissions: ["proxy"], }, incognitoOverride: "spanning", useAddonManager: "permanent", }); await extension.startup(); checkSettings("setting is not set after startup"); await withHandlingUserInput(extension, async () => { extension.sendMessage("request", { proxyType: "manual", http: "www.mozilla.org:8080", httpProxyAll: false, ssl: "www.mozilla.org:8082", socks: "mozilla.org:8083", socksVersion: 4, passthrough: ".mozilla.org", }); await extension.awaitMessage("set"); checkSettings("setting was set after request", true); extension.sendMessage("remove"); await extension.awaitMessage("removed"); checkSettings("setting is reset after remove"); // Set again to test after restart extension.sendMessage("request", { proxyType: "manual", http: "www.mozilla.org:8080", httpProxyAll: false, ssl: "www.mozilla.org:8082", socks: "mozilla.org:8083", socksVersion: 4, passthrough: ".mozilla.org", }); await extension.awaitMessage("set"); checkSettings("setting was set after request", true); }); // force the permissions store to be re-read on startup await ExtensionPermissions._uninit(); resetHandlingUserInput(); await AddonTestUtils.promiseRestartManager(); await extension.awaitBackgroundStarted(); await withHandlingUserInput(extension, async () => { extension.sendMessage("remove"); await extension.awaitMessage("removed"); checkSettings("setting is reset after remove"); }); await extension.unload(); });