You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
224 lines
6.2 KiB
224 lines
6.2 KiB
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ |
|
/* Copyright 2012 Mozilla Foundation |
|
* |
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
* you may not use this file except in compliance with the License. |
|
* You may obtain a copy of the License at |
|
* |
|
* http://www.apache.org/licenses/LICENSE-2.0 |
|
* |
|
* Unless required by applicable law or agreed to in writing, software |
|
* distributed under the License is distributed on an "AS IS" BASIS, |
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
* See the License for the specific language governing permissions and |
|
* limitations under the License. |
|
*/ |
|
|
|
// NOTE: Be careful what goes in this file, as it is also used from the context |
|
// of the addon. So using warn/error in here will break the addon. |
|
|
|
'use strict'; |
|
|
|
|
|
//#if (FIREFOX || MOZCENTRAL) |
|
// |
|
//Components.utils.import('resource://gre/modules/Services.jsm'); |
|
// |
|
//var EXPORTED_SYMBOLS = ['NetworkManager']; |
|
// |
|
//function log(aMsg) { |
|
// var msg = 'network.js: ' + (aMsg.join ? aMsg.join('') : aMsg); |
|
// Services.console.logStringMessage(msg); |
|
// // TODO(mack): dump() doesn't seem to work here... |
|
// dump(msg + '\n'); |
|
//} |
|
//#else |
|
function log(aMsg) { |
|
console.log(aMsg); |
|
} |
|
//#endif |
|
|
|
var NetworkManager = (function NetworkManagerClosure() { |
|
function NetworkManager(url, args) { |
|
this.url = url; |
|
args = args || {}; |
|
this.httpHeaders = args.httpHeaders || {}; |
|
this.getXhr = args.getXhr || |
|
function NetworkManager_getXhr() { |
|
return new XMLHttpRequest(); |
|
}; |
|
|
|
this.currXhrId = 0; |
|
this.pendingRequests = {}; |
|
this.loadedRequests = {}; |
|
} |
|
|
|
function getArrayBuffer(xhr) { |
|
var data = (xhr.mozResponseArrayBuffer || xhr.mozResponse || |
|
xhr.responseArrayBuffer || xhr.response); |
|
if (typeof data !== 'string') { |
|
return data; |
|
} |
|
var length = data.length; |
|
var buffer = new Uint8Array(length); |
|
for (var i = 0; i < length; i++) { |
|
buffer[i] = data.charCodeAt(i) & 0xFF; |
|
} |
|
return buffer; |
|
} |
|
|
|
NetworkManager.prototype = { |
|
requestRange: function NetworkManager_requestRange(begin, end, listeners) { |
|
var args = { |
|
begin: begin, |
|
end: end |
|
}; |
|
for (var prop in listeners) { |
|
args[prop] = listeners[prop]; |
|
} |
|
return this.request(args); |
|
}, |
|
|
|
requestFull: function NetworkManager_requestRange(listeners) { |
|
return this.request(listeners); |
|
}, |
|
|
|
request: function NetworkManager_requestRange(args) { |
|
var xhr = this.getXhr(); |
|
var xhrId = this.currXhrId++; |
|
var pendingRequest = this.pendingRequests[xhrId] = { |
|
xhr: xhr |
|
}; |
|
|
|
xhr.open('GET', this.url); |
|
for (var property in this.httpHeaders) { |
|
var value = this.httpHeaders[property]; |
|
if (typeof value === 'undefined') { |
|
continue; |
|
} |
|
xhr.setRequestHeader(property, value); |
|
} |
|
if ('begin' in args && 'end' in args) { |
|
var rangeStr = args.begin + '-' + (args.end - 1); |
|
xhr.setRequestHeader('Range', 'bytes=' + rangeStr); |
|
pendingRequest.expectedStatus = 206; |
|
} else { |
|
pendingRequest.expectedStatus = 200; |
|
} |
|
|
|
xhr.mozResponseType = xhr.responseType = 'arraybuffer'; |
|
|
|
if (args.onProgress) { |
|
xhr.onprogress = args.onProgress; |
|
} |
|
if (args.onError) { |
|
xhr.onerror = function(evt) { |
|
args.onError(xhr.status); |
|
}; |
|
} |
|
xhr.onreadystatechange = this.onStateChange.bind(this, xhrId); |
|
|
|
pendingRequest.onHeadersReceived = args.onHeadersReceived; |
|
pendingRequest.onDone = args.onDone; |
|
pendingRequest.onError = args.onError; |
|
|
|
xhr.send(null); |
|
|
|
return xhrId; |
|
}, |
|
|
|
onStateChange: function NetworkManager_onStateChange(xhrId, evt) { |
|
var pendingRequest = this.pendingRequests[xhrId]; |
|
if (!pendingRequest) { |
|
// Maybe abortRequest was called... |
|
return; |
|
} |
|
|
|
var xhr = pendingRequest.xhr; |
|
if (xhr.readyState >= 2 && pendingRequest.onHeadersReceived) { |
|
pendingRequest.onHeadersReceived(); |
|
delete pendingRequest.onHeadersReceived; |
|
} |
|
|
|
if (xhr.readyState !== 4) { |
|
return; |
|
} |
|
|
|
if (!(xhrId in this.pendingRequests)) { |
|
// The XHR request might have been aborted in onHeadersReceived() |
|
// callback, in which case we should abort request |
|
return; |
|
} |
|
|
|
delete this.pendingRequests[xhrId]; |
|
|
|
if (xhr.status === 0) { |
|
if (pendingRequest.onError) { |
|
pendingRequest.onError(xhr.status); |
|
} |
|
return; |
|
} |
|
|
|
if (xhr.status !== pendingRequest.expectedStatus) { |
|
if (pendingRequest.onError) { |
|
pendingRequest.onError(xhr.status); |
|
} |
|
return; |
|
} |
|
|
|
this.loadedRequests[xhrId] = true; |
|
|
|
var chunk = getArrayBuffer(xhr); |
|
if (pendingRequest.expectedStatus === 206) { |
|
var rangeHeader = xhr.getResponseHeader('Content-Range'); |
|
var matches = /bytes (\d+)-(\d+)\/(\d+)/.exec(rangeHeader); |
|
var begin = parseInt(matches[1], 10); |
|
var end = parseInt(matches[2], 10) + 1; |
|
pendingRequest.onDone({ |
|
begin: begin, |
|
end: end, |
|
chunk: chunk |
|
}); |
|
} else { |
|
pendingRequest.onDone({ |
|
chunk: chunk |
|
}); |
|
} |
|
}, |
|
|
|
hasPendingRequests: function NetworkManager_hasPendingRequests() { |
|
for (var xhrId in this.pendingRequests) { |
|
return true; |
|
} |
|
return false; |
|
}, |
|
|
|
getRequestXhr: function NetworkManager_getXhr(xhrId) { |
|
return this.pendingRequests[xhrId].xhr; |
|
}, |
|
|
|
isPendingRequest: function NetworkManager_isPendingRequest(xhrId) { |
|
return xhrId in this.pendingRequests; |
|
}, |
|
|
|
isLoadedRequest: function NetworkManager_isLoadedRequest(xhrId) { |
|
return xhrId in this.loadedRequests; |
|
}, |
|
|
|
abortAllRequests: function NetworkManager_abortAllRequests() { |
|
for (var xhrId in this.pendingRequests) { |
|
this.abortRequest(xhrId | 0); |
|
} |
|
}, |
|
|
|
abortRequest: function NetworkManager_abortRequest(xhrId) { |
|
var xhr = this.pendingRequests[xhrId].xhr; |
|
delete this.pendingRequests[xhrId]; |
|
xhr.abort(); |
|
} |
|
}; |
|
|
|
return NetworkManager; |
|
})(); |
|
|
|
|