browser_nsIFormPOSTActionChannel.js (7214B)
1 /* 2 * Tests for bug 1241377: A channel with nsIFormPOSTActionChannel interface 3 * should be able to accept form POST. 4 */ 5 6 "use strict"; 7 8 const SCHEME = "x-bug1241377"; 9 10 const FORM_BASE = SCHEME + "://dummy/form/"; 11 const NORMAL_FORM_URI = FORM_BASE + "normal.html"; 12 const UPLOAD_FORM_URI = FORM_BASE + "upload.html"; 13 const POST_FORM_URI = FORM_BASE + "post.html"; 14 15 const ACTION_BASE = SCHEME + "://dummy/action/"; 16 const NORMAL_ACTION_URI = ACTION_BASE + "normal.html"; 17 const UPLOAD_ACTION_URI = ACTION_BASE + "upload.html"; 18 const POST_ACTION_URI = ACTION_BASE + "post.html"; 19 20 function CustomProtocolHandler() {} 21 CustomProtocolHandler.prototype = { 22 /** nsIProtocolHandler */ 23 get scheme() { 24 return SCHEME; 25 }, 26 newChannel(aURI, aLoadInfo) { 27 return new CustomChannel(aURI, aLoadInfo); 28 }, 29 allowPort(port) { 30 return port != -1; 31 }, 32 33 /** nsISupports */ 34 QueryInterface: ChromeUtils.generateQI(["nsIProtocolHandler"]), 35 }; 36 37 function CustomChannel(aURI, aLoadInfo) { 38 this.uri = aURI; 39 this.loadInfo = aLoadInfo; 40 41 this._uploadStream = null; 42 43 var interfaces = [Ci.nsIRequest, Ci.nsIChannel]; 44 if (this.uri.spec == POST_ACTION_URI) { 45 interfaces.push(Ci.nsIFormPOSTActionChannel); 46 } else if (this.uri.spec == UPLOAD_ACTION_URI) { 47 interfaces.push(Ci.nsIUploadChannel); 48 } 49 this.QueryInterface = ChromeUtils.generateQI(interfaces); 50 } 51 CustomChannel.prototype = { 52 /** nsIUploadChannel */ 53 get uploadStream() { 54 return this._uploadStream; 55 }, 56 set uploadStream(val) { 57 throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); 58 }, 59 setUploadStream(aStream) { 60 this._uploadStream = aStream; 61 }, 62 63 /** nsIChannel */ 64 get originalURI() { 65 return this.uri; 66 }, 67 get URI() { 68 return this.uri; 69 }, 70 owner: null, 71 notificationCallbacks: null, 72 get securityInfo() { 73 return null; 74 }, 75 get contentType() { 76 return "text/html"; 77 }, 78 set contentType(val) {}, 79 contentCharset: "UTF-8", 80 get contentLength() { 81 return -1; 82 }, 83 set contentLength(val) { 84 throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); 85 }, 86 open() { 87 throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); 88 }, 89 asyncOpen(aListener) { 90 var data = ` 91 <!DOCTYPE html> 92 <html> 93 <head> 94 <meta charset="utf-8"> 95 <title>test bug 1241377</title> 96 </head> 97 <body> 98 `; 99 100 if (this.uri.spec.startsWith(FORM_BASE)) { 101 data += ` 102 <form id="form" action="${this.uri.spec.replace(FORM_BASE, ACTION_BASE)}" 103 method="post" enctype="text/plain" target="frame"> 104 <input type="hidden" name="foo" value="bar"> 105 <input type="submit"> 106 </form> 107 108 <iframe id="frame" name="frame" width="200" height="200"></iframe> 109 110 <script type="text/javascript"> 111 <!-- 112 document.getElementById('form').submit(); 113 //--> 114 </script> 115 `; 116 } else if (this.uri.spec.startsWith(ACTION_BASE)) { 117 var postData = ""; 118 var headers = {}; 119 if (this._uploadStream) { 120 var bstream = Cc["@mozilla.org/binaryinputstream;1"].createInstance( 121 Ci.nsIBinaryInputStream 122 ); 123 bstream.setInputStream(this._uploadStream); 124 postData = bstream.readBytes(bstream.available()); 125 126 if (this._uploadStream instanceof Ci.nsIMIMEInputStream) { 127 this._uploadStream.visitHeaders((name, value) => { 128 headers[name] = value; 129 }); 130 } 131 } 132 data += ` 133 <input id="upload_stream" value="${this._uploadStream ? "yes" : "no"}"> 134 <input id="post_data" value="${btoa(postData)}"> 135 <input id="upload_headers" value='${JSON.stringify(headers)}'> 136 `; 137 } 138 139 data += ` 140 </body> 141 </html> 142 `; 143 144 var stream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance( 145 Ci.nsIStringInputStream 146 ); 147 stream.setByteStringData(data); 148 149 var runnable = { 150 run: () => { 151 try { 152 aListener.onStartRequest(this, null); 153 } catch (e) {} 154 try { 155 aListener.onDataAvailable(this, stream, 0, stream.available()); 156 } catch (e) {} 157 try { 158 aListener.onStopRequest(this, null, Cr.NS_OK); 159 } catch (e) {} 160 }, 161 }; 162 Services.tm.dispatchToMainThread(runnable); 163 }, 164 165 /** nsIRequest */ 166 get name() { 167 return this.uri.spec; 168 }, 169 isPending() { 170 return false; 171 }, 172 get status() { 173 return Cr.NS_OK; 174 }, 175 cancel() {}, 176 loadGroup: null, 177 loadFlags: 178 Ci.nsIRequest.LOAD_NORMAL | 179 Ci.nsIRequest.INHIBIT_CACHING | 180 Ci.nsIRequest.LOAD_BYPASS_CACHE, 181 }; 182 183 function frameScript() { 184 /* eslint-env mozilla/frame-script */ 185 /* eslint-disable mozilla/no-arbitrary-setTimeout */ 186 addMessageListener("Test:WaitForIFrame", function () { 187 var check = function () { 188 if (content) { 189 var frame = content.document.getElementById("frame"); 190 if (frame) { 191 var upload_stream = 192 frame.contentDocument.getElementById("upload_stream"); 193 var post_data = frame.contentDocument.getElementById("post_data"); 194 var headers = frame.contentDocument.getElementById("upload_headers"); 195 if (upload_stream && post_data && headers) { 196 sendAsyncMessage("Test:IFrameLoaded", [ 197 upload_stream.value, 198 post_data.value, 199 headers.value, 200 ]); 201 return; 202 } 203 } 204 } 205 206 setTimeout(check, 100); 207 }; 208 209 check(); 210 }); 211 /* eslint-enable mozilla/no-arbitrary-setTimeout */ 212 } 213 214 function loadTestTab(uri) { 215 gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, uri); 216 var browser = gBrowser.selectedBrowser; 217 218 let manager = browser.messageManager; 219 browser.messageManager.loadFrameScript( 220 "data:,(" + frameScript.toString() + ")();", 221 true 222 ); 223 224 return new Promise(resolve => { 225 function listener({ data: [hasUploadStream, postData, headers] }) { 226 manager.removeMessageListener("Test:IFrameLoaded", listener); 227 resolve([hasUploadStream, atob(postData), JSON.parse(headers)]); 228 } 229 230 manager.addMessageListener("Test:IFrameLoaded", listener); 231 manager.sendAsyncMessage("Test:WaitForIFrame"); 232 }); 233 } 234 235 add_task(async function () { 236 var handler = new CustomProtocolHandler(); 237 Services.io.registerProtocolHandler( 238 SCHEME, 239 handler, 240 Ci.nsIProtocolHandler.URI_NORELATIVE | 241 Ci.nsIProtocolHandler.URI_IS_LOCAL_RESOURCE, 242 -1 243 ); 244 registerCleanupFunction(function () { 245 Services.io.unregisterProtocolHandler(SCHEME); 246 }); 247 }); 248 249 add_task(async function () { 250 var [hasUploadStream] = await loadTestTab(NORMAL_FORM_URI); 251 is(hasUploadStream, "no", "normal action should not have uploadStream"); 252 253 gBrowser.removeCurrentTab(); 254 }); 255 256 add_task(async function () { 257 var [hasUploadStream] = await loadTestTab(UPLOAD_FORM_URI); 258 is(hasUploadStream, "no", "upload action should not have uploadStream"); 259 260 gBrowser.removeCurrentTab(); 261 }); 262 263 add_task(async function () { 264 var [hasUploadStream, postData, headers] = await loadTestTab(POST_FORM_URI); 265 266 is(hasUploadStream, "yes", "post action should have uploadStream"); 267 is(postData, "foo=bar\r\n", "POST data is received correctly"); 268 269 is(headers["Content-Type"], "text/plain", "Content-Type header is correct"); 270 is(headers["Content-Length"], undefined, "Content-Length header is correct"); 271 272 gBrowser.removeCurrentTab(); 273 });