Skip to content

Instantly share code, notes, and snippets.

@Noitidart
Last active September 18, 2016 01:58
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Noitidart/30e44f6d88423bf5096e to your computer and use it in GitHub Desktop.
Save Noitidart/30e44f6d88423bf5096e to your computer and use it in GitHub Desktop.
_ff-addon-snippet-xhrPromise - A function that does XHR wrapped up in a promise. MAINTHREAD USE ONLY. Cannot use from Workers.
// rev10 - https://gist.github.com/Noitidart/30e44f6d88423bf5096e
function xhrPromise(aUrlOrFileUri, aOptions={}) {
// does an async request
// aUrlOrFileUri is either a string of a FileURI such as `OS.Path.toFileURI(OS.Path.join(OS.Constants.Path.desktopDir, 'test.png'));` or a URL such as `http://github.com/wet-boew/wet-boew/archive/master.zip`
// :note: When using XMLHttpRequest to access a file:// URL the request.status is not properly set to 200 to indicate success. In such cases, request.readyState == 4, request.status == 0 and request.response will evaluate to true.
// Returns a promise
// resolves with xhr object
// rejects with object holding property "xhr" which holds the xhr object
var aOptionsDefaults = {
loadFlags: Ci.nsIRequest.LOAD_ANONYMOUS | Ci.nsIRequest.LOAD_BYPASS_CACHE | Ci.nsIRequest.INHIBIT_PERSISTENT_CACHING, // https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/NsIRequest#Constants
// aPostData: null, // discontinued, if you want to post, then set options {method:'POST', data:jQLike.serialize({a:'true',b:'false'})}
responseType: 'text',
bgRequest: true, // boolean. If true, no load group is associated with the request, and security dialogs are prevented from being shown to the user
timeout: 0, // integer, milliseconds, 0 means never timeout, value is in milliseconds
headers: null, // make it an object of key value pairs
method: 'GET', // string
data: null, // make it whatever you want (formdata, null, etc), but follow the rules, like if aMethod is 'GET' then this must be null
onredirect: false // http://stackoverflow.com/a/11240627/1828637
};
aOptions = Object.assign(aOptionsDefaults, aOptions);
var deferredMain_xhr = new Deferred();
var xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest);
var handler = ev => {
evf(m => xhr.removeEventListener(m, handler, !1));
switch (ev.type) {
case 'load':
// note: if url was a file uri, xhr.readyState is 0, but you get to here
// otherwise xhr.readyState is 4
deferredMain_xhr.resolve({request:xhr, ok:true});
break;
case 'abort':
case 'error':
case 'timeout':
deferredMain_xhr.resolve({request:xhr, ok:false, reason:ev.type});
break;
default:
var result_details = {
reason: 'unknown',
request: xhr,
message: xhr.statusText + ' [' + ev.type + ':' + xhr.status + ']'
};
deferredMain_xhr.resolve({request:xhr, ok:false, reason:ev.type, result_details});
}
};
var evf = f => ['load', 'error', 'abort', 'timeout'].forEach(f);
evf(m => xhr.addEventListener(m, handler, false));
if (aOptions.bgRequest) xhr.mozBackgroundRequest = true;
if (aOptions.timeout) xhr.timeout = aOptions.timeout; // set time to timeout after, in ms
var do_setHeaders = function() {
if (aOptions.headers) {
for (var h in aOptions.headers) {
xhr.setRequestHeader(h, aOptions.headers[h]);
}
}
};
xhr.open(aOptions.method, aUrlOrFileUri, true);
do_setHeaders();
xhr.channel.loadFlags = aOptions.loadFlags;
xhr.responseType = aOptions.responseType;
if (aOptions.onredirect) {
var oldNotifications = xhr.channel.notificationCallbacks;
var oldEventSink = null;
xhr.channel.notificationCallbacks = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIInterfaceRequestor, Ci.nsIChannelEventSink]),
getInterface: function(iid) {
// We are only interested in nsIChannelEventSink, return the old callbacks for any other interface requests.
if (iid.equals(Ci.nsIChannelEventSink)) {
try {
oldEventSink = oldNotifications.QueryInterface(iid);
} catch (ignore) {}
return this;
}
if (!oldNotifications) throw Cr.NS_ERROR_NO_INTERFACE;
return oldNotifications.QueryInterface(iid);
},
asyncOnChannelRedirect: function(oldChannel, newChannel, flags, callback) {
aOptions.onredirect(oldChannel, newChannel, flags, callback); // if i want to cancel the redirect do throw anything per https://dxr.mozilla.org/mozilla-central/source/netwerk/base/nsIChannelEventSink.idl#94
if (oldEventSink)
oldEventSink.asyncOnChannelRedirect(oldChannel, newChannel, flags, callback);
else
callback.onRedirectVerifyCallback(Cr.NS_OK);
}
};
}
xhr.send(aOptions.data);
return deferredMain_xhr.promise;
}
@Noitidart
Copy link
Author

Noitidart commented Jul 13, 2015

README

Rev1

  • Works

Rev2

  • Added in support for aOptions.Headers

Rev3

  • Added support for aOptions.aMethod tested it with DELETE on Imgur

Rev4

  • Fixed timeout support

Rev5

  • Updated comment about aPostData and Headers, and that if you set post data it likely will not post unless you set header of 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'

Rev6

  • Apparently I made updates on 092315 and I didn't include the rev5, so i added the rev5 stuff to the 092315 stuff

Rev7

  • Added support for sending non null for things like put via the aMethodSend option, for example:

      var myDomFile = new File(OS.Path.join(OS.Constants.Path.desktopDir, 'Share to Classroom - Chrome Version.xpi'));
      var formData = Cc['@mozilla.org/files/formdata;1'].createInstance(Ci.nsIDOMFormData);
      formData.append('Content-Type', 'multipart/form-data');
      formData.append('upload', myDomFile);
    
      var promise_sign = xhr('https://addons.mozilla.org/api/v3/addons/noida-id-1/versions/0.1/', {
          aMethod: 'PUT',
          aMethodSend: formData,
          Headers: {
              Authorization: 'JWT ' + token
          }
      });
    

Rev8

Rev9

  • Made more true to xhrAsync from worker xhr style
  • Removed depedency so no more depedency on validateOptions, its es6 Object.assign
  • Support for onredirect option

Rev10

  • Forgot to add the rev9 code, i had just pasted the rev9 comment/link at top
  • Also took this chance to clean up the old crap in comments

@Noitidart
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment