tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 });