Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
166 views
in Technique[技术] by (71.8m points)

javascript - Replication of Form method with loadOneTab

If I dynamically insert a form object into a page, submit and remove the form and it works fine.

Here is an example of the form code:

<form target="_blank" enctype="multipart/form-data" 
      action="https://www.example.com/" method="POST">
  <input value="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" name="image_content" type="hidden">
  <input value="" name="filename" type="hidden">
  <input value="" name="image_url" type="hidden">
</form>

When I try to do the same process with loadOneTab(), result POST is not exactly the same and therefore the result is not the same as above.
On checking the headers, "some value" is not sent fully (gets cropped) and it sets Content-Length: 0.
I must be missing something.

let postStream = Components.classes['@mozilla.org/network/mime-input-stream;1']
                   .createInstance(Components.interfaces.nsIMIMEInputStream);
postStream.addHeader('Content-Type', 'multipart/form-data');
postStream.addHeader('filename', '');
postStream.addHeader('image_url', '');
postStream.addHeader('image_content', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==');
postStream.addContentLength = true;
window.gBrowser.loadOneTab('https://www.example.com/',
    {inBackground: false, postData: postStream});

Note: image_content value is 'data:image/png;base64' Data URI
NoScript causes issues with sending form and XSS and I prefer to use loadOneTab for the inBackground

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Normally, one would use FormData to compose the postData of a request, but unfortunately we cannot do so here, as there is currently no way to get the stream (and other information) from a FormData instance (nsIXHRSendable is not scriptable, unfortunately), so we'll have to create a multipart/form-data stream ourselves.

Since it is likely you'll want to post some file data as well, I added file uploads as well. ;)

function encodeFormData(data, charset) {
  let encoder = Cc["@mozilla.org/intl/saveascharset;1"].
                createInstance(Ci.nsISaveAsCharset);
  encoder.Init(charset || "utf-8",
               Ci.nsISaveAsCharset.attr_EntityAfterCharsetConv + 
               Ci.nsISaveAsCharset.attr_FallbackDecimalNCR,
               0);
  let encode = function(val, header) {
    val = encoder.Convert(val);
    if (header) {
      val = val.replace(/
/g, " ").replace(/"/g, """);
    }
    return val;
  }

  let boundary = "----boundary--" + Date.now();
  let mpis = Cc['@mozilla.org/io/multiplex-input-stream;1'].
             createInstance(Ci.nsIMultiplexInputStream);
  let item = "";
  for (let k of Object.keys(data)) {
    item += "--" + boundary + "
";
    let v = data[k];
    if (v instanceof Ci.nsIFile) {
      let fstream = Cc["@mozilla.org/network/file-input-stream;1"].
                    createInstance(Ci.nsIFileInputStream); 
      fstream.init(v, -1, -1, Ci.nsIFileInputStream.DEFER_OPEN);
      item += "Content-Disposition: form-data; name="" + encode(k, true) + "";" +
              " filename="" + encode(v.leafName, true) + ""
";
      let ctype = "application/octet-stream";
      try {
        let mime = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
        ctype = mime.getTypeFromFile(v) || ctype;
      }
      catch (ex) {
        console.warn("failed to get type", ex);
      }
      item += "Content-Type: " + ctype + "

";
      let ss = Cc["@mozilla.org/io/string-input-stream;1"].
               createInstance(Ci.nsIStringInputStream);
      ss.data = item;
      mpis.appendStream(ss);
      mpis.appendStream(fstream);
      item = "";
    }
    else {
      item += "Content-Disposition: form-data; name="" + encode(k, true) + ""

";
      item += encode(v);
    }
    item += "
";
  }
  item += "--" + boundary + "--
";
  let ss = Cc["@mozilla.org/io/string-input-stream;1"].
           createInstance(Ci.nsIStringInputStream);
  ss.data = item;
  mpis.appendStream(ss);
  let postStream = Cc["@mozilla.org/network/mime-input-stream;1"].
                   createInstance(Ci.nsIMIMEInputStream);
  postStream.addHeader("Content-Type",
                       "multipart/form-data; boundary=" + boundary);
  postStream.setData(mpis);
  postStream.addContentLength = true;
  return postStream;
}

(You could use additional nsIMIMEInputStream instead of string-concatenating stuff together, but this would perform worse and has no real merit).

Which can then be used like e.g.:

let file = Services.dirsvc.get("Desk", Ci.nsIFile);
file.append("australis-xp ha?llow, wo?rld.png");

let postData = encodeFormData({
  "filename": "",
  "image_url": "",
  "image_content": "--somne value ---",
  "contents": file
}, "iso8859-1");

gBrowser.loadOneTab("http://www.example.org/", {
  inBackground: false,
  postData: postData
});

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...