Last active
September 18, 2016 01:58
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
A non formdata way to send multipart/form data - https://github.com/mook/uploadscreenshot/blob/e68359ef64a384dab6f72c97796d3057b79a1ff3/chrome/content/uploadscreenshot.js#L339