/* * 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 = ` test bug 1241377 `; if (this.uri.spec.startsWith(FORM_BASE)) { data += `
`; } 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 += ` `; } data += ` `; 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(); });