diff options
Diffstat (limited to 'netwerk/test/browser')
19 files changed, 1197 insertions, 0 deletions
diff --git a/netwerk/test/browser/browser.ini b/netwerk/test/browser/browser.ini new file mode 100644 index 0000000000..dbe43345f2 --- /dev/null +++ b/netwerk/test/browser/browser.ini @@ -0,0 +1,31 @@ +[DEFAULT] +support-files = + dummy.html + ioactivity.html + redirect.sjs + +[browser_about_cache.js] +[browser_bug1535877.js] +[browser_NetUtil.js] +[browser_child_resource.js] +skip-if = !crashreporter + (e10s && debug && os == "linux" && bits == 64) + debug # Bug 1370783 + fission # Bug 1670867 +[browser_post_file.js] +[browser_nsIFormPOSTActionChannel.js] +skip-if = e10s # protocol handler and channel does not work in content process +[browser_resource_navigation.js] +[browser_test_io_activity.js] +skip-if = socketprocess_networking +[browser_cookie_sync_across_tabs.js] +[browser_test_favicon.js] +skip-if = (verify && (os == 'linux' || os == 'mac')) +support-files = + damonbowling.jpg + damonbowling.jpg^headers^ + file_favicon.html +[browser_fetch_lnk.js] +run-if = os == "win" +support-files = + file_lnk.lnk diff --git a/netwerk/test/browser/browser_NetUtil.js b/netwerk/test/browser/browser_NetUtil.js new file mode 100644 index 0000000000..f8578fa4f8 --- /dev/null +++ b/netwerk/test/browser/browser_NetUtil.js @@ -0,0 +1,112 @@ +/* +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +*/ +"use strict"; + +function test() { + waitForExplicitFinish(); + + // We overload this test to include verifying that httpd.js is + // importable as a testing-only JS module. + ChromeUtils.import("resource://testing-common/httpd.js", {}); + + nextTest(); +} + +function nextTest() { + if (tests.length) { + executeSoon(tests.shift()); + } else { + executeSoon(finish); + } +} + +var tests = [test_asyncFetchBadCert]; + +function test_asyncFetchBadCert() { + // Try a load from an untrusted cert, with errors supressed + NetUtil.asyncFetch( + { + uri: "https://untrusted.example.com", + loadUsingSystemPrincipal: true, + }, + function(aInputStream, aStatusCode, aRequest) { + ok(!Components.isSuccessCode(aStatusCode), "request failed"); + ok(aRequest instanceof Ci.nsIHttpChannel, "request is an nsIHttpChannel"); + + // Now try again with a channel whose notificationCallbacks doesn't suprress errors + let channel = NetUtil.newChannel({ + uri: "https://untrusted.example.com", + loadUsingSystemPrincipal: true, + }); + channel.notificationCallbacks = { + QueryInterface: ChromeUtils.generateQI([ + "nsIProgressEventSink", + "nsIInterfaceRequestor", + ]), + getInterface(aIID) { + return this.QueryInterface(aIID); + }, + onProgress() {}, + onStatus() {}, + }; + NetUtil.asyncFetch(channel, function( + aInputStream, + aStatusCode, + aRequest + ) { + ok(!Components.isSuccessCode(aStatusCode), "request failed"); + ok( + aRequest instanceof Ci.nsIHttpChannel, + "request is an nsIHttpChannel" + ); + + // Now try a valid request + NetUtil.asyncFetch( + { + uri: "https://example.com", + loadUsingSystemPrincipal: true, + }, + function(aInputStream, aStatusCode, aRequest) { + info("aStatusCode for valid request: " + aStatusCode); + ok(Components.isSuccessCode(aStatusCode), "request succeeded"); + ok( + aRequest instanceof Ci.nsIHttpChannel, + "request is an nsIHttpChannel" + ); + ok(aRequest.requestSucceeded, "HTTP request succeeded"); + + nextTest(); + } + ); + }); + } + ); +} + +function WindowListener(aURL, aCallback) { + this.callback = aCallback; + this.url = aURL; +} +WindowListener.prototype = { + onOpenWindow(aXULWindow) { + var domwindow = aXULWindow.docShell.domWindow; + var self = this; + domwindow.addEventListener( + "load", + function() { + if (domwindow.document.location.href != self.url) { + return; + } + + // Allow other window load listeners to execute before passing to callback + executeSoon(function() { + self.callback(domwindow); + }); + }, + { once: true } + ); + }, + onCloseWindow(aXULWindow) {}, +}; diff --git a/netwerk/test/browser/browser_about_cache.js b/netwerk/test/browser/browser_about_cache.js new file mode 100644 index 0000000000..dba4204129 --- /dev/null +++ b/netwerk/test/browser/browser_about_cache.js @@ -0,0 +1,126 @@ +"use strict"; + +/** + * Open a dummy page, then open about:cache and verify the opened page shows up in the cache. + */ +add_task(async function() { + await SpecialPowers.pushPrefEnv({ + set: [["privacy.partition.network_state", false]], + }); + + const kRoot = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content/", + "https://example.com/" + ); + const kTestPage = kRoot + "dummy.html"; + // Open the dummy page to get it cached. + let tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + kTestPage, + true + ); + BrowserTestUtils.removeTab(tab); + + tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + "about:cache", + true + ); + let expectedPageCheck = function(uri) { + info("Saw load for " + uri); + // Can't easily use searchParms and new URL() because it's an about: URI... + return uri.startsWith("about:cache?") && uri.includes("storage=disk"); + }; + let diskPageLoaded = BrowserTestUtils.browserLoaded( + tab.linkedBrowser, + false, + expectedPageCheck + ); + await SpecialPowers.spawn(tab.linkedBrowser, [], function() { + ok( + !content.document.nodePrincipal.isSystemPrincipal, + "about:cache should not have system principal" + ); + let principal = content.document.nodePrincipal; + let channel = content.docShell.currentDocumentChannel; + ok(!channel.loadInfo.loadingPrincipal, "Loading principal should be null."); + is( + principal.spec, + content.document.location.href, + "Principal matches location" + ); + let links = [...content.document.querySelectorAll("a[href*=disk]")]; + is(links.length, 1, "Should have 1 link to the disk entries"); + links[0].click(); + }); + await diskPageLoaded; + info("about:cache disk subpage loaded"); + + expectedPageCheck = function(uri) { + info("Saw load for " + uri); + return uri.startsWith("about:cache-entry") && uri.includes("dummy.html"); + }; + let triggeringURISpec = tab.linkedBrowser.currentURI.spec; + let entryLoaded = BrowserTestUtils.browserLoaded( + tab.linkedBrowser, + false, + expectedPageCheck + ); + await SpecialPowers.spawn(tab.linkedBrowser, [kTestPage], function( + kTestPage + ) { + ok( + !content.document.nodePrincipal.isSystemPrincipal, + "about:cache with query params should still not have system principal" + ); + let principal = content.document.nodePrincipal; + is( + principal.spec, + content.document.location.href, + "Principal matches location" + ); + let channel = content.docShell.currentDocumentChannel; + principal = channel.loadInfo.triggeringPrincipal; + is( + principal.spec, + "about:cache", + "Triggering principal matches previous location" + ); + ok(!channel.loadInfo.loadingPrincipal, "Loading principal should be null."); + let links = [ + ...content.document.querySelectorAll("a[href*='" + kTestPage + "']"), + ]; + is(links.length, 1, "Should have 1 link to the entry for " + kTestPage); + links[0].click(); + }); + await entryLoaded; + info("about:cache entry loaded"); + + await SpecialPowers.spawn(tab.linkedBrowser, [triggeringURISpec], function( + triggeringURISpec + ) { + ok( + !content.document.nodePrincipal.isSystemPrincipal, + "about:cache-entry should also not have system principal" + ); + let principal = content.document.nodePrincipal; + is( + principal.spec, + content.document.location.href, + "Principal matches location" + ); + let channel = content.docShell.currentDocumentChannel; + principal = channel.loadInfo.triggeringPrincipal; + is( + principal.spec, + triggeringURISpec, + "Triggering principal matches previous location" + ); + ok(!channel.loadInfo.loadingPrincipal, "Loading principal should be null."); + ok( + content.document.querySelectorAll("th").length, + "Should have several table headers with data." + ); + }); + BrowserTestUtils.removeTab(tab); +}); diff --git a/netwerk/test/browser/browser_bug1535877.js b/netwerk/test/browser/browser_bug1535877.js new file mode 100644 index 0000000000..0bd0a98d11 --- /dev/null +++ b/netwerk/test/browser/browser_bug1535877.js @@ -0,0 +1,15 @@ +"use strict"; + +add_task(_ => { + try { + Cc["@mozilla.org/network/effective-tld-service;1"].createInstance( + Ci.nsISupports + ); + } catch (e) { + is( + e.result, + Cr.NS_ERROR_XPC_CI_RETURNED_FAILURE, + "Component creation as an instance fails with expected code" + ); + } +}); diff --git a/netwerk/test/browser/browser_child_resource.js b/netwerk/test/browser/browser_child_resource.js new file mode 100644 index 0000000000..7535120f44 --- /dev/null +++ b/netwerk/test/browser/browser_child_resource.js @@ -0,0 +1,247 @@ +/* +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +*/ +"use strict"; + +// This must be loaded in the remote process for this test to be useful +const TEST_URL = "http://example.com/browser/netwerk/test/browser/dummy.html"; + +const expectedRemote = gMultiProcessBrowser ? "true" : ""; + +const resProtocol = Cc[ + "@mozilla.org/network/protocol;1?name=resource" +].getService(Ci.nsIResProtocolHandler); + +function waitForEvent(obj, name, capturing, chromeEvent) { + info("Waiting for " + name); + return new Promise(resolve => { + function listener(event) { + info("Saw " + name); + obj.removeEventListener(name, listener, capturing, chromeEvent); + resolve(event); + } + + obj.addEventListener(name, listener, capturing, chromeEvent); + }); +} + +function resolveURI(uri) { + uri = Services.io.newURI(uri); + try { + return resProtocol.resolveURI(uri); + } catch (e) { + return null; + } +} + +function remoteResolveURI(uri) { + return SpecialPowers.spawn(gBrowser.selectedBrowser, [uri], uriToResolve => { + const { Services } = ChromeUtils.import( + "resource://gre/modules/Services.jsm" + ); + let resProtocol = Cc[ + "@mozilla.org/network/protocol;1?name=resource" + ].getService(Ci.nsIResProtocolHandler); + + uriToResolve = Services.io.newURI(uriToResolve); + try { + return resProtocol.resolveURI(uriToResolve); + } catch (e) {} + return null; + }); +} + +// Restarts the child process by crashing it then reloading the tab +var restart = async function() { + let browser = gBrowser.selectedBrowser; + // If the tab isn't remote this would crash the main process so skip it + if (browser.getAttribute("remote") != "true") { + return browser; + } + + await BrowserTestUtils.crashFrame(browser); + + browser.reload(); + + await BrowserTestUtils.browserLoaded(browser); + is( + browser.getAttribute("remote"), + expectedRemote, + "Browser should be in the right process" + ); + return browser; +}; + +// Sanity check that this test is going to be useful +add_task(async function() { + await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL); + + // This must be loaded in the remote process for this test to be useful + is( + gBrowser.selectedBrowser.getAttribute("remote"), + expectedRemote, + "Browser should be in the right process" + ); + + let local = resolveURI("resource://gre/modules/Services.jsm"); + let remote = await remoteResolveURI("resource://gre/modules/Services.jsm"); + is(local, remote, "Services.jsm should resolve in both processes"); + + gBrowser.removeCurrentTab(); +}); + +// Add a mapping, update it then remove it +add_task(async function() { + await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL); + + info("Set"); + resProtocol.setSubstitution( + "testing", + Services.io.newURI("chrome://global/content") + ); + let local = resolveURI("resource://testing/test.js"); + let remote = await remoteResolveURI("resource://testing/test.js"); + is( + local, + "chrome://global/content/test.js", + "Should resolve in main process" + ); + is( + remote, + "chrome://global/content/test.js", + "Should resolve in child process" + ); + + info("Change"); + resProtocol.setSubstitution( + "testing", + Services.io.newURI("chrome://global/skin") + ); + local = resolveURI("resource://testing/test.js"); + remote = await remoteResolveURI("resource://testing/test.js"); + is(local, "chrome://global/skin/test.js", "Should resolve in main process"); + is(remote, "chrome://global/skin/test.js", "Should resolve in child process"); + + info("Clear"); + resProtocol.setSubstitution("testing", null); + local = resolveURI("resource://testing/test.js"); + remote = await remoteResolveURI("resource://testing/test.js"); + is(local, null, "Shouldn't resolve in main process"); + is(remote, null, "Shouldn't resolve in child process"); + + gBrowser.removeCurrentTab(); +}); + +// Add a mapping, restart the child process then check it is still there +add_task(async function() { + await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL); + + info("Set"); + resProtocol.setSubstitution( + "testing", + Services.io.newURI("chrome://global/content") + ); + let local = resolveURI("resource://testing/test.js"); + let remote = await remoteResolveURI("resource://testing/test.js"); + is( + local, + "chrome://global/content/test.js", + "Should resolve in main process" + ); + is( + remote, + "chrome://global/content/test.js", + "Should resolve in child process" + ); + + await restart(); + + local = resolveURI("resource://testing/test.js"); + remote = await remoteResolveURI("resource://testing/test.js"); + is( + local, + "chrome://global/content/test.js", + "Should resolve in main process" + ); + is( + remote, + "chrome://global/content/test.js", + "Should resolve in child process" + ); + + info("Change"); + resProtocol.setSubstitution( + "testing", + Services.io.newURI("chrome://global/skin") + ); + + await restart(); + + local = resolveURI("resource://testing/test.js"); + remote = await remoteResolveURI("resource://testing/test.js"); + is(local, "chrome://global/skin/test.js", "Should resolve in main process"); + is(remote, "chrome://global/skin/test.js", "Should resolve in child process"); + + info("Clear"); + resProtocol.setSubstitution("testing", null); + + await restart(); + + local = resolveURI("resource://testing/test.js"); + remote = await remoteResolveURI("resource://testing/test.js"); + is(local, null, "Shouldn't resolve in main process"); + is(remote, null, "Shouldn't resolve in child process"); + + gBrowser.removeCurrentTab(); +}); + +// Adding a mapping to a resource URI should work +add_task(async function() { + await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL); + + info("Set"); + resProtocol.setSubstitution( + "testing", + Services.io.newURI("chrome://global/content") + ); + resProtocol.setSubstitution( + "testing2", + Services.io.newURI("resource://testing") + ); + let local = resolveURI("resource://testing2/test.js"); + let remote = await remoteResolveURI("resource://testing2/test.js"); + is( + local, + "chrome://global/content/test.js", + "Should resolve in main process" + ); + is( + remote, + "chrome://global/content/test.js", + "Should resolve in child process" + ); + + info("Clear"); + resProtocol.setSubstitution("testing", null); + local = resolveURI("resource://testing2/test.js"); + remote = await remoteResolveURI("resource://testing2/test.js"); + is( + local, + "chrome://global/content/test.js", + "Should resolve in main process" + ); + is( + remote, + "chrome://global/content/test.js", + "Should resolve in child process" + ); + + resProtocol.setSubstitution("testing2", null); + local = resolveURI("resource://testing2/test.js"); + remote = await remoteResolveURI("resource://testing2/test.js"); + is(local, null, "Shouldn't resolve in main process"); + is(remote, null, "Shouldn't resolve in child process"); + + gBrowser.removeCurrentTab(); +}); diff --git a/netwerk/test/browser/browser_cookie_sync_across_tabs.js b/netwerk/test/browser/browser_cookie_sync_across_tabs.js new file mode 100644 index 0000000000..872e3cc4f5 --- /dev/null +++ b/netwerk/test/browser/browser_cookie_sync_across_tabs.js @@ -0,0 +1,79 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +"use strict"; + +add_task(async function() { + info("Make sure cookie changes in one process are visible in the other"); + + const kRoot = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content/", + "https://example.com/" + ); + const kTestPage = kRoot + "dummy.html"; + + Services.cookies.removeAll(); + + let tab1 = await BrowserTestUtils.openNewForegroundTab({ + gBrowser, + url: kTestPage, + forceNewProcess: true, + }); + let tab2 = await BrowserTestUtils.openNewForegroundTab({ + gBrowser, + url: kTestPage, + forceNewProcess: true, + }); + + let browser1 = gBrowser.getBrowserForTab(tab1); + let browser2 = gBrowser.getBrowserForTab(tab2); + + let pid1 = browser1.frameLoader.remoteTab.osPid; + let pid2 = browser2.frameLoader.remoteTab.osPid; + + // Note, this might not be true once fission is implemented (Bug 1451850) + ok(pid1 != pid2, "We should have different processes here."); + + await SpecialPowers.spawn(browser1, [], async function() { + is(content.document.cookie, "", "Expecting no cookies"); + }); + + await SpecialPowers.spawn(browser2, [], async function() { + is(content.document.cookie, "", "Expecting no cookies"); + }); + + await SpecialPowers.spawn(browser1, [], async function() { + content.document.cookie = "a1=test"; + }); + + await SpecialPowers.spawn(browser2, [], async function() { + is(content.document.cookie, "a1=test", "Cookie should be set"); + content.document.cookie = "a1=other_test"; + }); + + await SpecialPowers.spawn(browser1, [], async function() { + is(content.document.cookie, "a1=other_test", "Cookie should be set"); + content.document.cookie = "a2=again"; + }); + + await SpecialPowers.spawn(browser2, [], async function() { + is( + content.document.cookie, + "a1=other_test; a2=again", + "Cookie should be set" + ); + content.document.cookie = "a1=; expires=Thu, 01-Jan-1970 00:00:01 GMT;"; + content.document.cookie = "a2=; expires=Thu, 01-Jan-1970 00:00:01 GMT;"; + }); + + await SpecialPowers.spawn(browser1, [], async function() { + is(content.document.cookie, "", "Cookies should be cleared"); + }); + + BrowserTestUtils.removeTab(tab1); + BrowserTestUtils.removeTab(tab2); + + ok(true, "Got to the end of the test!"); +}); diff --git a/netwerk/test/browser/browser_fetch_lnk.js b/netwerk/test/browser/browser_fetch_lnk.js new file mode 100644 index 0000000000..cbfc6a4c4b --- /dev/null +++ b/netwerk/test/browser/browser_fetch_lnk.js @@ -0,0 +1,27 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async () => { + await SpecialPowers.pushPrefEnv({ + set: [["privacy.file_unique_origin", false]], + }); + + const FILE_PAGE = Services.io.newFileURI( + new FileUtils.File(getTestFilePath("dummy.html")) + ).spec; + await BrowserTestUtils.withNewTab(FILE_PAGE, async browser => { + try { + await SpecialPowers.spawn(browser, [], () => + content.fetch("./file_lnk.lnk") + ); + ok( + false, + "Loading lnk must fail if it links to a file from other directory" + ); + } catch (err) { + is(err.constructor.name, "TypeError", "Should fail on Windows"); + } + }); +}); diff --git a/netwerk/test/browser/browser_nsIFormPOSTActionChannel.js b/netwerk/test/browser/browser_nsIFormPOSTActionChannel.js new file mode 100644 index 0000000000..07387f8c09 --- /dev/null +++ b/netwerk/test/browser/browser_nsIFormPOSTActionChannel.js @@ -0,0 +1,292 @@ +/* + * Tests for bug 1241377: A channel with nsIFormPOSTActionChannel interface + * should be able to accept form POST. + */ + +/* eslint-env mozilla/frame-script */ + +"use strict"; + +const SCHEME = "x-bug1241377"; + +const FORM_BASE = SCHEME + "://dummy/form/"; +const NORMAL_FORM_URI = FORM_BASE + "normal.html"; +const UPLOAD_FORM_URI = FORM_BASE + "upload.html"; +const POST_FORM_URI = FORM_BASE + "post.html"; + +const ACTION_BASE = SCHEME + "://dummy/action/"; +const NORMAL_ACTION_URI = ACTION_BASE + "normal.html"; +const UPLOAD_ACTION_URI = ACTION_BASE + "upload.html"; +const POST_ACTION_URI = ACTION_BASE + "post.html"; + +function CustomProtocolHandler() {} +CustomProtocolHandler.prototype = { + /** nsIProtocolHandler */ + get scheme() { + return SCHEME; + }, + get defaultPort() { + return -1; + }, + get protocolFlags() { + return ( + Ci.nsIProtocolHandler.URI_NORELATIVE | + Ci.nsIProtocolHandler.URI_IS_LOCAL_RESOURCE + ); + }, + newChannel(aURI, aLoadInfo) { + return new CustomChannel(aURI, aLoadInfo); + }, + allowPort(port, scheme) { + return port != -1; + }, + + /** nsIFactory */ + createInstance(aOuter, aIID) { + if (aOuter) { + throw Components.Exception("", Cr.NS_ERROR_NO_AGGREGATION); + } + return this.QueryInterface(aIID); + }, + lockFactory() {}, + + /** nsISupports */ + QueryInterface: ChromeUtils.generateQI(["nsIProtocolHandler", "nsIFactory"]), + classID: Components.ID("{16d594bc-d9d8-47ae-a139-ea714dc0c35c}"), +}; + +function CustomChannel(aURI, aLoadInfo) { + this.uri = aURI; + this.loadInfo = aLoadInfo; + + this._uploadStream = null; + + var interfaces = [Ci.nsIRequest, Ci.nsIChannel]; + if (this.uri.spec == POST_ACTION_URI) { + interfaces.push(Ci.nsIFormPOSTActionChannel); + } else if (this.uri.spec == UPLOAD_ACTION_URI) { + interfaces.push(Ci.nsIUploadChannel); + } + this.QueryInterface = ChromeUtils.generateQI(interfaces); +} +CustomChannel.prototype = { + /** nsIUploadChannel */ + get uploadStream() { + return this._uploadStream; + }, + set uploadStream(val) { + throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); + }, + setUploadStream(aStream, aContentType, aContentLength) { + this._uploadStream = aStream; + }, + + /** nsIChannel */ + get originalURI() { + return this.uri; + }, + get URI() { + return this.uri; + }, + owner: null, + notificationCallbacks: null, + get securityInfo() { + return null; + }, + get contentType() { + return "text/html"; + }, + set contentType(val) {}, + contentCharset: "UTF-8", + get contentLength() { + return -1; + }, + set contentLength(val) { + throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); + }, + open() { + throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); + }, + asyncOpen(aListener) { + var data = ` +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>test bug 1241377</title> +</head> +<body> +`; + + if (this.uri.spec.startsWith(FORM_BASE)) { + data += ` +<form id="form" action="${this.uri.spec.replace(FORM_BASE, ACTION_BASE)}" + method="post" enctype="text/plain" target="frame"> +<input type="hidden" name="foo" value="bar"> +<input type="submit"> +</form> + +<iframe id="frame" name="frame" width="200" height="200"></iframe> + +<script type="text/javascript"> +<!-- +document.getElementById('form').submit(); +//--> +</script> +`; + } else if (this.uri.spec.startsWith(ACTION_BASE)) { + var postData = ""; + var headers = {}; + if (this._uploadStream) { + var bstream = Cc["@mozilla.org/binaryinputstream;1"].createInstance( + Ci.nsIBinaryInputStream + ); + bstream.setInputStream(this._uploadStream); + postData = bstream.readBytes(bstream.available()); + + if (this._uploadStream instanceof Ci.nsIMIMEInputStream) { + this._uploadStream.visitHeaders((name, value) => { + headers[name] = value; + }); + } + } + data += ` +<input id="upload_stream" value="${this._uploadStream ? "yes" : "no"}"> +<input id="post_data" value="${btoa(postData)}"> +<input id="upload_headers" value='${JSON.stringify(headers)}'> +`; + } + + data += ` +</body> +</html> +`; + + var stream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance( + Ci.nsIStringInputStream + ); + stream.setData(data, data.length); + + var runnable = { + run: () => { + try { + aListener.onStartRequest(this, null); + } catch (e) {} + try { + aListener.onDataAvailable(this, stream, 0, stream.available()); + } catch (e) {} + try { + aListener.onStopRequest(this, null, Cr.NS_OK); + } catch (e) {} + }, + }; + Services.tm.dispatchToMainThread(runnable); + }, + + /** nsIRequest */ + get name() { + return this.uri.spec; + }, + isPending() { + return false; + }, + get status() { + return Cr.NS_OK; + }, + cancel(status) {}, + loadGroup: null, + loadFlags: + Ci.nsIRequest.LOAD_NORMAL | + Ci.nsIRequest.INHIBIT_CACHING | + Ci.nsIRequest.LOAD_BYPASS_CACHE, +}; + +function frameScript() { + addMessageListener("Test:WaitForIFrame", function() { + var check = function() { + if (content) { + var frame = content.document.getElementById("frame"); + if (frame) { + var upload_stream = frame.contentDocument.getElementById( + "upload_stream" + ); + var post_data = frame.contentDocument.getElementById("post_data"); + var headers = frame.contentDocument.getElementById("upload_headers"); + if (upload_stream && post_data && headers) { + sendAsyncMessage("Test:IFrameLoaded", [ + upload_stream.value, + post_data.value, + headers.value, + ]); + return; + } + } + } + + setTimeout(check, 100); + }; + + check(); + }); +} + +function loadTestTab(uri) { + gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, uri); + var browser = gBrowser.selectedBrowser; + + let manager = browser.messageManager; + browser.messageManager.loadFrameScript( + "data:,(" + frameScript.toString() + ")();", + true + ); + + return new Promise(resolve => { + function listener({ data: [hasUploadStream, postData, headers] }) { + manager.removeMessageListener("Test:IFrameLoaded", listener); + resolve([hasUploadStream, atob(postData), JSON.parse(headers)]); + } + + manager.addMessageListener("Test:IFrameLoaded", listener); + manager.sendAsyncMessage("Test:WaitForIFrame"); + }); +} + +add_task(async function() { + var handler = new CustomProtocolHandler(); + var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); + registrar.registerFactory( + handler.classID, + "", + "@mozilla.org/network/protocol;1?name=" + handler.scheme, + handler + ); + registerCleanupFunction(function() { + registrar.unregisterFactory(handler.classID, handler); + }); +}); + +add_task(async function() { + var [hasUploadStream] = await loadTestTab(NORMAL_FORM_URI); + is(hasUploadStream, "no", "normal action should not have uploadStream"); + + gBrowser.removeCurrentTab(); +}); + +add_task(async function() { + var [hasUploadStream] = await loadTestTab(UPLOAD_FORM_URI); + is(hasUploadStream, "no", "upload action should not have uploadStream"); + + gBrowser.removeCurrentTab(); +}); + +add_task(async function() { + var [hasUploadStream, postData, headers] = await loadTestTab(POST_FORM_URI); + + is(hasUploadStream, "yes", "post action should have uploadStream"); + is(postData, "foo=bar\r\n", "POST data is received correctly"); + + is(headers["Content-Type"], "text/plain", "Content-Type header is correct"); + is(headers["Content-Length"], undefined, "Content-Length header is correct"); + + gBrowser.removeCurrentTab(); +}); diff --git a/netwerk/test/browser/browser_post_file.js b/netwerk/test/browser/browser_post_file.js new file mode 100644 index 0000000000..f8dc859cc0 --- /dev/null +++ b/netwerk/test/browser/browser_post_file.js @@ -0,0 +1,78 @@ +/* + * Tests for bug 1241100: Post to local file should not overwrite the file. + */ +"use strict"; +const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm"); + +async function createTestFile(filename, content) { + let path = OS.Path.join(OS.Constants.Path.tmpDir, filename); + await OS.File.writeAtomic(path, content); + return path; +} + +async function readFile(path) { + var array = await OS.File.read(path); + var decoder = new TextDecoder(); + return decoder.decode(array); +} + +add_task(async function() { + var postFilename = "post_file.html"; + var actionFilename = "action_file.html"; + + var postFileContent = ` +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>post file</title> +</head> +<body onload="document.getElementById('form').submit();"> +<form id="form" action="${actionFilename}" method="post" enctype="text/plain" target="frame"> +<input type="hidden" name="foo" value="bar"> +<input type="submit"> +</form> +<iframe id="frame" name="frame"></iframe> +</body> +</html> +`; + + var actionFileContent = ` +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>action file</title> +</head> +<body> +<div id="action_file_ok">ok</div> +</body> +</html> +`; + + var postPath = await createTestFile(postFilename, postFileContent); + var actionPath = await createTestFile(actionFilename, actionFileContent); + + var postURI = OS.Path.toFileURI(postPath); + var actionURI = OS.Path.toFileURI(actionPath); + + let tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + "about:blank" + ); + let browserLoadedPromise = BrowserTestUtils.browserLoaded( + tab.linkedBrowser, + true, + actionURI + ); + BrowserTestUtils.loadURI(tab.linkedBrowser, postURI); + await browserLoadedPromise; + + var actionFileContentAfter = await readFile(actionPath); + is(actionFileContentAfter, actionFileContent, "action file is not modified"); + + await OS.File.remove(postPath); + await OS.File.remove(actionPath); + + gBrowser.removeCurrentTab(); +}); diff --git a/netwerk/test/browser/browser_resource_navigation.js b/netwerk/test/browser/browser_resource_navigation.js new file mode 100644 index 0000000000..abcac2b554 --- /dev/null +++ b/netwerk/test/browser/browser_resource_navigation.js @@ -0,0 +1,76 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +"use strict"; + +add_task(async function() { + info("Make sure navigation through links in resource:// pages work"); + + await BrowserTestUtils.withNewTab( + { gBrowser, url: "resource://gre/" }, + async function(browser) { + // Following a directory link shall properly open the directory (bug 1224046) + await SpecialPowers.spawn(browser, [], function() { + let link = Array.prototype.filter.call( + content.document.getElementsByClassName("dir"), + function(element) { + let name = element.textContent; + // Depending whether resource:// is backed by jar: or file://, + // directories either have a trailing slash or they don't. + if (name.endsWith("/")) { + name = name.slice(0, -1); + } + return name == "components"; + } + )[0]; + // First ensure the link is in the viewport + link.scrollIntoView(); + // Then click on it. + link.click(); + }); + + await BrowserTestUtils.browserLoaded( + browser, + undefined, + "resource://gre/components/" + ); + + // Following the parent link shall properly open the parent (bug 1366180) + await SpecialPowers.spawn(browser, [], function() { + let link = content.document + .getElementById("UI_goUp") + .getElementsByTagName("a")[0]; + // The link should always be high enough in the page to be in the viewport. + link.click(); + }); + + await BrowserTestUtils.browserLoaded( + browser, + undefined, + "resource://gre/" + ); + + // Following a link to a given file shall properly open the file. + await SpecialPowers.spawn(browser, [], function() { + let link = Array.prototype.filter.call( + content.document.getElementsByClassName("file"), + function(element) { + return element.textContent == "greprefs.js"; + } + )[0]; + link.scrollIntoView(); + link.click(); + }); + + await BrowserTestUtils.browserLoaded( + browser, + undefined, + "resource://gre/greprefs.js" + ); + + ok(true, "Got to the end of the test!"); + } + ); +}); diff --git a/netwerk/test/browser/browser_test_favicon.js b/netwerk/test/browser/browser_test_favicon.js new file mode 100644 index 0000000000..2817385d20 --- /dev/null +++ b/netwerk/test/browser/browser_test_favicon.js @@ -0,0 +1,26 @@ +// Tests third party cookie blocking using a favicon loaded from a different +// domain. The cookie should be considered third party. +"use strict"; +add_task(async function() { + const iconUrl = + "http://example.org/browser/netwerk/test/browser/damonbowling.jpg"; + const pageUrl = + "http://example.com/browser/netwerk/test/browser/file_favicon.html"; + await SpecialPowers.pushPrefEnv({ + set: [["network.cookie.cookieBehavior", 1]], + }); + + let promise = TestUtils.topicObserved("cookie-rejected", subject => { + let uri = subject.QueryInterface(Ci.nsIURI); + return uri.spec == iconUrl; + }); + + // Kick off a page load that will load the favicon. + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl); + registerCleanupFunction(async function() { + BrowserTestUtils.removeTab(tab); + }); + + await promise; + ok(true, "foreign favicon cookie was blocked"); +}); diff --git a/netwerk/test/browser/browser_test_io_activity.js b/netwerk/test/browser/browser_test_io_activity.js new file mode 100644 index 0000000000..f847701888 --- /dev/null +++ b/netwerk/test/browser/browser_test_io_activity.js @@ -0,0 +1,50 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; +const ROOT_URL = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content/", + "https://example.com/" +); +const TEST_URL = "about:license"; +const TEST_URL2 = ROOT_URL + "ioactivity.html"; + +var gotSocket = false; +var gotFile = false; +var gotSqlite = false; +var gotEmptyData = false; + +function processResults(results) { + for (let data of results) { + console.log(data.location); + gotEmptyData = data.rx == 0 && data.tx == 0 && !gotEmptyData; + gotSocket = data.location.startsWith("socket://127.0.0.1:") || gotSocket; + gotFile = data.location.endsWith("aboutLicense.css") || gotFile; + gotSqlite = data.location.endsWith("places.sqlite") || gotSqlite; + // check for the write-ahead file as well + gotSqlite = data.location.endsWith("places.sqlite-wal") || gotSqlite; + } +} + +add_task(async function testRequestIOActivity() { + await SpecialPowers.pushPrefEnv({ + set: [["io.activity.enabled", true]], + }); + waitForExplicitFinish(); + Services.obs.notifyObservers(null, "profile-initial-state"); + + await BrowserTestUtils.withNewTab(TEST_URL, async function(browser) { + await BrowserTestUtils.withNewTab(TEST_URL2, async function(browser) { + let results = await ChromeUtils.requestIOActivity(); + processResults(results); + + ok(gotSocket, "A socket was used"); + // test deactivated for now + // ok(gotFile, "A file was used"); + ok(gotSqlite, "A sqlite DB was used"); + ok(!gotEmptyData, "Every I/O event had data"); + }); + }); +}); diff --git a/netwerk/test/browser/damonbowling.jpg b/netwerk/test/browser/damonbowling.jpg Binary files differnew file mode 100644 index 0000000000..8bdb2b6042 --- /dev/null +++ b/netwerk/test/browser/damonbowling.jpg diff --git a/netwerk/test/browser/damonbowling.jpg^headers^ b/netwerk/test/browser/damonbowling.jpg^headers^ new file mode 100644 index 0000000000..77f4f49089 --- /dev/null +++ b/netwerk/test/browser/damonbowling.jpg^headers^ @@ -0,0 +1,2 @@ +Cache-Control: no-store +Set-Cookie: damon=bowling diff --git a/netwerk/test/browser/dummy.html b/netwerk/test/browser/dummy.html new file mode 100644 index 0000000000..8025fcdb20 --- /dev/null +++ b/netwerk/test/browser/dummy.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> + +<head> +<meta http-equiv="content-type" content="text/html; charset=utf-8"> +</head> + +<html> +<body> + <p>Dummy Page</p> +</body> +</html> diff --git a/netwerk/test/browser/file_favicon.html b/netwerk/test/browser/file_favicon.html new file mode 100644 index 0000000000..77532a3a53 --- /dev/null +++ b/netwerk/test/browser/file_favicon.html @@ -0,0 +1,7 @@ +<!DOCTYPE html> + +<html> + <head> + <link rel="shortcut icon" href="http://example.org/browser/netwerk/test/browser/damonbowling.jpg"> + </head> +</html> diff --git a/netwerk/test/browser/file_lnk.lnk b/netwerk/test/browser/file_lnk.lnk Binary files differnew file mode 100644 index 0000000000..abce7587d2 --- /dev/null +++ b/netwerk/test/browser/file_lnk.lnk diff --git a/netwerk/test/browser/ioactivity.html b/netwerk/test/browser/ioactivity.html new file mode 100644 index 0000000000..5e23f6f117 --- /dev/null +++ b/netwerk/test/browser/ioactivity.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> + +<head> +<meta http-equiv="content-type" content="text/html; charset=utf-8"> +</head> + +<html> +<body> + <p>IOActivity Test Page</p> +</body> +</html> diff --git a/netwerk/test/browser/redirect.sjs b/netwerk/test/browser/redirect.sjs new file mode 100644 index 0000000000..7599fe33fb --- /dev/null +++ b/netwerk/test/browser/redirect.sjs @@ -0,0 +1,7 @@ +function handleRequest(request, response) +{ + response.setStatusLine(request.httpVersion, 301, "Moved Permanently"); + let location = request.queryString; + response.setHeader("Location", location, false); + response.write("Hello world!"); +} |