Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
michael@0 | 4 | * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | 'use strict'; |
michael@0 | 7 | |
michael@0 | 8 | const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components; |
michael@0 | 9 | |
michael@0 | 10 | Cu.import('resource://gre/modules/XPCOMUtils.jsm'); |
michael@0 | 11 | Cu.import('resource://gre/modules/Services.jsm'); |
michael@0 | 12 | |
michael@0 | 13 | const DEBUG = false; // set to true to show debug messages |
michael@0 | 14 | |
michael@0 | 15 | const kCAPTIVEPORTALDETECTOR_CONTRACTID = '@mozilla.org/toolkit/captive-detector;1'; |
michael@0 | 16 | const kCAPTIVEPORTALDETECTOR_CID = Components.ID('{d9cd00ba-aa4d-47b1-8792-b1fe0cd35060}'); |
michael@0 | 17 | |
michael@0 | 18 | const kOpenCaptivePortalLoginEvent = 'captive-portal-login'; |
michael@0 | 19 | const kAbortCaptivePortalLoginEvent = 'captive-portal-login-abort'; |
michael@0 | 20 | |
michael@0 | 21 | function URLFetcher(url, timeout) { |
michael@0 | 22 | let self = this; |
michael@0 | 23 | let xhr = Cc['@mozilla.org/xmlextras/xmlhttprequest;1'] |
michael@0 | 24 | .createInstance(Ci.nsIXMLHttpRequest); |
michael@0 | 25 | xhr.open('GET', url, true); |
michael@0 | 26 | // Prevent the request from reading from the cache. |
michael@0 | 27 | xhr.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE; |
michael@0 | 28 | // Prevent the request from writing to the cache. |
michael@0 | 29 | xhr.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING; |
michael@0 | 30 | // Prevent privacy leaks |
michael@0 | 31 | xhr.channel.loadFlags |= Ci.nsIRequest.LOAD_ANONYMOUS; |
michael@0 | 32 | // The Cache-Control header is only interpreted by proxies and the |
michael@0 | 33 | // final destination. It does not help if a resource is already |
michael@0 | 34 | // cached locally. |
michael@0 | 35 | xhr.setRequestHeader("Cache-Control", "no-cache"); |
michael@0 | 36 | // HTTP/1.0 servers might not implement Cache-Control and |
michael@0 | 37 | // might only implement Pragma: no-cache |
michael@0 | 38 | xhr.setRequestHeader("Pragma", "no-cache"); |
michael@0 | 39 | |
michael@0 | 40 | xhr.timeout = timeout; |
michael@0 | 41 | xhr.ontimeout = function () { self.ontimeout(); }; |
michael@0 | 42 | xhr.onerror = function () { self.onerror(); }; |
michael@0 | 43 | xhr.onreadystatechange = function(oEvent) { |
michael@0 | 44 | if (xhr.readyState === 4) { |
michael@0 | 45 | if (self._isAborted) { |
michael@0 | 46 | return; |
michael@0 | 47 | } |
michael@0 | 48 | if (xhr.status === 200) { |
michael@0 | 49 | self.onsuccess(xhr.responseText); |
michael@0 | 50 | } else if (xhr.status) { |
michael@0 | 51 | self.onredirectorerror(xhr.status); |
michael@0 | 52 | } |
michael@0 | 53 | } |
michael@0 | 54 | }; |
michael@0 | 55 | xhr.send(); |
michael@0 | 56 | this._xhr = xhr; |
michael@0 | 57 | } |
michael@0 | 58 | |
michael@0 | 59 | URLFetcher.prototype = { |
michael@0 | 60 | _isAborted: false, |
michael@0 | 61 | ontimeout: function() {}, |
michael@0 | 62 | onerror: function() {}, |
michael@0 | 63 | abort: function() { |
michael@0 | 64 | if (!this._isAborted) { |
michael@0 | 65 | this._isAborted = true; |
michael@0 | 66 | this._xhr.abort(); |
michael@0 | 67 | } |
michael@0 | 68 | }, |
michael@0 | 69 | } |
michael@0 | 70 | |
michael@0 | 71 | function LoginObserver(captivePortalDetector) { |
michael@0 | 72 | const LOGIN_OBSERVER_STATE_DETACHED = 0; /* Should not monitor network activity since no ongoing login procedure */ |
michael@0 | 73 | const LOGIN_OBSERVER_STATE_IDLE = 1; /* No network activity currently, waiting for a longer enough idle period */ |
michael@0 | 74 | const LOGIN_OBSERVER_STATE_BURST = 2; /* Network activity is detected, probably caused by a login procedure */ |
michael@0 | 75 | const LOGIN_OBSERVER_STATE_VERIFY_NEEDED = 3; /* Verifing network accessiblity is required after a long enough idle */ |
michael@0 | 76 | const LOGIN_OBSERVER_STATE_VERIFYING = 4; /* LoginObserver is probing if public network is available */ |
michael@0 | 77 | |
michael@0 | 78 | let state = LOGIN_OBSERVER_STATE_DETACHED; |
michael@0 | 79 | |
michael@0 | 80 | let timer = Cc['@mozilla.org/timer;1'].createInstance(Ci.nsITimer); |
michael@0 | 81 | let activityDistributor = Cc['@mozilla.org/network/http-activity-distributor;1'] |
michael@0 | 82 | .getService(Ci.nsIHttpActivityDistributor); |
michael@0 | 83 | let urlFetcher = null; |
michael@0 | 84 | |
michael@0 | 85 | let pageCheckingDone = function pageCheckingDone() { |
michael@0 | 86 | if (state === LOGIN_OBSERVER_STATE_VERIFYING) { |
michael@0 | 87 | urlFetcher = null; |
michael@0 | 88 | // Finish polling the canonical site, switch back to idle state and |
michael@0 | 89 | // waiting for next burst |
michael@0 | 90 | state = LOGIN_OBSERVER_STATE_IDLE; |
michael@0 | 91 | timer.initWithCallback(observer, |
michael@0 | 92 | captivePortalDetector._pollingTime, |
michael@0 | 93 | timer.TYPE_ONE_SHOT); |
michael@0 | 94 | } |
michael@0 | 95 | }; |
michael@0 | 96 | |
michael@0 | 97 | let checkPageContent = function checkPageContent() { |
michael@0 | 98 | debug("checking if public network is available after the login procedure"); |
michael@0 | 99 | |
michael@0 | 100 | urlFetcher = new URLFetcher(captivePortalDetector._canonicalSiteURL, |
michael@0 | 101 | captivePortalDetector._maxWaitingTime); |
michael@0 | 102 | urlFetcher.ontimeout = pageCheckingDone; |
michael@0 | 103 | urlFetcher.onerror = pageCheckingDone; |
michael@0 | 104 | urlFetcher.onsuccess = function (content) { |
michael@0 | 105 | if (captivePortalDetector.validateContent(content)) { |
michael@0 | 106 | urlFetcher = null; |
michael@0 | 107 | captivePortalDetector.executeCallback(true); |
michael@0 | 108 | } else { |
michael@0 | 109 | pageCheckingDone(); |
michael@0 | 110 | } |
michael@0 | 111 | }; |
michael@0 | 112 | urlFetcher.onredirectorerror = pageCheckingDone; |
michael@0 | 113 | }; |
michael@0 | 114 | |
michael@0 | 115 | // Public interface of LoginObserver |
michael@0 | 116 | let observer = { |
michael@0 | 117 | QueryInterface: XPCOMUtils.generateQI([Ci.nsIHttpActivityOberver, |
michael@0 | 118 | Ci.nsITimerCallback]), |
michael@0 | 119 | |
michael@0 | 120 | attach: function attach() { |
michael@0 | 121 | if (state === LOGIN_OBSERVER_STATE_DETACHED) { |
michael@0 | 122 | activityDistributor.addObserver(this); |
michael@0 | 123 | state = LOGIN_OBSERVER_STATE_IDLE; |
michael@0 | 124 | timer.initWithCallback(this, |
michael@0 | 125 | captivePortalDetector._pollingTime, |
michael@0 | 126 | timer.TYPE_ONE_SHOT); |
michael@0 | 127 | debug('attach HttpObserver for login activity'); |
michael@0 | 128 | } |
michael@0 | 129 | }, |
michael@0 | 130 | |
michael@0 | 131 | detach: function detach() { |
michael@0 | 132 | if (state !== LOGIN_OBSERVER_STATE_DETACHED) { |
michael@0 | 133 | if (urlFetcher) { |
michael@0 | 134 | urlFetcher.abort(); |
michael@0 | 135 | urlFetcher = null; |
michael@0 | 136 | } |
michael@0 | 137 | activityDistributor.removeObserver(this); |
michael@0 | 138 | timer.cancel(); |
michael@0 | 139 | state = LOGIN_OBSERVER_STATE_DETACHED; |
michael@0 | 140 | debug('detach HttpObserver for login activity'); |
michael@0 | 141 | } |
michael@0 | 142 | }, |
michael@0 | 143 | |
michael@0 | 144 | /* |
michael@0 | 145 | * Treat all HTTP transactions as captive portal login activities. |
michael@0 | 146 | */ |
michael@0 | 147 | observeActivity: function observeActivity(aHttpChannel, aActivityType, |
michael@0 | 148 | aActivitySubtype, aTimestamp, |
michael@0 | 149 | aExtraSizeData, aExtraStringData) { |
michael@0 | 150 | if (aActivityType === Ci.nsIHttpActivityObserver.ACTIVITY_TYPE_HTTP_TRANSACTION |
michael@0 | 151 | && aActivitySubtype === Ci.nsIHttpActivityObserver.ACTIVITY_SUBTYPE_RESPONSE_COMPLETE) { |
michael@0 | 152 | switch (state) { |
michael@0 | 153 | case LOGIN_OBSERVER_STATE_IDLE: |
michael@0 | 154 | case LOGIN_OBSERVER_STATE_VERIFY_NEEDED: |
michael@0 | 155 | state = LOGIN_OBSERVER_STATE_BURST; |
michael@0 | 156 | break; |
michael@0 | 157 | default: |
michael@0 | 158 | break; |
michael@0 | 159 | } |
michael@0 | 160 | } |
michael@0 | 161 | }, |
michael@0 | 162 | |
michael@0 | 163 | /* |
michael@0 | 164 | * Check if login activity is finished according to HTTP burst. |
michael@0 | 165 | */ |
michael@0 | 166 | notify : function notify() { |
michael@0 | 167 | switch(state) { |
michael@0 | 168 | case LOGIN_OBSERVER_STATE_BURST: |
michael@0 | 169 | // Wait while network stays idle for a short period |
michael@0 | 170 | state = LOGIN_OBSERVER_STATE_VERIFY_NEEDED; |
michael@0 | 171 | // Fall though to start polling timer |
michael@0 | 172 | case LOGIN_OBSERVER_STATE_IDLE: |
michael@0 | 173 | timer.initWithCallback(this, |
michael@0 | 174 | captivePortalDetector._pollingTime, |
michael@0 | 175 | timer.TYPE_ONE_SHOT); |
michael@0 | 176 | break; |
michael@0 | 177 | case LOGIN_OBSERVER_STATE_VERIFY_NEEDED: |
michael@0 | 178 | // Polling the canonical website since network stays idle for a while |
michael@0 | 179 | state = LOGIN_OBSERVER_STATE_VERIFYING; |
michael@0 | 180 | checkPageContent(); |
michael@0 | 181 | break; |
michael@0 | 182 | |
michael@0 | 183 | default: |
michael@0 | 184 | break; |
michael@0 | 185 | } |
michael@0 | 186 | }, |
michael@0 | 187 | }; |
michael@0 | 188 | |
michael@0 | 189 | return observer; |
michael@0 | 190 | } |
michael@0 | 191 | |
michael@0 | 192 | function CaptivePortalDetector() { |
michael@0 | 193 | // Load preference |
michael@0 | 194 | this._canonicalSiteURL = null; |
michael@0 | 195 | this._canonicalSiteExpectedContent = null; |
michael@0 | 196 | |
michael@0 | 197 | try { |
michael@0 | 198 | this._canonicalSiteURL = |
michael@0 | 199 | Services.prefs.getCharPref('captivedetect.canonicalURL'); |
michael@0 | 200 | this._canonicalSiteExpectedContent = |
michael@0 | 201 | Services.prefs.getCharPref('captivedetect.canonicalContent'); |
michael@0 | 202 | } catch(e) { |
michael@0 | 203 | debug('canonicalURL or canonicalContent not set.') |
michael@0 | 204 | } |
michael@0 | 205 | |
michael@0 | 206 | this._maxWaitingTime = |
michael@0 | 207 | Services.prefs.getIntPref('captivedetect.maxWaitingTime'); |
michael@0 | 208 | this._pollingTime = |
michael@0 | 209 | Services.prefs.getIntPref('captivedetect.pollingTime'); |
michael@0 | 210 | this._maxRetryCount = |
michael@0 | 211 | Services.prefs.getIntPref('captivedetect.maxRetryCount'); |
michael@0 | 212 | debug('Load Prefs {site=' + this._canonicalSiteURL + ',content=' |
michael@0 | 213 | + this._canonicalSiteExpectedContent + ',time=' + this._maxWaitingTime |
michael@0 | 214 | + "max-retry=" + this._maxRetryCount + '}'); |
michael@0 | 215 | |
michael@0 | 216 | // Create HttpObserver for monitoring the login procedure |
michael@0 | 217 | this._loginObserver = LoginObserver(this); |
michael@0 | 218 | |
michael@0 | 219 | this._nextRequestId = 0; |
michael@0 | 220 | this._runningRequest = null; |
michael@0 | 221 | this._requestQueue = []; // Maintain a progress table, store callbacks and the ongoing XHR |
michael@0 | 222 | this._interfaceNames = {}; // Maintain names of the requested network interfaces |
michael@0 | 223 | |
michael@0 | 224 | debug('CaptiveProtalDetector initiated, waitng for network connection established'); |
michael@0 | 225 | } |
michael@0 | 226 | |
michael@0 | 227 | CaptivePortalDetector.prototype = { |
michael@0 | 228 | classID: kCAPTIVEPORTALDETECTOR_CID, |
michael@0 | 229 | classInfo: XPCOMUtils.generateCI({classID: kCAPTIVEPORTALDETECTOR_CID, |
michael@0 | 230 | contractID: kCAPTIVEPORTALDETECTOR_CONTRACTID, |
michael@0 | 231 | classDescription: 'Captive Portal Detector', |
michael@0 | 232 | interfaces: [Ci.nsICaptivePortalDetector]}), |
michael@0 | 233 | QueryInterface: XPCOMUtils.generateQI([Ci.nsICaptivePortalDetector]), |
michael@0 | 234 | |
michael@0 | 235 | // nsICaptivePortalDetector |
michael@0 | 236 | checkCaptivePortal: function checkCaptivePortal(aInterfaceName, aCallback) { |
michael@0 | 237 | if (!this._canonicalSiteURL) { |
michael@0 | 238 | throw Components.Exception('No canonical URL set up.'); |
michael@0 | 239 | } |
michael@0 | 240 | |
michael@0 | 241 | // Prevent multiple requests on a single network interface |
michael@0 | 242 | if (this._interfaceNames[aInterfaceName]) { |
michael@0 | 243 | throw Components.Exception('Do not allow multiple request on one interface: ' + aInterface); |
michael@0 | 244 | } |
michael@0 | 245 | |
michael@0 | 246 | let request = {interfaceName: aInterfaceName}; |
michael@0 | 247 | if (aCallback) { |
michael@0 | 248 | let callback = aCallback.QueryInterface(Ci.nsICaptivePortalCallback); |
michael@0 | 249 | request['callback'] = callback; |
michael@0 | 250 | request['retryCount'] = 0; |
michael@0 | 251 | } |
michael@0 | 252 | this._addRequest(request); |
michael@0 | 253 | }, |
michael@0 | 254 | |
michael@0 | 255 | abort: function abort(aInterfaceName) { |
michael@0 | 256 | debug('abort for ' + aInterfaceName); |
michael@0 | 257 | this._removeRequest(aInterfaceName); |
michael@0 | 258 | }, |
michael@0 | 259 | |
michael@0 | 260 | finishPreparation: function finishPreparation(aInterfaceName) { |
michael@0 | 261 | debug('finish preparation phase for interface "' + aInterfaceName + '"'); |
michael@0 | 262 | if (!this._runningRequest |
michael@0 | 263 | || this._runningRequest.interfaceName !== aInterfaceName) { |
michael@0 | 264 | debug('invalid finishPreparation for ' + aInterfaceName); |
michael@0 | 265 | throw Components.Exception('only first request is allowed to invoke |finishPreparation|'); |
michael@0 | 266 | return; |
michael@0 | 267 | } |
michael@0 | 268 | |
michael@0 | 269 | this._startDetection(); |
michael@0 | 270 | }, |
michael@0 | 271 | |
michael@0 | 272 | cancelLogin: function cancelLogin(eventId) { |
michael@0 | 273 | debug('login canceled by user for request "' + eventId + '"'); |
michael@0 | 274 | // Captive portal login procedure is canceled by user |
michael@0 | 275 | if (this._runningRequest && this._runningRequest.hasOwnProperty('eventId')) { |
michael@0 | 276 | let id = this._runningRequest.eventId; |
michael@0 | 277 | if (eventId === id) { |
michael@0 | 278 | this.executeCallback(false); |
michael@0 | 279 | } |
michael@0 | 280 | } |
michael@0 | 281 | }, |
michael@0 | 282 | |
michael@0 | 283 | _applyDetection: function _applyDetection() { |
michael@0 | 284 | debug('enter applyDetection('+ this._runningRequest.interfaceName + ')'); |
michael@0 | 285 | |
michael@0 | 286 | // Execute network interface preparation |
michael@0 | 287 | if (this._runningRequest.hasOwnProperty('callback')) { |
michael@0 | 288 | this._runningRequest.callback.prepare(); |
michael@0 | 289 | } else { |
michael@0 | 290 | this._startDetection(); |
michael@0 | 291 | } |
michael@0 | 292 | }, |
michael@0 | 293 | |
michael@0 | 294 | _startDetection: function _startDetection() { |
michael@0 | 295 | debug('startDetection {site=' + this._canonicalSiteURL + ',content=' |
michael@0 | 296 | + this._canonicalSiteExpectedContent + ',time=' + this._maxWaitingTime + '}'); |
michael@0 | 297 | let self = this; |
michael@0 | 298 | |
michael@0 | 299 | let urlFetcher = new URLFetcher(this._canonicalSiteURL, this._maxWaitingTime); |
michael@0 | 300 | |
michael@0 | 301 | let mayRetry = this._mayRetry.bind(this); |
michael@0 | 302 | |
michael@0 | 303 | urlFetcher.ontimeout = mayRetry; |
michael@0 | 304 | urlFetcher.onerror = mayRetry; |
michael@0 | 305 | urlFetcher.onsuccess = function (content) { |
michael@0 | 306 | if (self.validateContent(content)) { |
michael@0 | 307 | self.executeCallback(true); |
michael@0 | 308 | } else { |
michael@0 | 309 | // Content of the canonical website has been overwrite |
michael@0 | 310 | self._startLogin(); |
michael@0 | 311 | } |
michael@0 | 312 | }; |
michael@0 | 313 | urlFetcher.onredirectorerror = function (status) { |
michael@0 | 314 | if (status >= 300 && status <= 399) { |
michael@0 | 315 | // The canonical website has been redirected to an unknown location |
michael@0 | 316 | self._startLogin(); |
michael@0 | 317 | } else { |
michael@0 | 318 | mayRetry(); |
michael@0 | 319 | } |
michael@0 | 320 | }; |
michael@0 | 321 | |
michael@0 | 322 | this._runningRequest['urlFetcher'] = urlFetcher; |
michael@0 | 323 | }, |
michael@0 | 324 | |
michael@0 | 325 | _startLogin: function _startLogin() { |
michael@0 | 326 | let id = this._allocateRequestId(); |
michael@0 | 327 | let details = { |
michael@0 | 328 | type: kOpenCaptivePortalLoginEvent, |
michael@0 | 329 | id: id, |
michael@0 | 330 | url: this._canonicalSiteURL, |
michael@0 | 331 | }; |
michael@0 | 332 | this._loginObserver.attach(); |
michael@0 | 333 | this._runningRequest['eventId'] = id; |
michael@0 | 334 | this._sendEvent(kOpenCaptivePortalLoginEvent, details); |
michael@0 | 335 | }, |
michael@0 | 336 | |
michael@0 | 337 | _mayRetry: function _mayRetry() { |
michael@0 | 338 | if (this._runningRequest.retryCount++ < this._maxRetryCount) { |
michael@0 | 339 | debug('retry-Detection: ' + this._runningRequest.retryCount + '/' + this._maxRetryCount); |
michael@0 | 340 | this._startDetection(); |
michael@0 | 341 | } else { |
michael@0 | 342 | this.executeCallback(true); |
michael@0 | 343 | } |
michael@0 | 344 | }, |
michael@0 | 345 | |
michael@0 | 346 | executeCallback: function executeCallback(success) { |
michael@0 | 347 | if (this._runningRequest) { |
michael@0 | 348 | debug('callback executed'); |
michael@0 | 349 | if (this._runningRequest.hasOwnProperty('callback')) { |
michael@0 | 350 | this._runningRequest.callback.complete(success); |
michael@0 | 351 | } |
michael@0 | 352 | |
michael@0 | 353 | // Continue the following request |
michael@0 | 354 | this._runningRequest['complete'] = true; |
michael@0 | 355 | this._removeRequest(this._runningRequest.interfaceName); |
michael@0 | 356 | } |
michael@0 | 357 | }, |
michael@0 | 358 | |
michael@0 | 359 | _sendEvent: function _sendEvent(topic, details) { |
michael@0 | 360 | debug('sendEvent "' + JSON.stringify(details) + '"'); |
michael@0 | 361 | Services.obs.notifyObservers(this, |
michael@0 | 362 | topic, |
michael@0 | 363 | JSON.stringify(details)); |
michael@0 | 364 | }, |
michael@0 | 365 | |
michael@0 | 366 | validateContent: function validateContent(content) { |
michael@0 | 367 | debug('received content: ' + content); |
michael@0 | 368 | return (content === this._canonicalSiteExpectedContent); |
michael@0 | 369 | }, |
michael@0 | 370 | |
michael@0 | 371 | _allocateRequestId: function _allocateRequestId() { |
michael@0 | 372 | let newId = this._nextRequestId++; |
michael@0 | 373 | return newId.toString(); |
michael@0 | 374 | }, |
michael@0 | 375 | |
michael@0 | 376 | _runNextRequest: function _runNextRequest() { |
michael@0 | 377 | let nextRequest = this._requestQueue.shift(); |
michael@0 | 378 | if (nextRequest) { |
michael@0 | 379 | this._runningRequest = nextRequest; |
michael@0 | 380 | this._applyDetection(); |
michael@0 | 381 | } |
michael@0 | 382 | }, |
michael@0 | 383 | |
michael@0 | 384 | _addRequest: function _addRequest(request) { |
michael@0 | 385 | this._interfaceNames[request.interfaceName] = true; |
michael@0 | 386 | this._requestQueue.push(request); |
michael@0 | 387 | if (!this._runningRequest) { |
michael@0 | 388 | this._runNextRequest(); |
michael@0 | 389 | } |
michael@0 | 390 | }, |
michael@0 | 391 | |
michael@0 | 392 | _removeRequest: function _removeRequest(aInterfaceName) { |
michael@0 | 393 | if (!this._interfaceNames[aInterfaceName]) { |
michael@0 | 394 | return; |
michael@0 | 395 | } |
michael@0 | 396 | |
michael@0 | 397 | delete this._interfaceNames[aInterfaceName]; |
michael@0 | 398 | |
michael@0 | 399 | if (this._runningRequest |
michael@0 | 400 | && this._runningRequest.interfaceName === aInterfaceName) { |
michael@0 | 401 | this._loginObserver.detach(); |
michael@0 | 402 | |
michael@0 | 403 | if (!this._runningRequest.complete) { |
michael@0 | 404 | // Abort the user login procedure |
michael@0 | 405 | if (this._runningRequest.hasOwnProperty('eventId')) { |
michael@0 | 406 | let details = { |
michael@0 | 407 | type: kAbortCaptivePortalLoginEvent, |
michael@0 | 408 | id: this._runningRequest.eventId |
michael@0 | 409 | }; |
michael@0 | 410 | this._sendEvent(kAbortCaptivePortalLoginEvent, details); |
michael@0 | 411 | } |
michael@0 | 412 | |
michael@0 | 413 | // Abort the ongoing HTTP request |
michael@0 | 414 | if (this._runningRequest.hasOwnProperty('urlFetcher')) { |
michael@0 | 415 | this._runningRequest.urlFetcher.abort(); |
michael@0 | 416 | } |
michael@0 | 417 | } |
michael@0 | 418 | |
michael@0 | 419 | debug('remove running request'); |
michael@0 | 420 | this._runningRequest = null; |
michael@0 | 421 | |
michael@0 | 422 | // Continue next pending reqeust if the ongoing one has been aborted |
michael@0 | 423 | this._runNextRequest(); |
michael@0 | 424 | return; |
michael@0 | 425 | } |
michael@0 | 426 | |
michael@0 | 427 | // Check if a pending request has been aborted |
michael@0 | 428 | for (let i = 0; i < this._requestQueue.length; i++) { |
michael@0 | 429 | if (this._requestQueue[i].interfaceName == aInterfaceName) { |
michael@0 | 430 | this._requestQueue.splice(i, 1); |
michael@0 | 431 | |
michael@0 | 432 | debug('remove pending request #' + i + ', remaining ' + this._requestQueue.length); |
michael@0 | 433 | break; |
michael@0 | 434 | } |
michael@0 | 435 | } |
michael@0 | 436 | }, |
michael@0 | 437 | }; |
michael@0 | 438 | |
michael@0 | 439 | let debug; |
michael@0 | 440 | if (DEBUG) { |
michael@0 | 441 | debug = function (s) { |
michael@0 | 442 | dump('-*- CaptivePortalDetector component: ' + s + '\n'); |
michael@0 | 443 | }; |
michael@0 | 444 | } else { |
michael@0 | 445 | debug = function (s) {}; |
michael@0 | 446 | } |
michael@0 | 447 | |
michael@0 | 448 | this.NSGetFactory = XPCOMUtils.generateNSGetFactory([CaptivePortalDetector]); |