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