diff options
Diffstat (limited to 'remote/test/browser/network')
19 files changed, 2347 insertions, 0 deletions
diff --git a/remote/test/browser/network/browser.ini b/remote/test/browser/network/browser.ini new file mode 100644 index 0000000000..274291c3f3 --- /dev/null +++ b/remote/test/browser/network/browser.ini @@ -0,0 +1,29 @@ +[DEFAULT] +tags = remote +subsuite = remote +prefs = + remote.enabled=true +support-files = + !/remote/test/browser/chrome-remote-interface.js + !/remote/test/browser/head.js + head.js + doc_empty.html + doc_frameset.html + doc_networkEvents.html + file_networkEvents.js + file_framesetEvents.js + sjs-cookies.sjs + +[browser_deleteCookies.js] +[browser_emulateNetworkConditions.js] +[browser_getAllCookies.js] +[browser_getCookies.js] +skip-if = (os == 'win' && os_version == '10.0' && ccov) # Bug 1605650 +[browser_navigationEvents.js] +[browser_responseReceived.js] +[browser_requestWillBeSent.js] +[browser_setCookie.js] +[browser_setCookies.js] +[browser_setCacheDisabled.js] +skip-if = true # Bug 1610382 +[browser_setUserAgentOverride.js] diff --git a/remote/test/browser/network/browser_deleteCookies.js b/remote/test/browser/network/browser_deleteCookies.js new file mode 100644 index 0000000000..70489cdd7d --- /dev/null +++ b/remote/test/browser/network/browser_deleteCookies.js @@ -0,0 +1,298 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const SJS_PATH = "/browser/remote/test/browser/network/sjs-cookies.sjs"; + +const DEFAULT_HOST = "http://example.org"; +const DEFAULT_HOSTNAME = "example.org"; +const ALT_HOST = "http://example.net"; +const ALT_HOSTNAME = "example.net"; +const SECURE_HOST = "https://example.com"; +const SECURE_HOSTNAME = "example.com"; + +const DEFAULT_URL = `${DEFAULT_HOST}${SJS_PATH}`; + +add_task(async function failureWithoutArguments({ client }) { + const { Network } = client; + + let errorThrown = false; + try { + await Network.deleteCookies(); + } catch (e) { + errorThrown = true; + } + ok(errorThrown, "Fails without any arguments"); +}); + +add_task(async function failureWithoutDomainOrURL({ client }) { + const { Network } = client; + + let errorThrown = false; + try { + await Network.deleteCookies({ name: "foo" }); + } catch (e) { + errorThrown = true; + } + ok(errorThrown, "Fails without domain or URL"); +}); + +add_task(async function failureWithInvalidProtocol({ client }) { + const { Network } = client; + + const FTP_URL = `ftp://${DEFAULT_HOSTNAME}`; + + let errorThrown = false; + try { + await Network.deleteCookies({ name: "foo", url: FTP_URL }); + } catch (e) { + errorThrown = true; + } + ok(errorThrown, "Fails for invalid protocol in URL"); +}); + +add_task(async function pristineContext({ client }) { + const { Network } = client; + + await loadURL(DEFAULT_URL); + + const { cookies } = await Network.getCookies(); + is(cookies.length, 0, "No cookies have been found"); + + await Network.deleteCookies({ name: "foo", url: DEFAULT_URL }); +}); + +add_task(async function fromHostWithPort({ client }) { + const { Network } = client; + + const PORT_URL = `${DEFAULT_HOST}:8000${SJS_PATH}`; + await loadURL(PORT_URL + "?name=id&value=1"); + + const cookie = { + name: "id", + value: "1", + }; + + try { + const { cookies: before } = await Network.getCookies(); + is(before.length, 1, "A cookie has been found"); + + await Network.deleteCookies({ name: cookie.name, url: PORT_URL }); + + const { cookies: after } = await Network.getCookies(); + is(after.length, 0, "No cookie has been found"); + } finally { + Services.cookies.removeAll(); + } +}); + +add_task(async function forSpecificDomain({ client }) { + const { Network } = client; + + const ALT_URL = ALT_HOST + SJS_PATH; + + await loadURL(`${ALT_URL}?name=foo&value=bar`); + await loadURL(`${DEFAULT_URL}?name=foo&value=bar`); + + const cookie = { + name: "foo", + value: "bar", + domain: "example.net", + }; + + try { + const { cookies: before } = await Network.getCookies(); + is(before.length, 1, "A cookie has been found"); + + await Network.deleteCookies({ + name: cookie.name, + domain: DEFAULT_HOSTNAME, + }); + + const { cookies: after } = await Network.getCookies(); + is(after.length, 0, "No cookie has been found"); + + await loadURL(ALT_URL); + + const { cookies: other } = await Network.getCookies(); + is(other.length, 1, "A cookie has been found"); + assertCookie(other[0], cookie); + } finally { + Services.cookies.removeAll(); + } +}); + +add_task(async function forSpecificURL({ client }) { + const { Network } = client; + + const ALT_URL = ALT_HOST + SJS_PATH; + + await loadURL(`${ALT_URL}?name=foo&value=bar`); + await loadURL(`${DEFAULT_URL}?name=foo&value=bar`); + + const cookie = { + name: "foo", + value: "bar", + domain: "example.net", + }; + + try { + const { cookies: before } = await Network.getCookies(); + is(before.length, 1, "A cookie has been found"); + + await Network.deleteCookies({ name: cookie.name, url: DEFAULT_URL }); + + const { cookies: after } = await Network.getCookies(); + is(after.length, 0, "No cookie has been found"); + + await loadURL(ALT_URL); + + const { cookies: other } = await Network.getCookies(); + is(other.length, 1, "A cookie has been found"); + assertCookie(other[0], cookie); + } finally { + Services.cookies.removeAll(); + } +}); + +add_task(async function forSecureURL({ client }) { + const { Network } = client; + + const SECURE_URL = `${SECURE_HOST}${SJS_PATH}`; + + await loadURL(`${DEFAULT_URL}?name=foo&value=bar`); + await loadURL(`${SECURE_URL}?name=foo&value=bar`); + + const cookie = { + name: "foo", + value: "bar", + domain: "example.org", + }; + + try { + const { cookies: before } = await Network.getCookies(); + is(before.length, 1, "A cookie has been found"); + + await Network.deleteCookies({ name: cookie.name, url: SECURE_URL }); + + const { cookies: after } = await Network.getCookies(); + is(after.length, 0, "No cookie has been found"); + + await loadURL(DEFAULT_URL); + + const { cookies: other } = await Network.getCookies(); + is(other.length, 1, "A cookie has been found"); + assertCookie(other[0], cookie); + } finally { + Services.cookies.removeAll(); + } +}); + +add_task(async function forSpecificDomainAndURL({ client }) { + const { Network } = client; + + const ALT_URL = ALT_HOST + SJS_PATH; + + await loadURL(`${ALT_URL}?name=foo&value=bar`); + await loadURL(`${DEFAULT_URL}?name=foo&value=bar`); + + const cookie = { + name: "foo", + value: "bar", + domain: "example.net", + }; + + try { + const { cookies: before } = await Network.getCookies(); + is(before.length, 1, "A cookie has been found"); + + // Domain has precedence before URL + await Network.deleteCookies({ + name: cookie.name, + domain: DEFAULT_HOSTNAME, + url: ALT_URL, + }); + + const { cookies: after } = await Network.getCookies(); + is(after.length, 0, "No cookie has been found"); + + await loadURL(ALT_URL); + + const { cookies: other } = await Network.getCookies(); + is(other.length, 1, "A cookie has been found"); + assertCookie(other[0], cookie); + } finally { + Services.cookies.removeAll(); + } +}); + +add_task(async function path({ client }) { + const { Network } = client; + + const PATH = "/browser/remote/test/browser/"; + const PARENT_PATH = "/browser/remote/test/"; + const SUB_PATH = "/browser/remote/test/browser/network/"; + + const cookie = { + name: "foo", + value: "bar", + path: PATH, + }; + + try { + console.log("Check exact path"); + await loadURL(`${DEFAULT_URL}?name=foo&value=bar&path=${PATH}`); + let result = await Network.getCookies(); + is(result.cookies.length, 1, "A single cookie has been found"); + + await Network.deleteCookies({ + name: cookie.name, + path: PATH, + url: DEFAULT_URL, + }); + result = await Network.getCookies(); + is(result.cookies.length, 0, "No cookie has been found"); + + console.log("Check sub path"); + await loadURL(`${DEFAULT_URL}?name=foo&value=bar&path=${PATH}`); + result = await Network.getCookies(); + is(result.cookies.length, 1, "A single cookie has been found"); + + await Network.deleteCookies({ + name: cookie.name, + path: SUB_PATH, + url: DEFAULT_URL, + }); + result = await Network.getCookies(); + is(result.cookies.length, 1, "A single cookie has been found"); + + console.log("Check parent path"); + await loadURL(`${DEFAULT_URL}?name=foo&value=bar&path=${PATH}`); + result = await Network.getCookies(); + is(result.cookies.length, 1, "A single cookie has been found"); + + await Network.deleteCookies({ + name: cookie.name, + path: PARENT_PATH, + url: DEFAULT_URL, + }); + result = await Network.getCookies(); + is(result.cookies.length, 0, "No cookie has been found"); + + console.log("Check non matching path"); + await loadURL(`${DEFAULT_URL}?name=foo&value=bar&path=${PATH}`); + result = await Network.getCookies(); + is(result.cookies.length, 1, "A single cookie has been found"); + + await Network.deleteCookies({ + name: cookie.name, + path: "/foo/bar", + url: DEFAULT_URL, + }); + result = await Network.getCookies(); + is(result.cookies.length, 1, "A single cookie has been found"); + } finally { + Services.cookies.removeAll(); + } +}); diff --git a/remote/test/browser/network/browser_emulateNetworkConditions.js b/remote/test/browser/network/browser_emulateNetworkConditions.js new file mode 100644 index 0000000000..8eacdacd32 --- /dev/null +++ b/remote/test/browser/network/browser_emulateNetworkConditions.js @@ -0,0 +1,248 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const pageEmptyURL = + "http://example.com/browser/remote/test/browser/page/doc_empty.html"; + +/** + * Acts just as `add_task`, but does cleanup afterwards + * @param taskFn + */ +function add_networking_task(taskFn) { + add_task(async client => { + try { + await taskFn(client); + } finally { + Services.io.offline = false; + } + }); +} + +add_networking_task(async function offlineWithoutArguments({ client }) { + const { Network } = client; + + let errorThrown = ""; + try { + await Network.emulateNetworkConditions(); + } catch (e) { + errorThrown = e.message; + } + + ok( + errorThrown.match(/offline: boolean value expected/), + "Fails without any arguments" + ); +}); + +add_networking_task(async function offlineWithEmptyArguments({ client }) { + const { Network } = client; + + let errorThrown = ""; + try { + await Network.emulateNetworkConditions({}); + } catch (e) { + errorThrown = e.message; + } + + ok( + errorThrown.match(/offline: boolean value expected/), + "Fails with only empty arguments" + ); +}); + +add_networking_task(async function offlineWithInvalidArguments({ client }) { + const { Network } = client; + const testTable = [null, undefined, 1, "foo", [], {}]; + + for (const testCase of testTable) { + let errorThrown = ""; + try { + await Network.emulateNetworkConditions({ offline: testCase }); + } catch (e) { + errorThrown = e.message; + } + + const testType = typeof testCase; + + ok( + errorThrown.match(/offline: boolean value expected/), + `Fails with ${testType}-type argument for offline` + ); + } +}); + +add_networking_task(async function offlineWithUnsupportedArguments({ client }) { + const { Network } = client; + + // Random valid values for the Network.emulateNetworkConditions command, even though we don't support them yet + const args = { + offline: true, + latency: 500, + downloadThroughput: 500, + uploadThroughput: 500, + connectionType: "cellular2g", + someFutureArg: false, + }; + + await Network.emulateNetworkConditions(args); + + ok(true, "No errors should be thrown due to non-implemented arguments"); +}); + +add_networking_task(async function emulateOfflineWhileOnline({ client }) { + const { Network } = client; + + // Assert we're online to begin with + await assertOfflineStatus(false); + + // Assert for offline + await Network.emulateNetworkConditions({ offline: true }); + await assertOfflineStatus(true); + + // Assert we really can't navigate after setting offline + await assertOfflineNavigationFails(); +}); + +add_networking_task(async function emulateOfflineWhileOffline({ client }) { + const { Network } = client; + + // Assert we're online to begin with + await assertOfflineStatus(false); + + // Assert for offline + await Network.emulateNetworkConditions({ offline: true }); + await assertOfflineStatus(true); + + // Assert for no-offline event, because we're offline - and changing to offline - so nothing changes + await Network.emulateNetworkConditions({ offline: true }); + await assertOfflineStatus(true); + + // Assert we still can't navigate after setting offline twice + await assertOfflineNavigationFails(); +}); + +add_networking_task(async function emulateOnlineWhileOnline({ client }) { + const { Network } = client; + + // Assert we're online to begin with + await assertOfflineStatus(false); + + // Assert for no-offline event, because we're online - and changing to online - so nothing changes + await Network.emulateNetworkConditions({ offline: false }); + await assertOfflineStatus(false); +}); + +add_networking_task(async function emulateOnlineWhileOffline({ client }) { + const { Network } = client; + + // Assert we're online to begin with + await assertOfflineStatus(false); + + // Assert for offline event, because we're online - and changing to offline + const offlineChanged = Promise.race([ + BrowserTestUtils.waitForContentEvent( + gBrowser.selectedBrowser, + "online", + true + ), + BrowserTestUtils.waitForContentEvent( + gBrowser.selectedBrowser, + "offline", + true + ), + ]); + + await Network.emulateNetworkConditions({ offline: true }); + + info("Waiting for offline event on window"); + is(await offlineChanged, "offline", "Only the offline-event should fire"); + await assertOfflineStatus(true); + + // Assert for online event, because we're offline - and changing to online + const offlineChangedBack = Promise.race([ + BrowserTestUtils.waitForContentEvent( + gBrowser.selectedBrowser, + "online", + true + ), + BrowserTestUtils.waitForContentEvent( + gBrowser.selectedBrowser, + "offline", + true + ), + ]); + await Network.emulateNetworkConditions({ offline: false }); + + info("Waiting for online event on window"); + is(await offlineChangedBack, "online", "Only the online-event should fire"); + await assertOfflineStatus(false); +}); + +/** + * Navigates to a page, and asserting any status code to appear + */ +async function assertOfflineNavigationFails() { + // Tests always connect to localhost, and per bug 87717, localhost is now + // reachable in offline mode. To avoid this, disable any proxy. + const proxyPrefValue = SpecialPowers.getIntPref("network.proxy.type"); + const diskPrefValue = SpecialPowers.getBoolPref("browser.cache.disk.enable"); + const memoryPrefValue = SpecialPowers.getBoolPref( + "browser.cache.memory.enable" + ); + SpecialPowers.pushPrefEnv({ + set: [ + ["network.proxy.type", 0], + ["browser.cache.disk.enable", false], + ["browser.cache.memory.enable", false], + ], + }); + + try { + const browser = gBrowser.selectedTab.linkedBrowser; + let netErrorLoaded = BrowserTestUtils.waitForErrorPage(browser); + + BrowserTestUtils.loadURI(browser, pageEmptyURL); + await netErrorLoaded; + + await SpecialPowers.spawn(browser, [], () => { + ok( + content.document.documentURI.startsWith("about:neterror?e=netOffline"), + "Should be showing error page" + ); + }); + } finally { + // Re-enable the proxy so example.com is resolved to localhost, rather than + // the actual example.com. + SpecialPowers.pushPrefEnv({ + set: [ + ["network.proxy.type", proxyPrefValue], + ["browser.cache.disk.enable", diskPrefValue], + ["browser.cache.memory.enable", memoryPrefValue], + ], + }); + } +} + +/** + * Checks on the page what the value of window.navigator.onLine is on the currently navigated page + * + * @param {boolean} offline + * True if offline is expected + */ +function assertOfflineStatus(offline) { + is( + Services.io.offline, + offline, + "Services.io.offline should be " + (offline ? "true" : "false") + ); + + return SpecialPowers.spawn(gBrowser.selectedBrowser, [offline], offline => { + is( + content.navigator.onLine, + !offline, + "Page should be " + (offline ? "offline" : "online") + ); + }); +} diff --git a/remote/test/browser/network/browser_getAllCookies.js b/remote/test/browser/network/browser_getAllCookies.js new file mode 100644 index 0000000000..e3d07faba6 --- /dev/null +++ b/remote/test/browser/network/browser_getAllCookies.js @@ -0,0 +1,225 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const SJS_PATH = "/browser/remote/test/browser/network/sjs-cookies.sjs"; + +const DEFAULT_HOST = "http://example.org"; +const ALT_HOST = "http://example.net"; +const SECURE_HOST = "https://example.com"; + +const DEFAULT_URL = `${DEFAULT_HOST}${SJS_PATH}`; + +add_task(async function noCookiesWhenNoneAreSet({ client }) { + const { Network } = client; + const { cookies } = await Network.getAllCookies(); + is(cookies.length, 0, "No cookies have been found"); +}); + +add_task(async function noCookiesForPristineContext({ client }) { + const { Network } = client; + await loadURL(DEFAULT_URL); + + try { + const { cookies } = await Network.getAllCookies(); + is(cookies.length, 0, "No cookies have been found"); + } finally { + Services.cookies.removeAll(); + } +}); + +add_task(async function allCookiesFromHostWithPort({ client }) { + const { Network } = client; + const PORT_URL = `${DEFAULT_HOST}:8000${SJS_PATH}?name=id&value=1`; + await loadURL(PORT_URL); + + const cookie = { + name: "id", + value: "1", + }; + + try { + const { cookies } = await Network.getAllCookies(); + is(cookies.length, 1, "All cookies have been found"); + assertCookie(cookies[0], cookie); + } finally { + Services.cookies.removeAll(); + } +}); + +add_task(async function allCookiesFromMultipleOrigins({ client }) { + const { Network } = client; + await loadURL(`${ALT_HOST}${SJS_PATH}?name=users&value=password`); + await loadURL(`${SECURE_HOST}${SJS_PATH}?name=secure&value=password`); + await loadURL(`${DEFAULT_URL}?name=foo&value=bar`); + + const cookie1 = { name: "foo", value: "bar", domain: "example.org" }; + const cookie2 = { name: "secure", value: "password", domain: "example.com" }; + const cookie3 = { name: "users", value: "password", domain: "example.net" }; + + try { + const { cookies } = await Network.getAllCookies(); + cookies.sort((a, b) => a.name.localeCompare(b.name)); + is(cookies.length, 3, "All cookies have been found"); + assertCookie(cookies[0], cookie1); + assertCookie(cookies[1], cookie2); + assertCookie(cookies[2], cookie3); + } finally { + Services.cookies.removeAll(); + } +}); + +add_task(async function secure({ client }) { + const { Network } = client; + await loadURL(`${SECURE_HOST}${SJS_PATH}?name=foo&value=bar&secure`); + + const cookie = { + name: "foo", + value: "bar", + domain: "example.com", + secure: true, + }; + + try { + // Cookie returned for secure protocols + let result = await Network.getAllCookies(); + is(result.cookies.length, 1, "The secure cookie has been found"); + assertCookie(result.cookies[0], cookie); + + // For unsecure protocols the secure cookies are also returned + await loadURL(DEFAULT_URL); + result = await Network.getAllCookies(); + is(result.cookies.length, 1, "The secure cookie has been found"); + assertCookie(result.cookies[0], cookie); + } finally { + Services.cookies.removeAll(); + } +}); + +add_task(async function expiry({ client }) { + const { Network } = client; + const date = new Date(); + date.setDate(date.getDate() + 3); + + const encodedDate = encodeURI(date.toUTCString()); + await loadURL(`${DEFAULT_URL}?name=foo&value=bar&expiry=${encodedDate}`); + + const cookie = { + name: "foo", + value: "bar", + expires: Math.floor(date.getTime() / 1000), + session: false, + }; + + try { + const { cookies } = await Network.getAllCookies(); + is(cookies.length, 1, "A single cookie has been found"); + assertCookie(cookies[0], cookie); + } finally { + Services.cookies.removeAll(); + } +}); + +add_task(async function session({ client }) { + const { Network } = client; + await loadURL(`${DEFAULT_URL}?name=foo&value=bar`); + + const cookie = { + name: "foo", + value: "bar", + expiry: -1, + session: true, + }; + + try { + const { cookies } = await Network.getAllCookies(); + is(cookies.length, 1, "A single cookie has been found"); + assertCookie(cookies[0], cookie); + } finally { + Services.cookies.removeAll(); + } +}); + +add_task(async function path({ client }) { + const { Network } = client; + const PATH = "/browser/remote/test/browser/"; + const PARENT_PATH = "/browser/remote/test/"; + + await loadURL(`${DEFAULT_URL}?name=foo&value=bar&path=${PATH}`); + + const cookie = { + name: "foo", + value: "bar", + path: PATH, + }; + + try { + console.log("Check exact path"); + await loadURL(`${DEFAULT_HOST}${PATH}`); + let result = await Network.getAllCookies(); + is(result.cookies.length, 1, "A single cookie has been found"); + assertCookie(result.cookies[0], cookie); + + console.log("Check sub path"); + await loadURL(`${DEFAULT_HOST}${SJS_PATH}`); + result = await Network.getAllCookies(); + is(result.cookies.length, 1, "A single cookie has been found"); + assertCookie(result.cookies[0], cookie); + + console.log("Check parent path"); + await loadURL(`${DEFAULT_HOST}${PARENT_PATH}`); + result = await Network.getAllCookies(); + is(result.cookies.length, 1, "A single cookie has been found"); + assertCookie(result.cookies[0], cookie); + + console.log("Check non matching path"); + await loadURL(`${DEFAULT_HOST}/foo/bar`); + result = await Network.getAllCookies(); + is(result.cookies.length, 1, "A single cookie has been found"); + assertCookie(result.cookies[0], cookie); + } finally { + Services.cookies.removeAll(); + } +}); + +add_task(async function httpOnly({ client }) { + const { Network } = client; + await loadURL(`${DEFAULT_URL}?name=foo&value=bar&httpOnly`); + + const cookie = { + name: "foo", + value: "bar", + httpOnly: true, + }; + + try { + const { cookies } = await Network.getAllCookies(); + is(cookies.length, 1, "A single cookie has been found"); + assertCookie(cookies[0], cookie); + } finally { + Services.cookies.removeAll(); + } +}); + +add_task(async function sameSite({ client }) { + const { Network } = client; + for (const value of ["Lax", "Strict"]) { + console.log(`Test cookie with SameSite=${value}`); + await loadURL(`${DEFAULT_URL}?name=foo&value=bar&sameSite=${value}`); + + const cookie = { + name: "foo", + value: "bar", + sameSite: value, + }; + + try { + const { cookies } = await Network.getAllCookies(); + is(cookies.length, 1, "A single cookie has been found"); + assertCookie(cookies[0], cookie); + } finally { + Services.cookies.removeAll(); + } + } +}); diff --git a/remote/test/browser/network/browser_getCookies.js b/remote/test/browser/network/browser_getCookies.js new file mode 100644 index 0000000000..c53fe9f0cc --- /dev/null +++ b/remote/test/browser/network/browser_getCookies.js @@ -0,0 +1,220 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const SJS_PATH = "/browser/remote/test/browser/network/sjs-cookies.sjs"; + +const DEFAULT_HOST = "http://example.org"; +const ALT_HOST = "http://example.net"; +const SECURE_HOST = "https://example.com"; + +const DEFAULT_URL = `${DEFAULT_HOST}${SJS_PATH}`; + +add_task(async function noCookiesWhenNoneAreSet({ client }) { + const { Network } = client; + const { cookies } = await Network.getCookies({ urls: [DEFAULT_HOST] }); + is(cookies.length, 0, "No cookies have been found"); +}); + +add_task(async function noCookiesForPristineContext({ client }) { + const { Network } = client; + await loadURL(DEFAULT_URL); + + try { + const { cookies } = await Network.getCookies(); + is(cookies.length, 0, "No cookies have been found"); + } finally { + Services.cookies.removeAll(); + } +}); + +add_task(async function allCookiesFromHostWithPort({ client }) { + const { Network } = client; + const PORT_URL = `${DEFAULT_HOST}:8000${SJS_PATH}?name=id&value=1`; + await loadURL(PORT_URL); + + const cookie = { + name: "id", + value: "1", + }; + + try { + const { cookies } = await Network.getCookies(); + is(cookies.length, 1, "All cookies have been found"); + assertCookie(cookies[0], cookie); + } finally { + Services.cookies.removeAll(); + } +}); + +add_task(async function allCookiesFromCurrentURL({ client }) { + const { Network } = client; + await loadURL(`${ALT_HOST}${SJS_PATH}?name=user&value=password`); + await loadURL(`${DEFAULT_URL}?name=foo&value=bar`); + await loadURL(`${DEFAULT_URL}?name=user&value=password`); + + const cookie1 = { name: "foo", value: "bar", domain: "example.org" }; + const cookie2 = { name: "user", value: "password", domain: "example.org" }; + + try { + const { cookies } = await Network.getCookies(); + cookies.sort((a, b) => a.name.localeCompare(b.name)); + is(cookies.length, 2, "All cookies have been found"); + assertCookie(cookies[0], cookie1); + assertCookie(cookies[1], cookie2); + } finally { + Services.cookies.removeAll(); + } +}); + +add_task(async function secure({ client }) { + const { Network } = client; + await loadURL(`${SECURE_HOST}${SJS_PATH}?name=foo&value=bar&secure`); + + const cookie = { + name: "foo", + value: "bar", + domain: "example.com", + secure: true, + }; + + try { + // Cookie returned for secure protocols + let result = await Network.getCookies(); + is(result.cookies.length, 1, "The secure cookie has been found"); + assertCookie(result.cookies[0], cookie); + + // For unsecure protocols no secure cookies are returned + await loadURL(DEFAULT_URL); + result = await Network.getCookies(); + is(result.cookies.length, 0, "No secure cookies have been found"); + } finally { + Services.cookies.removeAll(); + } +}); + +add_task(async function expiry({ client }) { + const { Network } = client; + const date = new Date(); + date.setDate(date.getDate() + 3); + + const encodedDate = encodeURI(date.toUTCString()); + await loadURL(`${DEFAULT_URL}?name=foo&value=bar&expiry=${encodedDate}`); + + const cookie = { + name: "foo", + value: "bar", + expires: Math.floor(date.getTime() / 1000), + session: false, + }; + + try { + const { cookies } = await Network.getCookies(); + is(cookies.length, 1, "A single cookie has been found"); + assertCookie(cookies[0], cookie); + } finally { + Services.cookies.removeAll(); + } +}); + +add_task(async function session({ client }) { + const { Network } = client; + await loadURL(`${DEFAULT_URL}?name=foo&value=bar`); + + const cookie = { + name: "foo", + value: "bar", + expiry: -1, + session: true, + }; + + try { + const { cookies } = await Network.getCookies(); + is(cookies.length, 1, "A single cookie has been found"); + assertCookie(cookies[0], cookie); + } finally { + Services.cookies.removeAll(); + } +}); + +add_task(async function path({ client }) { + const { Network } = client; + const PATH = "/browser/remote/test/browser/"; + const PARENT_PATH = "/browser/remote/test/"; + + await loadURL(`${DEFAULT_URL}?name=foo&value=bar&path=${PATH}`); + + const cookie = { + name: "foo", + value: "bar", + path: PATH, + }; + + try { + console.log("Check exact path"); + await loadURL(`${DEFAULT_HOST}${PATH}`); + let result = await Network.getCookies(); + is(result.cookies.length, 1, "A single cookie has been found"); + assertCookie(result.cookies[0], cookie); + + console.log("Check sub path"); + await loadURL(`${DEFAULT_HOST}${SJS_PATH}`); + result = await Network.getCookies(); + is(result.cookies.length, 1, "A single cookie has been found"); + assertCookie(result.cookies[0], cookie); + + console.log("Check parent path"); + await loadURL(`${DEFAULT_HOST}${PARENT_PATH}`); + result = await Network.getCookies(); + is(result.cookies.length, 0, "No cookies have been found"); + + console.log("Check non matching path"); + await loadURL(`${DEFAULT_HOST}/foo/bar`); + result = await Network.getCookies(); + is(result.cookies.length, 0, "No cookies have been found"); + } finally { + Services.cookies.removeAll(); + } +}); + +add_task(async function httpOnly({ client }) { + const { Network } = client; + await loadURL(`${DEFAULT_URL}?name=foo&value=bar&httpOnly`); + + const cookie = { + name: "foo", + value: "bar", + httpOnly: true, + }; + + try { + const { cookies } = await Network.getCookies(); + is(cookies.length, 1, "A single cookie has been found"); + assertCookie(cookies[0], cookie); + } finally { + Services.cookies.removeAll(); + } +}); + +add_task(async function sameSite({ client }) { + const { Network } = client; + for (const value of ["Lax", "Strict"]) { + console.log(`Test cookie with SameSite=${value}`); + await loadURL(`${DEFAULT_URL}?name=foo&value=bar&sameSite=${value}`); + + const cookie = { + name: "foo", + value: "bar", + sameSite: value, + }; + + try { + const { cookies } = await Network.getCookies(); + is(cookies.length, 1, "A single cookie has been found"); + assertCookie(cookies[0], cookie); + } finally { + Services.cookies.removeAll(); + } + } +}); diff --git a/remote/test/browser/network/browser_navigationEvents.js b/remote/test/browser/network/browser_navigationEvents.js new file mode 100644 index 0000000000..4f08821418 --- /dev/null +++ b/remote/test/browser/network/browser_navigationEvents.js @@ -0,0 +1,202 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test order and consistency of Network/Page events as a whole. +// Details of specific events are checked in event-specific test files. + +const BASE_PATH = "http://example.com/browser/remote/test/browser/network"; +const FRAMESET_URL = `${BASE_PATH}/doc_frameset.html`; +const FRAMESET_JS_URL = `${BASE_PATH}/file_framesetEvents.js`; +const PAGE_URL = `${BASE_PATH}/doc_networkEvents.html`; +const PAGE_JS_URL = `${BASE_PATH}/file_networkEvents.js`; + +add_task(async function eventsForTopFrameNavigation({ client }) { + const { history, frameId: frameIdNav } = await prepareTest( + client, + FRAMESET_URL, + 10 + ); + + const documentEvents = filterEventsByType(history, "Document"); + const scriptEvents = filterEventsByType(history, "Script"); + const subdocumentEvents = filterEventsByType(history, "Subdocument"); + + is(documentEvents.length, 2, "Expected number of Document events"); + is(subdocumentEvents.length, 2, "Expected number of Subdocument events"); + is(scriptEvents.length, 4, "Expected number of Script events"); + + const navigatedEvents = history.findEvents("Page.navigate"); + is(navigatedEvents.length, 1, "Expected number of navigate done events"); + + const frameAttachedEvents = history.findEvents("Page.frameAttached"); + is(frameAttachedEvents.length, 1, "Expected number of frame attached events"); + + // network events for document and script + assertEventOrder(documentEvents[0], documentEvents[1]); + assertEventOrder(documentEvents[1], navigatedEvents[0], { + ignoreTimestamps: true, + }); + assertEventOrder(navigatedEvents[0], scriptEvents[0], { + ignoreTimestamps: true, + }); + assertEventOrder(scriptEvents[0], scriptEvents[1]); + + const docRequest = documentEvents[0].payload; + is(docRequest.documentURL, FRAMESET_URL, "documenURL matches target url"); + is(docRequest.frameId, frameIdNav, "Got the expected frame id"); + is(docRequest.request.url, FRAMESET_URL, "Got the Document request"); + + const docResponse = documentEvents[1].payload; + is(docResponse.frameId, frameIdNav, "Got the expected frame id"); + is(docResponse.response.url, FRAMESET_URL, "Got the Document response"); + ok(!!docResponse.response.headers.server, "Document response has headers"); + // TODO? response reports extra request header "upgrade-insecure-requests":"1" + // Assert.deepEqual( + // docResponse.response.requestHeaders, + // docRequest.request.headers, + // "Response event reports same request headers as request event" + // ); + + const scriptRequest = scriptEvents[0].payload; + is( + scriptRequest.documentURL, + FRAMESET_URL, + "documentURL is trigger document" + ); + is(scriptRequest.frameId, frameIdNav, "Got the expected frame id"); + is(scriptRequest.request.url, FRAMESET_JS_URL, "Got the Script request"); + + const scriptResponse = scriptEvents[1].payload; + is(scriptResponse.frameId, frameIdNav, "Got the expected frame id"); + todo( + scriptResponse.loaderId === docRequest.loaderId, + "The same loaderId is used for dependent responses (Bug 1637838)" + ); + is(scriptResponse.response.url, FRAMESET_JS_URL, "Got the Script response"); + Assert.deepEqual( + scriptResponse.response.requestHeaders, + scriptRequest.request.headers, + "Response event reports same request headers as request event" + ); + + // frame is attached after all resources of the document have been loaded + // and before sub document starts loading + assertEventOrder(scriptEvents[1], frameAttachedEvents[0], { + ignoreTimestamps: true, + }); + assertEventOrder(frameAttachedEvents[0], subdocumentEvents[0], { + ignoreTimestamps: true, + }); + + const { + frameId: frameIdSubFrame, + parentFrameId, + } = frameAttachedEvents[0].payload; + is(parentFrameId, frameIdNav, "Got expected parent frame id"); + + // network events for subdocument and script + assertEventOrder(subdocumentEvents[0], subdocumentEvents[1]); + assertEventOrder(subdocumentEvents[1], scriptEvents[2]); + assertEventOrder(scriptEvents[2], scriptEvents[3]); + + const subdocRequest = subdocumentEvents[0].payload; + is( + subdocRequest.documentURL, + FRAMESET_URL, + "documentURL is trigger document" + ); + is(subdocRequest.frameId, frameIdSubFrame, "Got the expected frame id"); + is(subdocRequest.request.url, PAGE_URL, "Got the Subdocument request"); + + const subdocResponse = subdocumentEvents[1].payload; + is(subdocResponse.frameId, frameIdSubFrame, "Got the expected frame id"); + is(subdocResponse.response.url, PAGE_URL, "Got the Subdocument response"); + + const subscriptRequest = scriptEvents[2].payload; + is(subscriptRequest.documentURL, PAGE_URL, "documentURL is trigger document"); + is(subscriptRequest.frameId, frameIdSubFrame, "Got the expected frame id"); + is(subscriptRequest.request.url, PAGE_JS_URL, "Got the Script request"); + + const subscriptResponse = scriptEvents[3].payload; + is(subscriptResponse.frameId, frameIdSubFrame, "Got the expected frame id"); + is(subscriptResponse.response.url, PAGE_JS_URL, "Got the Script response"); + todo( + subscriptResponse.loaderId === subdocRequest.loaderId, + "The same loaderId is used for dependent responses (Bug 1637838)" + ); + Assert.deepEqual( + subscriptResponse.response.requestHeaders, + subscriptRequest.request.headers, + "Response event reports same request headers as request event" + ); + + const lifeCycleEvents = history + .findEvents("Page.lifecycleEvent") + .map(event => event.payload); + for (const { name, loaderId } of lifeCycleEvents) { + is( + loaderId, + docRequest.loaderId, + `${name} lifecycle event has same loaderId as Document request` + ); + } +}); + +async function prepareTest(client, url, totalCount) { + const REQUEST = "Network.requestWillBeSent"; + const RESPONSE = "Network.responseReceived"; + const FRAMEATTACHED = "Page.frameAttached"; + const LIFECYCLE = "Page.livecycleEvent"; + + const { Network, Page } = client; + const history = new RecordEvents(totalCount); + + history.addRecorder({ + event: Network.requestWillBeSent, + eventName: REQUEST, + messageFn: payload => { + return `Received ${REQUEST} for ${payload.request?.url}`; + }, + }); + + history.addRecorder({ + event: Network.responseReceived, + eventName: RESPONSE, + messageFn: payload => { + return `Received ${RESPONSE} for ${payload.response?.url}`; + }, + }); + + history.addRecorder({ + event: Page.frameAttached, + eventName: FRAMEATTACHED, + messageFn: ({ frameId, parentFrameId: parentId }) => { + return `Received ${FRAMEATTACHED} frame=${frameId} parent=${parentId}`; + }, + }); + + history.addRecorder({ + event: Page.lifecycleEvent, + eventName: LIFECYCLE, + messageFn: payload => { + return `Received ${LIFECYCLE} ${payload.name}`; + }, + }); + + await Network.enable(); + await Page.enable(); + + const navigateDone = history.addPromise("Page.navigate"); + const { frameId } = await Page.navigate({ url }).then(navigateDone); + ok(frameId, "Page.navigate returned a frameId"); + + info("Wait for events"); + const events = await history.record(); + + info(`Received events: ${events.map(getDescriptionForEvent)}`); + is(events.length, totalCount, "Received expected number of events"); + + return { history, frameId }; +} diff --git a/remote/test/browser/network/browser_requestWillBeSent.js b/remote/test/browser/network/browser_requestWillBeSent.js new file mode 100644 index 0000000000..274bf50e51 --- /dev/null +++ b/remote/test/browser/network/browser_requestWillBeSent.js @@ -0,0 +1,154 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const BASE_PATH = "http://example.com/browser/remote/test/browser/network"; +const FRAMESET_URL = `${BASE_PATH}/doc_frameset.html`; +const FRAMESET_JS_URL = `${BASE_PATH}/file_framesetEvents.js`; +const PAGE_URL = `${BASE_PATH}/doc_networkEvents.html`; +const PAGE_JS_URL = `${BASE_PATH}/file_networkEvents.js`; + +add_task(async function noEventsWhenNetworkDomainDisabled({ client }) { + const history = configureHistory(client, 0); + await loadURL(PAGE_URL); + + const events = await history.record(); + is(events.length, 0, "Expected no Network.responseReceived events"); +}); + +add_task(async function noEventsAfterNetworkDomainDisabled({ client }) { + const { Network } = client; + + const history = configureHistory(client, 0); + await Network.enable(); + await Network.disable(); + await loadURL(PAGE_URL); + + const events = await history.record(); + is(events.length, 0, "Expected no Network.responseReceived events"); +}); + +add_task(async function documentNavigationWithResource({ client }) { + const { Page, Network } = client; + + await Network.enable(); + await Page.enable(); + + const history = configureHistory(client, 4); + + const frameAttached = Page.frameAttached(); + const { frameId: frameIdNav } = await Page.navigate({ url: FRAMESET_URL }); + const { frameId: frameIdSubFrame } = await frameAttached; + ok(frameIdNav, "Page.navigate returned a frameId"); + + info("Wait for Network events"); + const events = await history.record(); + is(events.length, 4, "Expected number of Network.requestWillBeSent events"); + + // Check top-level document request + const docRequest = events[0].payload; + is(docRequest.type, "Document", "Document request has the expected type"); + is(docRequest.documentURL, FRAMESET_URL, "documentURL matches requested url"); + is(docRequest.frameId, frameIdNav, "Got the expected frame id"); + is(docRequest.request.url, FRAMESET_URL, "Got the Document request"); + is(docRequest.request.method, "GET", "Has the expected request method"); + is( + docRequest.requestId, + docRequest.loaderId, + "The request id is equal to the loader id" + ); + is( + docRequest.request.headers.host, + "example.com", + "Document request has headers" + ); + + // Check top-level script request + const scriptRequest = events[1].payload; + is(scriptRequest.type, "Script", "Script request has the expected type"); + is( + scriptRequest.documentURL, + FRAMESET_URL, + "documentURL is trigger document for the script request" + ); + is(scriptRequest.frameId, frameIdNav, "Got the expected frame id"); + is(scriptRequest.request.url, FRAMESET_JS_URL, "Got the Script request"); + is(scriptRequest.request.method, "GET", "Has the expected request method"); + is( + scriptRequest.request.headers.host, + "example.com", + "Script request has headers" + ); + todo( + scriptRequest.loaderId === docRequest.loaderId, + "The same loaderId is used for dependent requests (Bug 1637838)" + ); + assertEventOrder(events[0], events[1]); + + // Check subdocument request + const subdocRequest = events[2].payload; + is( + subdocRequest.type, + "Subdocument", + "Subdocument request has the expected type" + ); + is(subdocRequest.documentURL, FRAMESET_URL, "documenURL matches request url"); + is(subdocRequest.frameId, frameIdSubFrame, "Got the expected frame id"); + is( + subdocRequest.requestId, + subdocRequest.loaderId, + "The request id is equal to the loader id" + ); + is(subdocRequest.request.url, PAGE_URL, "Got the Subdocument request"); + is(subdocRequest.request.method, "GET", "Has the expected request method"); + is( + subdocRequest.request.headers.host, + "example.com", + "Subdocument request has headers" + ); + assertEventOrder(events[1], events[2]); + + // Check script request (frame) + const subscriptRequest = events[3].payload; + is(subscriptRequest.type, "Script", "Script request has the expected type"); + is( + subscriptRequest.documentURL, + PAGE_URL, + "documentURL is trigger document for the script request" + ); + is(subscriptRequest.frameId, frameIdSubFrame, "Got the expected frame id"); + todo( + subscriptRequest.loaderId === docRequest.loaderId, + "The same loaderId is used for dependent requests (Bug 1637838)" + ); + is(subscriptRequest.request.url, PAGE_JS_URL, "Got the Script request"); + is( + subscriptRequest.request.method, + "GET", + "Script request has the expected method" + ); + is( + subscriptRequest.request.headers.host, + "example.com", + "Script request has headers" + ); + assertEventOrder(events[2], events[3]); +}); + +function configureHistory(client, total) { + const REQUEST = "Network.requestWillBeSent"; + + const { Network } = client; + const history = new RecordEvents(total); + + history.addRecorder({ + event: Network.requestWillBeSent, + eventName: REQUEST, + messageFn: payload => { + return `Received ${REQUEST} for ${payload.request.url}`; + }, + }); + + return history; +} diff --git a/remote/test/browser/network/browser_responseReceived.js b/remote/test/browser/network/browser_responseReceived.js new file mode 100644 index 0000000000..a258c95010 --- /dev/null +++ b/remote/test/browser/network/browser_responseReceived.js @@ -0,0 +1,236 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const BASE_PATH = "http://example.com/browser/remote/test/browser/network"; +const FRAMESET_URL = `${BASE_PATH}/doc_frameset.html`; +const FRAMESET_JS_URL = `${BASE_PATH}/file_framesetEvents.js`; +const PAGE_URL = `${BASE_PATH}/doc_networkEvents.html`; +const PAGE_JS_URL = `${BASE_PATH}/file_networkEvents.js`; + +add_task(async function noEventsWhenNetworkDomainDisabled({ client }) { + const history = configureHistory(client, 0); + await loadURL(PAGE_URL); + + const events = await history.record(); + is(events.length, 0, "Expected no Network.responseReceived events"); +}); + +add_task(async function noEventsAfterNetworkDomainDisabled({ client }) { + const { Network } = client; + + const history = configureHistory(client, 0); + await Network.enable(); + await Network.disable(); + await loadURL(PAGE_URL); + + const events = await history.record(); + is(events.length, 0, "Expected no Network.responseReceived events"); +}); + +add_task(async function documentNavigationWithResource({ client }) { + const { Page, Network } = client; + + await Network.enable(); + await Page.enable(); + + const history = configureHistory(client, 4); + + const frameAttached = Page.frameAttached(); + const { frameId: frameIdNav } = await Page.navigate({ url: FRAMESET_URL }); + const { frameId: frameIdSubframe } = await frameAttached; + ok(frameIdNav, "Page.navigate returned a frameId"); + + info("Wait for Network events"); + const events = await history.record(); + is(events.length, 4, "Expected number of Network.responseReceived events"); + + // Check top-level document response + const docResponse = events[0].payload; + is(docResponse.type, "Document", "Document response has expected type"); + is(docResponse.frameId, frameIdNav, "Got the expected frame id"); + is( + docResponse.requestId, + docResponse.loaderId, + "The response id is equal to the loader id" + ); + is(docResponse.response.url, FRAMESET_URL, "Got the Document response"); + is( + docResponse.response.mimeType, + "text/html", + "Document response has expected mimeType" + ); + ok(!!docResponse.response.headers.server, "Document response has headers"); + is(docResponse.response.status, 200, "Document response has expected status"); + is( + docResponse.response.statusText, + "OK", + "Document response has expected status text" + ); + if (docResponse.response.fromDiskCache === false) { + is( + docResponse.response.remoteIPAddress, + "127.0.0.1", + "Document response has the expected IP address" + ); + ok( + typeof docResponse.response.remotePort == "number", + "Document response has a remotePort" + ); + } + is( + docResponse.response.protocol, + "http/1.1", + "Document response has expected protocol" + ); + + // Check top-level script response + const scriptResponse = events[1].payload; + is(scriptResponse.type, "Script", "Script response has expected type"); + is(scriptResponse.frameId, frameIdNav, "Got the expected frame id"); + is(scriptResponse.response.url, FRAMESET_JS_URL, "Got the Script response"); + is( + scriptResponse.response.mimeType, + "application/x-javascript", + "Script response has expected mimeType" + ); + ok(!!scriptResponse.response.headers.server, "Script response has headers"); + is( + scriptResponse.response.status, + 200, + "Script response has the expected status" + ); + is( + scriptResponse.response.statusText, + "OK", + "Script response has the expected status text" + ); + if (scriptResponse.response.fromDiskCache === false) { + is( + scriptResponse.response.remoteIPAddress, + docResponse.response.remoteIPAddress, + "Script response has same IP address as document response" + ); + ok( + typeof scriptResponse.response.remotePort == "number", + "Script response has a remotePort" + ); + } + is( + scriptResponse.response.protocol, + "http/1.1", + "Script response has the expected protocol" + ); + + // Check subdocument response + const frameDocResponse = events[2].payload; + is( + frameDocResponse.type, + "Subdocument", + "Subdocument response has expected type" + ); + is(frameDocResponse.frameId, frameIdSubframe, "Got the expected frame id"); + is( + frameDocResponse.requestId, + frameDocResponse.loaderId, + "The response id is equal to the loader id" + ); + is( + frameDocResponse.response.url, + PAGE_URL, + "Got the expected Document response" + ); + is( + frameDocResponse.response.mimeType, + "text/html", + "Document response has expected mimeType" + ); + ok( + !!frameDocResponse.response.headers.server, + "Subdocument response has headers" + ); + is( + frameDocResponse.response.status, + 200, + "Subdocument response has expected status" + ); + is( + frameDocResponse.response.statusText, + "OK", + "Subdocument response has expected status text" + ); + if (frameDocResponse.response.fromDiskCache === false) { + is( + frameDocResponse.response.remoteIPAddress, + "127.0.0.1", + "Subdocument response has the expected IP address" + ); + ok( + typeof frameDocResponse.response.remotePort == "number", + "Subdocument response has a remotePort" + ); + } + is( + frameDocResponse.response.protocol, + "http/1.1", + "Subdocument response has expected protocol" + ); + + // Check frame script response + const frameScriptResponse = events[3].payload; + is(frameScriptResponse.type, "Script", "Script response has expected type"); + is(frameScriptResponse.frameId, frameIdSubframe, "Got the expected frame id"); + is(frameScriptResponse.response.url, PAGE_JS_URL, "Got the Script response"); + is( + frameScriptResponse.response.mimeType, + "application/x-javascript", + "Script response has expected mimeType" + ); + ok( + !!frameScriptResponse.response.headers.server, + "Script response has headers" + ); + is( + frameScriptResponse.response.status, + 200, + "Script response has the expected status" + ); + is( + frameScriptResponse.response.statusText, + "OK", + "Script response has the expected status text" + ); + if (frameScriptResponse.response.fromDiskCache === false) { + is( + frameScriptResponse.response.remoteIPAddress, + docResponse.response.remoteIPAddress, + "Script response has same IP address as document response" + ); + ok( + typeof frameScriptResponse.response.remotePort == "number", + "Script response has a remotePort" + ); + } + is( + frameScriptResponse.response.protocol, + "http/1.1", + "Script response has the expected protocol" + ); +}); + +function configureHistory(client, total) { + const RESPONSE = "Network.responseReceived"; + + const { Network } = client; + const history = new RecordEvents(total); + + history.addRecorder({ + event: Network.responseReceived, + eventName: RESPONSE, + messageFn: payload => { + return `Received ${RESPONSE} for ${payload.response.url}`; + }, + }); + return history; +} diff --git a/remote/test/browser/network/browser_setCacheDisabled.js b/remote/test/browser/network/browser_setCacheDisabled.js new file mode 100644 index 0000000000..610c4b7531 --- /dev/null +++ b/remote/test/browser/network/browser_setCacheDisabled.js @@ -0,0 +1,122 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { INHIBIT_CACHING, LOAD_BYPASS_CACHE, LOAD_NORMAL } = Ci.nsIRequest; + +const TEST_PAGE = + "http://example.com/browser/remote/test/browser/network/doc_empty.html"; + +add_task(async function cacheEnabledAfterDisabled({ client }) { + const { Network } = client; + await Network.setCacheDisabled({ cacheDisabled: true }); + await Network.setCacheDisabled({ cacheDisabled: false }); + + const checkPromise = checkLoadFlags(LOAD_NORMAL, TEST_PAGE); + await loadURL(TEST_PAGE); + await checkPromise; +}); + +add_task(async function cacheEnabledByDefault({ Network }) { + const checkPromise = checkLoadFlags(LOAD_NORMAL, TEST_PAGE); + await loadURL(TEST_PAGE); + await checkPromise; +}); + +add_task(async function cacheDisabled({ client }) { + const { Network } = client; + await Network.setCacheDisabled({ cacheDisabled: true }); + + const checkPromise = checkLoadFlags( + LOAD_BYPASS_CACHE | INHIBIT_CACHING, + TEST_PAGE + ); + await loadURL(TEST_PAGE); + await checkPromise; +}); + +function checkLoadFlags(flags, url) { + return ContentTask.spawn( + gBrowser.selectedBrowser, + { flags, url }, + async (options = {}) => { + const { flags, url } = options; + + // an nsIWebProgressListener that checks all requests made by the docShell + // have the flags we expect. + var RequestWatcher = { + init(docShell, expectedLoadFlags, url, callback) { + this.callback = callback; + this.docShell = docShell; + this.expectedLoadFlags = expectedLoadFlags; + this.url = url; + + this.requestCount = 0; + + const { + NOTIFY_STATE_DOCUMENT, + NOTIFY_STATE_REQUEST, + } = Ci.nsIWebProgress; + + this.docShell + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebProgress) + .addProgressListener( + this, + NOTIFY_STATE_DOCUMENT | NOTIFY_STATE_REQUEST + ); + }, + + onStateChange(webProgress, request, flags, status) { + // We are checking requests - if there isn't one, ignore it. + if (!request) { + return; + } + + // We will usually see requests for 'about:document-onload-blocker' not + // have the flag, so we just ignore them. + // We also see, eg, resource://gre-resources/loading-image.png, so + // skip resource:// URLs too. + // We may also see, eg, chrome://global/skin/icons/chevron.svg, so + // skip chrome:// URLs too. + if ( + request.name.startsWith("about:") || + request.name.startsWith("resource:") || + request.name.startsWith("chrome:") + ) { + return; + } + is( + request.loadFlags & this.expectedLoadFlags, + this.expectedLoadFlags, + "request " + request.name + " has the expected flags" + ); + this.requestCount += 1; + + var stopFlags = + Ci.nsIWebProgressListener.STATE_STOP | + Ci.nsIWebProgressListener.STATE_IS_DOCUMENT; + + if (request.name == this.url && (flags & stopFlags) == stopFlags) { + this.docShell.removeProgressListener(this); + ok( + this.requestCount > 1, + this.url + " saw " + this.requestCount + " requests" + ); + this.callback(); + } + }, + + QueryInterface: ChromeUtils.generateQI([ + "nsIWebProgressListener", + "nsISupportsWeakReference", + ]), + }; + + await new Promise(resolve => { + RequestWatcher.init(docShell, flags, url, resolve); + }); + } + ); +} diff --git a/remote/test/browser/network/browser_setCookie.js b/remote/test/browser/network/browser_setCookie.js new file mode 100644 index 0000000000..bb16e65c56 --- /dev/null +++ b/remote/test/browser/network/browser_setCookie.js @@ -0,0 +1,298 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const SJS_PATH = "/browser/remote/test/browser/network/sjs-cookies.sjs"; + +const DEFAULT_HOST = "example.org"; +const ALT_HOST = "foo.example.org"; +const SECURE_HOST = "example.com"; + +const DEFAULT_URL = `http://${DEFAULT_HOST}`; + +add_task(async function failureWithoutArguments({ client }) { + const { Network } = client; + + let errorThrown = false; + try { + await Network.setCookie(); + } catch (e) { + errorThrown = true; + } + ok(errorThrown, "Fails without any arguments"); +}); + +add_task(async function failureWithMissingNameAndValue({ client }) { + const { Network } = client; + + let errorThrown = false; + try { + await Network.setCookie({ + value: "bar", + domain: "example.org", + }); + } catch (e) { + errorThrown = true; + } + ok(errorThrown, "Fails without name specified"); + + errorThrown = false; + try { + await Network.setCookie({ + name: "foo", + domain: "example.org", + }); + } catch (e) { + errorThrown = true; + } + ok(errorThrown, "Fails without value specified"); +}); + +add_task(async function failureWithMissingDomainAndURL({ client }) { + const { Network } = client; + + let errorThrown = false; + try { + await Network.setCookie({ name: "foo", value: "bar" }); + } catch (e) { + errorThrown = true; + } + ok(errorThrown, "Fails without domain and URL specified"); +}); + +add_task(async function setCookieWithDomain({ client }) { + const { Network } = client; + + const cookie = { + name: "foo", + value: "bar", + domain: ALT_HOST, + }; + + try { + const { success } = await Network.setCookie(cookie); + ok(success, "Cookie has been set"); + + const cookies = getCookies(); + is(cookies.length, 1, "A single cookie has been found"); + assertCookie(cookies[0], cookie); + } finally { + Services.cookies.removeAll(); + } +}); + +add_task(async function setCookieWithEmptyDomain({ client }) { + const { Network } = client; + + try { + const { success } = await Network.setCookie({ + name: "foo", + value: "bar", + url: "", + }); + ok(!success, "Cookie has not been set"); + + const cookies = getCookies(); + is(cookies.length, 0, "No cookie has been found"); + } finally { + Services.cookies.removeAll(); + } +}); + +add_task(async function setCookieWithURL({ client }) { + const { Network } = client; + + const cookie = { + name: "foo", + value: "bar", + domain: ALT_HOST, + }; + + try { + const { success } = await Network.setCookie({ + name: cookie.name, + value: cookie.value, + url: `http://${ALT_HOST}`, + }); + ok(success, "Cookie has been set"); + + const cookies = getCookies(); + is(cookies.length, 1, "A single cookie has been found"); + assertCookie(cookies[0], cookie); + } finally { + Services.cookies.removeAll(); + } +}); + +add_task(async function setCookieWithEmptyURL({ client }) { + const { Network } = client; + + try { + const { success } = await Network.setCookie({ + name: "foo", + value: "bar", + url: "", + }); + ok(!success, "No cookie has been set"); + + const cookies = getCookies(); + is(cookies.length, 0, "No cookie has been found"); + } finally { + Services.cookies.removeAll(); + } +}); + +add_task(async function setCookieWithDomainAndURL({ client }) { + const { Network } = client; + + const cookie = { + name: "foo", + value: "bar", + domain: ALT_HOST, + }; + + try { + const { success } = await Network.setCookie({ + name: cookie.name, + value: cookie.value, + domain: cookie.domain, + url: `http://${DEFAULT_HOST}`, + }); + ok(success, "Cookie has been set"); + + const cookies = getCookies(); + is(cookies.length, 1, "A single cookie has been found"); + assertCookie(cookies[0], cookie); + } finally { + Services.cookies.removeAll(); + } +}); + +add_task(async function setCookieWithHttpOnly({ client }) { + const { Network } = client; + + const cookie = { + name: "foo", + value: "bar", + domain: DEFAULT_HOST, + httpOnly: true, + }; + + try { + const { success } = await Network.setCookie(cookie); + ok(success, "Cookie has been set"); + + const cookies = getCookies(); + is(cookies.length, 1, "A single cookie has been found"); + assertCookie(cookies[0], cookie); + } finally { + Services.cookies.removeAll(); + } +}); + +add_task(async function setCookieWithExpiry({ client }) { + const { Network } = client; + + const tomorrow = Math.floor(Date.now() / 1000) + 60 * 60 * 24; + + const cookie = { + name: "foo", + value: "bar", + domain: DEFAULT_HOST, + expires: tomorrow, + session: false, + }; + + try { + const { success } = await Network.setCookie(cookie); + ok(success, "Cookie has been set"); + + const cookies = getCookies(); + is(cookies.length, 1, "A single cookie has been found"); + assertCookie(cookies[0], cookie); + } finally { + Services.cookies.removeAll(); + } +}); + +add_task(async function setCookieWithPath({ client }) { + const { Network } = client; + + const cookie = { + name: "foo", + value: "bar", + domain: ALT_HOST, + path: SJS_PATH, + }; + + try { + const { success } = await Network.setCookie(cookie); + ok(success, "Cookie has been set"); + + const cookies = getCookies(); + is(cookies.length, 1, "A single cookie has been found"); + assertCookie(cookies[0], cookie); + } finally { + Services.cookies.removeAll(); + } +}); + +add_task(async function testAddSameSiteCookie({ client }) { + const { Network } = client; + + for (const sameSite of ["None", "Lax", "Strict"]) { + console.log(`Check same site value: ${sameSite}`); + const cookie = { + name: "foo", + value: "bar", + domain: DEFAULT_HOST, + }; + if (sameSite != "None") { + cookie.sameSite = sameSite; + } + + try { + const { success } = await Network.setCookie({ + name: cookie.name, + value: cookie.value, + domain: cookie.domain, + sameSite, + }); + ok(success, "Cookie has been set"); + + const cookies = getCookies(); + is(cookies.length, 1, "A single cookie has been found"); + assertCookie(cookies[0], cookie); + } finally { + Services.cookies.removeAll(); + } + } +}); + +add_task(async function testAddSecureCookie({ client }) { + const { Network } = client; + + const cookie = { + name: "foo", + value: "bar", + domain: "example.com", + secure: true, + }; + + try { + const { success } = await Network.setCookie({ + name: cookie.name, + value: cookie.value, + url: `https://${SECURE_HOST}`, + }); + ok(success, "Cookie has been set"); + + const cookies = getCookies(); + is(cookies.length, 1, "A single cookie has been found"); + assertCookie(cookies[0], cookie); + ok(cookies[0].secure, `Cookie for HTTPS is secure`); + } finally { + Services.cookies.removeAll(); + } +}); diff --git a/remote/test/browser/network/browser_setCookies.js b/remote/test/browser/network/browser_setCookies.js new file mode 100644 index 0000000000..c89a68ca16 --- /dev/null +++ b/remote/test/browser/network/browser_setCookies.js @@ -0,0 +1,70 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const ALT_HOST = "foo.example.org"; +const DEFAULT_HOST = "example.org"; + +add_task(async function failureWithoutArguments({ client }) { + const { Network } = client; + + let errorThrown = false; + try { + await Network.setCookies(); + } catch (e) { + errorThrown = true; + } + ok(errorThrown, "Fails without any arguments"); +}); + +add_task(async function setCookies({ client }) { + const { Network } = client; + + const expected_cookies = [ + { + name: "foo", + value: "bar", + domain: DEFAULT_HOST, + }, + { + name: "user", + value: "password", + domain: ALT_HOST, + }, + ]; + + try { + await Network.setCookies({ cookies: expected_cookies }); + + const cookies = getCookies(); + cookies.sort((a, b) => a.name.localeCompare(b.name)); + is(cookies.length, expected_cookies.length, "Correct number of cookies"); + assertCookie(cookies[0], expected_cookies[0]); + assertCookie(cookies[1], expected_cookies[1]); + } finally { + Services.cookies.removeAll(); + } +}); + +add_task(async function setCookiesWithInvalidField({ client }) { + const { Network } = client; + + const cookies = [ + { + name: "foo", + value: "bar", + domain: "", + }, + ]; + + let errorThrown = false; + try { + await Network.setCookies({ cookies }); + } catch (e) { + errorThrown = true; + } finally { + Services.cookies.removeAll(); + } + ok(errorThrown, "Fails with an invalid field"); +}); diff --git a/remote/test/browser/network/browser_setUserAgentOverride.js b/remote/test/browser/network/browser_setUserAgentOverride.js new file mode 100644 index 0000000000..911a0a296c --- /dev/null +++ b/remote/test/browser/network/browser_setUserAgentOverride.js @@ -0,0 +1,68 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const DOC = toDataURL(`<script>document.write(navigator.userAgent);</script>`); + +// Network.setUserAgentOverride is a redirect to Emulation.setUserAgentOverride. +// Run at least one test for setting and resetting the user agent to make sure +// that the redirect works. + +add_task(async function forwardToEmulation({ client }) { + const { Network } = client; + const userAgent = "Mozilla/5.0 (rv: 23) Romanesco/42.0"; + const platform = "foobar"; + + await loadURL(DOC); + const originalUserAgent = await getNavigatorProperty("userAgent"); + const originalPlatform = await getNavigatorProperty("platform"); + + isnot(originalUserAgent, userAgent, "Custom user agent hasn't been set"); + isnot(originalPlatform, platform, "Custom platform hasn't been set"); + + await Network.setUserAgentOverride({ userAgent, platform }); + await loadURL(DOC); + is( + await getNavigatorProperty("userAgent"), + userAgent, + "Custom user agent has been set" + ); + is( + await getNavigatorProperty("platform"), + platform, + "Custom platform has been set" + ); + + await Network.setUserAgentOverride({ userAgent: "", platform: "" }); + await loadURL(DOC); + is( + await getNavigatorProperty("userAgent"), + originalUserAgent, + "Custom user agent has been reset" + ); + is( + await getNavigatorProperty("platform"), + originalPlatform, + "Custom platform has been reset" + ); + + await Network.setUserAgentOverride({ userAgent, platform }); + await loadURL(DOC); + is( + await getNavigatorProperty("userAgent"), + userAgent, + "Custom user agent has been set" + ); + is( + await getNavigatorProperty("platform"), + platform, + "Custom platform has been set" + ); +}); + +async function getNavigatorProperty(prop) { + return SpecialPowers.spawn(gBrowser.selectedBrowser, [prop], _prop => { + return content.navigator[_prop]; + }); +} diff --git a/remote/test/browser/network/doc_empty.html b/remote/test/browser/network/doc_empty.html new file mode 100644 index 0000000000..effbcb5fac --- /dev/null +++ b/remote/test/browser/network/doc_empty.html @@ -0,0 +1,9 @@ +<!DOCTYPE html> +<html> +<head> + <title>Test page</title> +</head> +<body> + <div id="content">Example page</div> +</body> +</html> diff --git a/remote/test/browser/network/doc_frameset.html b/remote/test/browser/network/doc_frameset.html new file mode 100644 index 0000000000..9fa4fb80e3 --- /dev/null +++ b/remote/test/browser/network/doc_frameset.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<html> +<head> + <title>Frameset for Network events</title> + <script type="text/javascript" src="file_framesetEvents.js"></script> +</head> +<body> + <iframe src="doc_networkEvents.html"></iframe> +</body> +</html> diff --git a/remote/test/browser/network/doc_networkEvents.html b/remote/test/browser/network/doc_networkEvents.html new file mode 100644 index 0000000000..51482954cc --- /dev/null +++ b/remote/test/browser/network/doc_networkEvents.html @@ -0,0 +1,9 @@ +<!DOCTYPE html> +<html> +<head> + <title>Test page for Network events</title> + <script type="text/javascript" src="file_networkEvents.js"></script> +</head> +<body> +</body> +</html> diff --git a/remote/test/browser/network/file_framesetEvents.js b/remote/test/browser/network/file_framesetEvents.js new file mode 100644 index 0000000000..3e146d265d --- /dev/null +++ b/remote/test/browser/network/file_framesetEvents.js @@ -0,0 +1,2 @@ +// Test file to emit Network events. +var foo = true; diff --git a/remote/test/browser/network/file_networkEvents.js b/remote/test/browser/network/file_networkEvents.js new file mode 100644 index 0000000000..3e146d265d --- /dev/null +++ b/remote/test/browser/network/file_networkEvents.js @@ -0,0 +1,2 @@ +// Test file to emit Network events. +var foo = true; diff --git a/remote/test/browser/network/head.js b/remote/test/browser/network/head.js new file mode 100644 index 0000000000..d3a9786a4b --- /dev/null +++ b/remote/test/browser/network/head.js @@ -0,0 +1,101 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/* import-globals-from ../head.js */ + +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/remote/test/browser/head.js", + this +); + +function assertCookie(cookie, expected = {}) { + const { + name = "", + value = "", + domain = "example.org", + path = "/", + expires = -1, + size = name.length + value.length, + httpOnly = false, + secure = false, + session = true, + sameSite, + } = expected; + + const expectedCookie = { + name, + value, + domain, + path, + expires, + size, + httpOnly, + secure, + session, + }; + + if (sameSite) { + expectedCookie.sameSite = sameSite; + } + + Assert.deepEqual(cookie, expectedCookie); +} + +function assertEventOrder(first, second, options = {}) { + const { ignoreTimestamps = false } = options; + + const firstDescription = getDescriptionForEvent(first); + const secondDescription = getDescriptionForEvent(second); + + ok( + first.index < second.index, + `${firstDescription} received before ${secondDescription})` + ); + + if (!ignoreTimestamps) { + ok( + first.payload.timestamp <= second.payload.timestamp, + `Timestamp of ${firstDescription}) is earlier than ${secondDescription})` + ); + } +} + +function filterEventsByType(history, type) { + return history.filter(event => event.payload.type == type); +} + +function getCookies() { + return Services.cookies.cookies.map(cookie => { + const data = { + name: cookie.name, + value: cookie.value, + domain: cookie.host, + path: cookie.path, + expires: cookie.isSession ? -1 : cookie.expiry, + // The size is the combined length of both the cookie name and value + size: cookie.name.length + cookie.value.length, + httpOnly: cookie.isHttpOnly, + secure: cookie.isSecure, + session: cookie.isSession, + }; + + if (cookie.sameSite) { + const sameSiteMap = new Map([ + [Ci.nsICookie.SAMESITE_LAX, "Lax"], + [Ci.nsICookie.SAMESITE_STRICT, "Strict"], + ]); + + data.sameSite = sameSiteMap.get(cookie.sameSite); + } + + return data; + }); +} + +function getDescriptionForEvent(event) { + const { eventName, payload } = event; + + return `${eventName}(${payload.type || payload.name || payload.frameId})`; +} diff --git a/remote/test/browser/network/sjs-cookies.sjs b/remote/test/browser/network/sjs-cookies.sjs new file mode 100644 index 0000000000..345a66880d --- /dev/null +++ b/remote/test/browser/network/sjs-cookies.sjs @@ -0,0 +1,44 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// eslint-disable-next-line mozilla/reject-importGlobalProperties +Cu.importGlobalProperties(["URLSearchParams"]); + +function handleRequest(request, response) { + const queryString = new URLSearchParams(request.queryString); + + response.setStatusLine(request.httpVersion, 200, "OK"); + response.setHeader("Content-Type", "text/plain; charset=utf-8", false); + + if (queryString.has("name") && queryString.has("value")) { + const name = queryString.get("name"); + const value = queryString.get("value"); + const path = queryString.get("path") || "/"; + + const expiry = queryString.get("expiry"); + const httpOnly = queryString.has("httpOnly"); + const secure = queryString.has("secure"); + const sameSite = queryString.get("sameSite"); + + let cookie = `${name}=${value}; Path=${path}`; + + if (expiry) { + cookie += `; Expires=${expiry}`; + } + + if (httpOnly) { + cookie += "; HttpOnly"; + } + + if (sameSite != undefined) { + cookie += `; sameSite=${sameSite}`; + } + + if (secure) { + cookie += "; Secure"; + } + + response.setHeader("Set-Cookie", cookie, true); + response.write(`Set cookie: ${cookie}`); + } +} |