1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/extensions/pdfjs/content/network.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,237 @@ 1.4 +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ 1.6 +/* Copyright 2012 Mozilla Foundation 1.7 + * 1.8 + * Licensed under the Apache License, Version 2.0 (the "License"); 1.9 + * you may not use this file except in compliance with the License. 1.10 + * You may obtain a copy of the License at 1.11 + * 1.12 + * http://www.apache.org/licenses/LICENSE-2.0 1.13 + * 1.14 + * Unless required by applicable law or agreed to in writing, software 1.15 + * distributed under the License is distributed on an "AS IS" BASIS, 1.16 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1.17 + * See the License for the specific language governing permissions and 1.18 + * limitations under the License. 1.19 + */ 1.20 + 1.21 +// NOTE: Be careful what goes in this file, as it is also used from the context 1.22 +// of the addon. So using warn/error in here will break the addon. 1.23 + 1.24 +'use strict'; 1.25 + 1.26 + 1.27 + 1.28 + Components.utils.import('resource://gre/modules/Services.jsm'); 1.29 + 1.30 + var EXPORTED_SYMBOLS = ['NetworkManager']; 1.31 + 1.32 + var console = { 1.33 + log: function console_log(aMsg) { 1.34 + var msg = 'network.js: ' + (aMsg.join ? aMsg.join('') : aMsg); 1.35 + Services.console.logStringMessage(msg); 1.36 + // TODO(mack): dump() doesn't seem to work here... 1.37 + dump(msg + '\n'); 1.38 + } 1.39 + } 1.40 + 1.41 +var NetworkManager = (function NetworkManagerClosure() { 1.42 + 1.43 + var OK_RESPONSE = 200; 1.44 + var PARTIAL_CONTENT_RESPONSE = 206; 1.45 + 1.46 + function NetworkManager(url, args) { 1.47 + this.url = url; 1.48 + args = args || {}; 1.49 + this.isHttp = /^https?:/i.test(url); 1.50 + this.httpHeaders = (this.isHttp && args.httpHeaders) || {}; 1.51 + this.withCredentials = args.withCredentials || false; 1.52 + this.getXhr = args.getXhr || 1.53 + function NetworkManager_getXhr() { 1.54 + return new XMLHttpRequest(); 1.55 + }; 1.56 + 1.57 + this.currXhrId = 0; 1.58 + this.pendingRequests = {}; 1.59 + this.loadedRequests = {}; 1.60 + } 1.61 + 1.62 + function getArrayBuffer(xhr) { 1.63 + var data = (xhr.mozResponseArrayBuffer || xhr.mozResponse || 1.64 + xhr.responseArrayBuffer || xhr.response); 1.65 + if (typeof data !== 'string') { 1.66 + return data; 1.67 + } 1.68 + var length = data.length; 1.69 + var buffer = new Uint8Array(length); 1.70 + for (var i = 0; i < length; i++) { 1.71 + buffer[i] = data.charCodeAt(i) & 0xFF; 1.72 + } 1.73 + return buffer; 1.74 + } 1.75 + 1.76 + NetworkManager.prototype = { 1.77 + requestRange: function NetworkManager_requestRange(begin, end, listeners) { 1.78 + var args = { 1.79 + begin: begin, 1.80 + end: end 1.81 + }; 1.82 + for (var prop in listeners) { 1.83 + args[prop] = listeners[prop]; 1.84 + } 1.85 + return this.request(args); 1.86 + }, 1.87 + 1.88 + requestFull: function NetworkManager_requestRange(listeners) { 1.89 + return this.request(listeners); 1.90 + }, 1.91 + 1.92 + request: function NetworkManager_requestRange(args) { 1.93 + var xhr = this.getXhr(); 1.94 + var xhrId = this.currXhrId++; 1.95 + var pendingRequest = this.pendingRequests[xhrId] = { 1.96 + xhr: xhr 1.97 + }; 1.98 + 1.99 + xhr.open('GET', this.url); 1.100 + xhr.withCredentials = this.withCredentials; 1.101 + for (var property in this.httpHeaders) { 1.102 + var value = this.httpHeaders[property]; 1.103 + if (typeof value === 'undefined') { 1.104 + continue; 1.105 + } 1.106 + xhr.setRequestHeader(property, value); 1.107 + } 1.108 + if (this.isHttp && 'begin' in args && 'end' in args) { 1.109 + var rangeStr = args.begin + '-' + (args.end - 1); 1.110 + xhr.setRequestHeader('Range', 'bytes=' + rangeStr); 1.111 + pendingRequest.expectedStatus = 206; 1.112 + } else { 1.113 + pendingRequest.expectedStatus = 200; 1.114 + } 1.115 + 1.116 + xhr.mozResponseType = xhr.responseType = 'arraybuffer'; 1.117 + 1.118 + if (args.onProgress) { 1.119 + xhr.onprogress = args.onProgress; 1.120 + } 1.121 + if (args.onError) { 1.122 + xhr.onerror = function(evt) { 1.123 + args.onError(xhr.status); 1.124 + }; 1.125 + } 1.126 + xhr.onreadystatechange = this.onStateChange.bind(this, xhrId); 1.127 + 1.128 + pendingRequest.onHeadersReceived = args.onHeadersReceived; 1.129 + pendingRequest.onDone = args.onDone; 1.130 + pendingRequest.onError = args.onError; 1.131 + 1.132 + xhr.send(null); 1.133 + 1.134 + return xhrId; 1.135 + }, 1.136 + 1.137 + onStateChange: function NetworkManager_onStateChange(xhrId, evt) { 1.138 + var pendingRequest = this.pendingRequests[xhrId]; 1.139 + if (!pendingRequest) { 1.140 + // Maybe abortRequest was called... 1.141 + return; 1.142 + } 1.143 + 1.144 + var xhr = pendingRequest.xhr; 1.145 + if (xhr.readyState >= 2 && pendingRequest.onHeadersReceived) { 1.146 + pendingRequest.onHeadersReceived(); 1.147 + delete pendingRequest.onHeadersReceived; 1.148 + } 1.149 + 1.150 + if (xhr.readyState !== 4) { 1.151 + return; 1.152 + } 1.153 + 1.154 + if (!(xhrId in this.pendingRequests)) { 1.155 + // The XHR request might have been aborted in onHeadersReceived() 1.156 + // callback, in which case we should abort request 1.157 + return; 1.158 + } 1.159 + 1.160 + delete this.pendingRequests[xhrId]; 1.161 + 1.162 + // success status == 0 can be on ftp, file and other protocols 1.163 + if (xhr.status === 0 && this.isHttp) { 1.164 + if (pendingRequest.onError) { 1.165 + pendingRequest.onError(xhr.status); 1.166 + } 1.167 + return; 1.168 + } 1.169 + var xhrStatus = xhr.status || OK_RESPONSE; 1.170 + 1.171 + // From http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.2: 1.172 + // "A server MAY ignore the Range header". This means it's possible to 1.173 + // get a 200 rather than a 206 response from a range request. 1.174 + var ok_response_on_range_request = 1.175 + xhrStatus === OK_RESPONSE && 1.176 + pendingRequest.expectedStatus === PARTIAL_CONTENT_RESPONSE; 1.177 + 1.178 + if (!ok_response_on_range_request && 1.179 + xhrStatus !== pendingRequest.expectedStatus) { 1.180 + if (pendingRequest.onError) { 1.181 + pendingRequest.onError(xhr.status); 1.182 + } 1.183 + return; 1.184 + } 1.185 + 1.186 + this.loadedRequests[xhrId] = true; 1.187 + 1.188 + var chunk = getArrayBuffer(xhr); 1.189 + if (xhrStatus === PARTIAL_CONTENT_RESPONSE) { 1.190 + var rangeHeader = xhr.getResponseHeader('Content-Range'); 1.191 + var matches = /bytes (\d+)-(\d+)\/(\d+)/.exec(rangeHeader); 1.192 + var begin = parseInt(matches[1], 10); 1.193 + pendingRequest.onDone({ 1.194 + begin: begin, 1.195 + chunk: chunk 1.196 + }); 1.197 + } else { 1.198 + pendingRequest.onDone({ 1.199 + begin: 0, 1.200 + chunk: chunk 1.201 + }); 1.202 + } 1.203 + }, 1.204 + 1.205 + hasPendingRequests: function NetworkManager_hasPendingRequests() { 1.206 + for (var xhrId in this.pendingRequests) { 1.207 + return true; 1.208 + } 1.209 + return false; 1.210 + }, 1.211 + 1.212 + getRequestXhr: function NetworkManager_getXhr(xhrId) { 1.213 + return this.pendingRequests[xhrId].xhr; 1.214 + }, 1.215 + 1.216 + isPendingRequest: function NetworkManager_isPendingRequest(xhrId) { 1.217 + return xhrId in this.pendingRequests; 1.218 + }, 1.219 + 1.220 + isLoadedRequest: function NetworkManager_isLoadedRequest(xhrId) { 1.221 + return xhrId in this.loadedRequests; 1.222 + }, 1.223 + 1.224 + abortAllRequests: function NetworkManager_abortAllRequests() { 1.225 + for (var xhrId in this.pendingRequests) { 1.226 + this.abortRequest(xhrId | 0); 1.227 + } 1.228 + }, 1.229 + 1.230 + abortRequest: function NetworkManager_abortRequest(xhrId) { 1.231 + var xhr = this.pendingRequests[xhrId].xhr; 1.232 + delete this.pendingRequests[xhrId]; 1.233 + xhr.abort(); 1.234 + } 1.235 + }; 1.236 + 1.237 + return NetworkManager; 1.238 +})(); 1.239 + 1.240 +