summaryrefslogtreecommitdiffstats
path: root/remote/test/browser/network
diff options
context:
space:
mode:
Diffstat (limited to 'remote/test/browser/network')
-rw-r--r--remote/test/browser/network/browser.ini29
-rw-r--r--remote/test/browser/network/browser_deleteCookies.js298
-rw-r--r--remote/test/browser/network/browser_emulateNetworkConditions.js248
-rw-r--r--remote/test/browser/network/browser_getAllCookies.js225
-rw-r--r--remote/test/browser/network/browser_getCookies.js220
-rw-r--r--remote/test/browser/network/browser_navigationEvents.js202
-rw-r--r--remote/test/browser/network/browser_requestWillBeSent.js154
-rw-r--r--remote/test/browser/network/browser_responseReceived.js236
-rw-r--r--remote/test/browser/network/browser_setCacheDisabled.js122
-rw-r--r--remote/test/browser/network/browser_setCookie.js298
-rw-r--r--remote/test/browser/network/browser_setCookies.js70
-rw-r--r--remote/test/browser/network/browser_setUserAgentOverride.js68
-rw-r--r--remote/test/browser/network/doc_empty.html9
-rw-r--r--remote/test/browser/network/doc_frameset.html10
-rw-r--r--remote/test/browser/network/doc_networkEvents.html9
-rw-r--r--remote/test/browser/network/file_framesetEvents.js2
-rw-r--r--remote/test/browser/network/file_networkEvents.js2
-rw-r--r--remote/test/browser/network/head.js101
-rw-r--r--remote/test/browser/network/sjs-cookies.sjs44
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}`);
+ }
+}