/*
* 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();
});