diff options
Diffstat (limited to 'netwerk/test/browser')
-rw-r--r-- | netwerk/test/browser/browser.ini | 11 | ||||
-rw-r--r-- | netwerk/test/browser/browser_NetUtil.js | 92 | ||||
-rw-r--r-- | netwerk/test/browser/browser_about_cache.js | 71 | ||||
-rw-r--r-- | netwerk/test/browser/browser_child_resource.js | 256 | ||||
-rw-r--r-- | netwerk/test/browser/browser_nsIFormPOSTActionChannel.js | 284 | ||||
-rw-r--r-- | netwerk/test/browser/browser_post_file.js | 101 | ||||
-rw-r--r-- | netwerk/test/browser/dummy.html | 7 |
7 files changed, 822 insertions, 0 deletions
diff --git a/netwerk/test/browser/browser.ini b/netwerk/test/browser/browser.ini new file mode 100644 index 000000000..8611891fd --- /dev/null +++ b/netwerk/test/browser/browser.ini @@ -0,0 +1,11 @@ +[DEFAULT] +support-files = + dummy.html + +[browser_about_cache.js] +[browser_NetUtil.js] +[browser_child_resource.js] +skip-if = e10s && debug && os == "linux" && bits == 64 +[browser_post_file.js] +[browser_nsIFormPOSTActionChannel.js] +skip-if = e10s # protocol handler and channel does not work in content process diff --git a/netwerk/test/browser/browser_NetUtil.js b/netwerk/test/browser/browser_NetUtil.js new file mode 100644 index 000000000..a6c4f2bcd --- /dev/null +++ b/netwerk/test/browser/browser_NetUtil.js @@ -0,0 +1,92 @@ +/* +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +*/ + +Components.utils.import("resource://gre/modules/NetUtil.jsm"); + +function test() { + waitForExplicitFinish(); + + // We overload this test to include verifying that httpd.js is + // importable as a testing-only JS module. + Components.utils.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: XPCOMUtils.generateQI([Ci.nsIProgressEventSink, + Ci.nsIInterfaceRequestor]), + getInterface: function (aIID) { return this.QueryInterface(aIID); }, + onProgress: function () {}, + onStatus: function () {} + }; + 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: function(aXULWindow) { + var domwindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindow); + var self = this; + domwindow.addEventListener("load", function() { + domwindow.removeEventListener("load", arguments.callee, false); + + if (domwindow.document.location.href != self.url) + return; + + // Allow other window load listeners to execute before passing to callback + executeSoon(function() { + self.callback(domwindow); + }); + }, false); + }, + onCloseWindow: function(aXULWindow) {}, + onWindowTitleChange: function(aXULWindow, aNewTitle) {} +} diff --git a/netwerk/test/browser/browser_about_cache.js b/netwerk/test/browser/browser_about_cache.js new file mode 100644 index 000000000..38cfa3d02 --- /dev/null +++ b/netwerk/test/browser/browser_about_cache.js @@ -0,0 +1,71 @@ +"use strict"; + +/** + * Open a dummy page, then open about:cache and verify the opened page shows up in the cache. + */ +add_task(function*() { + 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 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, kTestPage, true); + yield BrowserTestUtils.removeTab(tab); + + tab = yield 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); + yield ContentTask.spawn(tab.linkedBrowser, null, function() { + ok(!content.document.nodePrincipal.isSystemPrincipal, + "about:cache should not have system principal"); + let principalURI = content.document.nodePrincipal.URI; + let channel = content.document.docShell.currentDocumentChannel; + ok(!channel.loadInfo.loadingPrincipal, "Loading principal should be null."); + is(principalURI && principalURI.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(); + }); + yield 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); + yield ContentTask.spawn(tab.linkedBrowser, kTestPage, function(kTestPage) { + ok(!content.document.nodePrincipal.isSystemPrincipal, + "about:cache with query params should still not have system principal"); + let principalURI = content.document.nodePrincipal.URI; + is(principalURI && principalURI.spec, content.document.location.href, "Principal matches location"); + let channel = content.document.docShell.currentDocumentChannel; + principalURI = channel.loadInfo.triggeringPrincipal.URI; + is(principalURI && principalURI.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(); + }); + yield entryLoaded; + info("about:cache entry loaded"); + + + yield ContentTask.spawn(tab.linkedBrowser, triggeringURISpec, function(triggeringURISpec) { + ok(!content.document.nodePrincipal.isSystemPrincipal, + "about:cache-entry should also not have system principal"); + let principalURI = content.document.nodePrincipal.URI; + is(principalURI && principalURI.spec, content.document.location.href, "Principal matches location"); + let channel = content.document.docShell.currentDocumentChannel; + principalURI = channel.loadInfo.triggeringPrincipal.URI; + is(principalURI && principalURI.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."); + }); + yield BrowserTestUtils.removeTab(tab); +}); diff --git a/netwerk/test/browser/browser_child_resource.js b/netwerk/test/browser/browser_child_resource.js new file mode 100644 index 000000000..098e6bd84 --- /dev/null +++ b/netwerk/test/browser/browser_child_resource.js @@ -0,0 +1,256 @@ +/* +Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ +*/ + +// 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" : ""; + +Components.utils.import("resource://gre/modules/Services.jsm"); +const resProtocol = Cc["@mozilla.org/network/protocol;1?name=resource"] + .getService(Ci.nsIResProtocolHandler); + +function getMinidumpDirectory() { + var dir = Services.dirsvc.get('ProfD', Ci.nsIFile); + dir.append("minidumps"); + return dir; +} + +// This observer is needed so we can clean up all evidence of the crash so +// the testrunner thinks things are peachy. +var CrashObserver = { + observe: function(subject, topic, data) { + is(topic, 'ipc:content-shutdown', 'Received correct observer topic.'); + ok(subject instanceof Ci.nsIPropertyBag2, + 'Subject implements nsIPropertyBag2.'); + // we might see this called as the process terminates due to previous tests. + // We are only looking for "abnormal" exits... + if (!subject.hasKey("abnormal")) { + info("This is a normal termination and isn't the one we are looking for..."); + return; + } + + var dumpID; + if ('nsICrashReporter' in Ci) { + dumpID = subject.getPropertyAsAString('dumpID'); + ok(dumpID, "dumpID is present and not an empty string"); + } + + if (dumpID) { + var minidumpDirectory = getMinidumpDirectory(); + let file = minidumpDirectory.clone(); + file.append(dumpID + '.dmp'); + file.remove(true); + file = minidumpDirectory.clone(); + file.append(dumpID + '.extra'); + file.remove(true); + } + } +} +Services.obs.addObserver(CrashObserver, 'ipc:content-shutdown', false); + +registerCleanupFunction(() => { + Services.obs.removeObserver(CrashObserver, 'ipc:content-shutdown'); +}); + +function frameScript() { + Components.utils.import("resource://gre/modules/Services.jsm"); + let resProtocol = Components.classes["@mozilla.org/network/protocol;1?name=resource"] + .getService(Components.interfaces.nsIResProtocolHandler); + + addMessageListener("Test:ResolveURI", function({ data: uri }) { + uri = Services.io.newURI(uri, null, null); + try { + let resolved = resProtocol.resolveURI(uri); + sendAsyncMessage("Test:ResolvedURI", resolved); + } + catch (e) { + sendAsyncMessage("Test:ResolvedURI", null); + } + }); + + addMessageListener("Test:Crash", function() { + dump("Crashing\n"); + privateNoteIntentionalCrash(); + Components.utils.import("resource://gre/modules/ctypes.jsm"); + let zero = new ctypes.intptr_t(8); + let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t)); + badptr.contents + }); +} + +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, null, null); + try { + return resProtocol.resolveURI(uri); + } + catch (e) { + return null; + } +} + +function remoteResolveURI(uri) { + return new Promise((resolve) => { + let manager = gBrowser.selectedBrowser.messageManager; + + function listener({ data: resolved }) { + manager.removeMessageListener("Test:ResolvedURI", listener); + resolve(resolved); + } + + manager.addMessageListener("Test:ResolvedURI", listener); + manager.sendAsyncMessage("Test:ResolveURI", uri); + }); +} + +var loadTestTab = Task.async(function*() { + gBrowser.selectedTab = gBrowser.addTab(TEST_URL); + let browser = gBrowser.selectedBrowser; + yield BrowserTestUtils.browserLoaded(browser); + browser.messageManager.loadFrameScript("data:,(" + frameScript.toString() + ")();", true); + return browser; +}); + +// Restarts the child process by crashing it then reloading the tab +var restart = Task.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; + + browser.messageManager.sendAsyncMessage("Test:Crash"); + yield waitForEvent(browser, "AboutTabCrashedLoad", false, true); + + browser.reload(); + + yield BrowserTestUtils.browserLoaded(browser); + is(browser.getAttribute("remote"), expectedRemote, "Browser should be in the right process"); + browser.messageManager.loadFrameScript("data:,(" + frameScript.toString() + ")();", true); + return browser; +}); + +// Sanity check that this test is going to be useful +add_task(function*() { + let browser = yield loadTestTab(); + + // This must be loaded in the remote process for this test to be useful + is(browser.getAttribute("remote"), expectedRemote, "Browser should be in the right process"); + + let local = resolveURI("resource://gre/modules/Services.jsm"); + let remote = yield 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(function*() { + let browser = yield loadTestTab(); + + info("Set"); + resProtocol.setSubstitution("testing", Services.io.newURI("chrome://global/content", null, null)); + let local = resolveURI("resource://testing/test.js"); + let remote = yield 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", null, null)); + local = resolveURI("resource://testing/test.js"); + remote = yield 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 = yield 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(function*() { + let browser = yield loadTestTab(); + + info("Set"); + resProtocol.setSubstitution("testing", Services.io.newURI("chrome://global/content", null, null)); + let local = resolveURI("resource://testing/test.js"); + let remote = yield 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"); + + yield restart(); + + local = resolveURI("resource://testing/test.js"); + remote = yield 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", null, null)); + + yield restart(); + + local = resolveURI("resource://testing/test.js"); + remote = yield 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); + + yield restart(); + + local = resolveURI("resource://testing/test.js"); + remote = yield 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(function*() { + let browser = yield loadTestTab(); + + info("Set"); + resProtocol.setSubstitution("testing", Services.io.newURI("chrome://global/content", null, null)); + resProtocol.setSubstitution("testing2", Services.io.newURI("resource://testing", null, null)); + let local = resolveURI("resource://testing2/test.js"); + let remote = yield 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 = yield 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 = yield 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_nsIFormPOSTActionChannel.js b/netwerk/test/browser/browser_nsIFormPOSTActionChannel.js new file mode 100644 index 000000000..150c4feca --- /dev/null +++ b/netwerk/test/browser/browser_nsIFormPOSTActionChannel.js @@ -0,0 +1,284 @@ +/* + * Tests for bug 1241377: A channel with nsIFormPOSTActionChannel interface + * should be able to accept form POST. + */ + +Components.utils.import("resource://gre/modules/Services.jsm"); +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); +Components.utils.import("resource://gre/modules/Task.jsm"); + +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; + }, + newURI: function(aSpec, aOriginCharset, aBaseURI) { + var uri = Cc["@mozilla.org/network/standard-url;1"]. + createInstance(Ci.nsIURI); + uri.spec = aSpec; + return uri; + }, + newChannel2: function(aURI, aLoadInfo) { + return new CustomChannel(aURI, aLoadInfo); + }, + newChannel: function(aURI) { + throw Cr.NS_ERROR_NOT_IMPLEMENTED; + }, + allowPort: function(port, scheme) { + return port != -1; + }, + + /** nsIFactory */ + createInstance: function(aOuter, aIID) { + if (aOuter) { + throw Cr.NS_ERROR_NO_AGGREGATION; + } + return this.QueryInterface(aIID); + }, + lockFactory: function() {}, + + /** nsISupports */ + QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler, + Ci.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 = XPCOMUtils.generateQI(interfaces); +} +CustomChannel.prototype = { + /** nsIUploadChannel */ + get uploadStream() { + return this._uploadStream; + }, + set uploadStream(val) { + throw Cr.NS_ERROR_NOT_IMPLEMENTED; + }, + setUploadStream: function(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 Cr.NS_ERROR_NOT_IMPLEMENTED; + }, + open: function() { + throw Cr.NS_ERROR_NOT_IMPLEMENTED; + }, + open2: function() { + throw Cr.NS_ERROR_NOT_IMPLEMENTED; + }, + asyncOpen: function(aListener, aContext) { + 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 = ""; + if (this._uploadStream) { + var bstream = Cc["@mozilla.org/binaryinputstream;1"] + .createInstance(Ci.nsIBinaryInputStream); + bstream.setInputStream(this._uploadStream); + postData = bstream.readBytes(bstream.available()); + } + data += ` +<input id="upload_stream" value="${this._uploadStream ? "yes" : "no"}"> +<input id="post_data" value="${btoa(postData)}"> +`; + } + + 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, aContext); + } catch(e) {} + try { + aListener.onDataAvailable(this, aContext, stream, 0, stream.available()); + } catch(e) {} + try { + aListener.onStopRequest(this, aContext, Cr.NS_OK); + } catch(e) {} + } + }; + Services.tm.currentThread.dispatch(runnable, Ci.nsIEventTarget.DISPATCH_NORMAL); + }, + asyncOpen2: function(aListener) { + this.asyncOpen(aListener, null); + }, + + /** nsIRequest */ + get name() { + return this.uri.spec; + }, + isPending: function () { + return false; + }, + get status() { + return Cr.NS_OK; + }, + cancel: function(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"); + if (upload_stream && post_data) { + sendAsyncMessage("Test:IFrameLoaded", [upload_stream.value, post_data.value]); + return; + } + } + } + + setTimeout(check, 100); + }; + + check(); + }); +} + +function loadTestTab(uri) { + gBrowser.selectedTab = gBrowser.addTab(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] }) { + manager.removeMessageListener("Test:IFrameLoaded", listener); + resolve([hasUploadStream, atob(postData)]); + } + + manager.addMessageListener("Test:IFrameLoaded", listener); + manager.sendAsyncMessage("Test:WaitForIFrame"); + }); +} + +add_task(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(function*() { + var [hasUploadStream, postData] = yield loadTestTab(NORMAL_FORM_URI); + is(hasUploadStream, "no", "normal action should not have uploadStream"); + + gBrowser.removeCurrentTab(); +}); + +add_task(function*() { + var [hasUploadStream, postData] = yield loadTestTab(UPLOAD_FORM_URI); + is(hasUploadStream, "no", "upload action should not have uploadStream"); + + gBrowser.removeCurrentTab(); +}); + +add_task(function*() { + var [hasUploadStream, postData] = yield loadTestTab(POST_FORM_URI); + is(hasUploadStream, "yes", "post action should have uploadStream"); + is(postData, + "Content-Type: text/plain\r\n" + + "Content-Length: 9\r\n" + + "\r\n" + + "foo=bar\r\n", "POST data is received correctly"); + + 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 000000000..6c9fd7a2f --- /dev/null +++ b/netwerk/test/browser/browser_post_file.js @@ -0,0 +1,101 @@ +/* + * Tests for bug 1241100: Post to local file should not overwrite the file. + */ + +Components.utils.import("resource://gre/modules/osfile.jsm"); + +function* createTestFile(filename, content) { + let path = OS.Path.join(OS.Constants.Path.tmpDir, filename); + yield OS.File.writeAtomic(path, content); + return path; +} + +function* readFile(path) { + var array = yield OS.File.read(path); + var decoder = new TextDecoder(); + return decoder.decode(array); +} + +function frameScript() { + addMessageListener("Test:WaitForIFrame", function() { + var check = function() { + if (content) { + var frame = content.document.getElementById("frame"); + if (frame) { + var okBox = frame.contentDocument.getElementById("action_file_ok"); + if (okBox) { + sendAsyncMessage("Test:IFrameLoaded"); + return; + } + } + } + + setTimeout(check, 100); + }; + + check(); + }); +} + +add_task(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 = yield* createTestFile(postFilename, postFileContent); + var actionPath = yield* createTestFile(actionFilename, actionFileContent); + + var postURI = OS.Path.toFileURI(postPath); + + gBrowser.selectedTab = gBrowser.addTab(postURI); + let browser = gBrowser.selectedBrowser; + browser.messageManager.loadFrameScript("data:,(" + frameScript.toString() + ")();", true); + yield new Promise(resolve => { + let manager = browser.messageManager; + + function listener() { + manager.removeMessageListener("Test:IFrameLoaded", listener); + resolve(); + } + + manager.addMessageListener("Test:IFrameLoaded", listener); + manager.sendAsyncMessage("Test:WaitForIFrame"); + }); + + var actionFileContentAfter = yield* readFile(actionPath); + is(actionFileContentAfter, actionFileContent, "action file is not modified"); + + yield OS.File.remove(postPath); + yield OS.File.remove(actionPath); + + gBrowser.removeCurrentTab(); +}); diff --git a/netwerk/test/browser/dummy.html b/netwerk/test/browser/dummy.html new file mode 100644 index 000000000..6b28a248f --- /dev/null +++ b/netwerk/test/browser/dummy.html @@ -0,0 +1,7 @@ +<!DOCTYPE html> + +<html> +<body> + <p>Dummy Page</p> +</body> +</html> |