mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 07:13:20 +00:00
Bug 752982 - Part 1, implement captive portal detection service. r=thinker,dolske rs=dolske a=shira.
This commit is contained in:
parent
61884a045e
commit
011a0868cd
@ -286,6 +286,9 @@
|
||||
@BINPATH@/components/services-crypto.xpt
|
||||
#endif
|
||||
@BINPATH@/components/services-crypto-component.xpt
|
||||
#ifdef MOZ_CAPTIVEDETECT
|
||||
@BINPATH@/components/captivedetect.xpt
|
||||
#endif
|
||||
@BINPATH@/components/shellservice.xpt
|
||||
@BINPATH@/components/shistory.xpt
|
||||
@BINPATH@/components/spellchecker.xpt
|
||||
@ -492,6 +495,10 @@
|
||||
@BINPATH@/components/HealthReportComponents.manifest
|
||||
@BINPATH@/components/HealthReportService.js
|
||||
#endif
|
||||
#ifdef MOZ_CAPTIVEDETECT
|
||||
@BINPATH@/components/CaptivePortalDetectComponents.manifest
|
||||
@BINPATH@/components/captivedetect.js
|
||||
#endif
|
||||
@BINPATH@/components/TelemetryPing.js
|
||||
@BINPATH@/components/TelemetryPing.manifest
|
||||
@BINPATH@/components/Webapps.js
|
||||
|
@ -285,6 +285,9 @@
|
||||
@BINPATH@/components/saxparser.xpt
|
||||
@BINPATH@/browser/components/sessionstore.xpt
|
||||
@BINPATH@/components/services-crypto-component.xpt
|
||||
#ifdef MOZ_CAPTIVEDETECT
|
||||
@BINPATH@/components/captivedetect.xpt
|
||||
#endif
|
||||
@BINPATH@/browser/components/shellservice.xpt
|
||||
@BINPATH@/components/shistory.xpt
|
||||
@BINPATH@/components/spellchecker.xpt
|
||||
@ -483,6 +486,10 @@
|
||||
@BINPATH@/components/SyncComponents.manifest
|
||||
@BINPATH@/components/Weave.js
|
||||
#endif
|
||||
#ifdef MOZ_CAPTIVEDETECT
|
||||
@BINPATH@/components/CaptivePortalDetectComponents.manifest
|
||||
@BINPATH@/components/captivedetect.js
|
||||
#endif
|
||||
@BINPATH@/components/servicesComponents.manifest
|
||||
@BINPATH@/components/cryptoComponents.manifest
|
||||
@BINPATH@/components/TelemetryPing.js
|
||||
|
@ -8378,6 +8378,12 @@ if test -n "$MOZ_SERVICES_SYNC"; then
|
||||
AC_DEFINE(MOZ_SERVICES_SYNC)
|
||||
fi
|
||||
|
||||
dnl Build Captive Portal Detector if required
|
||||
AC_SUBST(MOZ_CAPTIVEDETECT)
|
||||
if test -n "$MOZ_CAPTIVEDETECT"; then
|
||||
AC_DEFINE(MOZ_CAPTIVEDETECT)
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
if test "$MOZ_DEBUG" -o "$NS_TRACE_MALLOC" -o "$MOZ_DMD"; then
|
||||
MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS=
|
||||
|
@ -210,6 +210,9 @@
|
||||
@BINPATH@/components/saxparser.xpt
|
||||
@BINPATH@/components/sessionstore.xpt
|
||||
@BINPATH@/components/services-crypto-component.xpt
|
||||
#ifdef MOZ_CAPTIVEDETECT
|
||||
@BINPATH@/components/captivedetect.xpt
|
||||
#endif
|
||||
@BINPATH@/components/shellservice.xpt
|
||||
@BINPATH@/components/shistory.xpt
|
||||
@BINPATH@/components/spellchecker.xpt
|
||||
@ -377,6 +380,11 @@
|
||||
@BINPATH@/components/HealthReportService.js
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_CAPTIVEDETECT
|
||||
@BINPATH@/components/CaptivePortalDetectComponents.manifest
|
||||
@BINPATH@/components/captivedetect.js
|
||||
#endif
|
||||
|
||||
; Modules
|
||||
@BINPATH@/modules/*
|
||||
|
||||
|
@ -4112,3 +4112,9 @@ pref("ui.touch_activation.delay_ms", 100);
|
||||
// nsMemoryInfoDumper can watch a fifo in the temp directory and take various
|
||||
// actions when the fifo is written to. Disable this in general.
|
||||
pref("memory_info_dumper.watch_fifo", false);
|
||||
|
||||
#ifdef MOZ_CAPTIVEDETECT
|
||||
pref("captivedetect.maxWaitingTime", 5000);
|
||||
pref("captivedetect.pollingTime", 3000);
|
||||
pref("captivedetect.maxRetryCount", 5);
|
||||
#endif
|
@ -85,6 +85,10 @@ ifdef MOZ_URL_CLASSIFIER
|
||||
PARALLEL_DIRS += url-classifier
|
||||
endif
|
||||
|
||||
ifdef MOZ_CAPTIVEDETECT
|
||||
PARALLEL_DIRS += captivedetect
|
||||
endif
|
||||
|
||||
DIRS += \
|
||||
build \
|
||||
$(NULL)
|
||||
|
@ -0,0 +1,2 @@
|
||||
component {d9cd00ba-aa4d-47b1-8792-b1fe0cd35060} captivedetect.js
|
||||
contract @mozilla.org/toolkit/captive-detector;1 {d9cd00ba-aa4d-47b1-8792-b1fe0cd35060}
|
23
toolkit/components/captivedetect/Makefile.in
Normal file
23
toolkit/components/captivedetect/Makefile.in
Normal file
@ -0,0 +1,23 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DEPTH = @DEPTH@
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
MODULE = captivedetect
|
||||
|
||||
XPIDLSRCS = \
|
||||
nsICaptivePortalDetector.idl \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_COMPONENTS = \
|
||||
CaptivePortalDetectComponents.manifest \
|
||||
captivedetect.js \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
441
toolkit/components/captivedetect/captivedetect.js
Normal file
441
toolkit/components/captivedetect/captivedetect.js
Normal file
@ -0,0 +1,441 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
'use strict';
|
||||
|
||||
const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
|
||||
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
Cu.import('resource://gre/modules/Services.jsm');
|
||||
|
||||
const DEBUG = false; // set to true to show debug messages
|
||||
|
||||
const kCAPTIVEPORTALDETECTOR_CONTRACTID = '@mozilla.org/toolkit/captive-detector;1';
|
||||
const kCAPTIVEPORTALDETECTOR_CID = Components.ID('{d9cd00ba-aa4d-47b1-8792-b1fe0cd35060}');
|
||||
|
||||
const kOpenCaptivePortalLoginEvent = 'captive-portal-login';
|
||||
const kAbortCaptivePortalLoginEvent = 'captive-portal-login-abort';
|
||||
|
||||
function URLFetcher(url, timeout) {
|
||||
let self = this;
|
||||
let xhr = Cc['@mozilla.org/xmlextras/xmlhttprequest;1']
|
||||
.createInstance(Ci.nsIXMLHttpRequest);
|
||||
xhr.open('GET', url, true);
|
||||
// Prevent the request from reading from the cache.
|
||||
xhr.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
|
||||
// Prevent the request from writing to the cache.
|
||||
xhr.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
|
||||
// Prevent privacy leaks
|
||||
xhr.channel.loadFlags |= Ci.nsIRequest.LOAD_ANONYMOUS;
|
||||
// The Cache-Control header is only interpreted by proxies and the
|
||||
// final destination. It does not help if a resource is already
|
||||
// cached locally.
|
||||
xhr.setRequestHeader("Cache-Control", "no-cache");
|
||||
// HTTP/1.0 servers might not implement Cache-Control and
|
||||
// might only implement Pragma: no-cache
|
||||
xhr.setRequestHeader("Pragma", "no-cache");
|
||||
|
||||
xhr.timeout = timeout;
|
||||
xhr.ontimeout = function () { self.ontimeout(); };
|
||||
xhr.onerror = function () { self.onerror(); };
|
||||
xhr.onreadystatechange = function(oEvent) {
|
||||
if (xhr.readyState === 4) {
|
||||
if (self._isAborted) {
|
||||
return;
|
||||
}
|
||||
if (xhr.status === 200) {
|
||||
self.onsuccess(xhr.responseText);
|
||||
} else if (xhr.status) {
|
||||
self.onredirectorerror(xhr.status);
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.send();
|
||||
this._xhr = xhr;
|
||||
}
|
||||
|
||||
URLFetcher.prototype = {
|
||||
_isAborted: false,
|
||||
ontimeout: function() {},
|
||||
onerror: function() {},
|
||||
abort: function() {
|
||||
if (!this._isAborted) {
|
||||
this._isAborted = true;
|
||||
this._xhr.abort();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
function LoginObserver(captivePortalDetector) {
|
||||
const LOGIN_OBSERVER_STATE_DETACHED = 0; /* Should not monitor network activity since no ongoing login procedure */
|
||||
const LOGIN_OBSERVER_STATE_IDLE = 1; /* No network activity currently, waiting for a longer enough idle period */
|
||||
const LOGIN_OBSERVER_STATE_BURST = 2; /* Network activity is detected, probably caused by a login procedure */
|
||||
const LOGIN_OBSERVER_STATE_VERIFY_NEEDED = 3; /* Verifing network accessiblity is required after a long enough idle */
|
||||
const LOGIN_OBSERVER_STATE_VERIFYING = 4; /* LoginObserver is probing if public network is available */
|
||||
|
||||
let state = LOGIN_OBSERVER_STATE_DETACHED;
|
||||
|
||||
let timer = Cc['@mozilla.org/timer;1'].createInstance(Ci.nsITimer);
|
||||
let activityDistributor = Cc['@mozilla.org/network/http-activity-distributor;1']
|
||||
.getService(Ci.nsIHttpActivityDistributor);
|
||||
let urlFetcher = null;
|
||||
|
||||
let pageCheckingDone = function pageCheckingDone() {
|
||||
if (state === LOGIN_OBSERVER_STATE_VERIFYING) {
|
||||
urlFetcher = null;
|
||||
// Finish polling the canonical site, switch back to idle state and
|
||||
// waiting for next burst
|
||||
state = LOGIN_OBSERVER_STATE_IDLE;
|
||||
timer.initWithCallback(observer,
|
||||
captivePortalDetector._pollingTime,
|
||||
timer.TYPE_ONE_SHOT);
|
||||
}
|
||||
};
|
||||
|
||||
let checkPageContent = function checkPageContent() {
|
||||
debug("checking if public network is available after the login procedure");
|
||||
|
||||
urlFetcher = new URLFetcher(captivePortalDetector._canonicalSiteURL,
|
||||
captivePortalDetector._maxWaitingTime);
|
||||
urlFetcher.ontimeout = pageCheckingDone;
|
||||
urlFetcher.onerror = pageCheckingDone;
|
||||
urlFetcher.onsuccess = function (content) {
|
||||
if (captivePortalDetector.validateContent(content)) {
|
||||
urlFetcher = null;
|
||||
captivePortalDetector.executeCallback(true);
|
||||
} else {
|
||||
pageCheckingDone();
|
||||
}
|
||||
};
|
||||
urlFetcher.onredirectorerror = pageCheckingDone;
|
||||
};
|
||||
|
||||
// Public interface of LoginObserver
|
||||
let observer = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIHttpActivityOberver,
|
||||
Ci.nsITimerCallback]),
|
||||
|
||||
attach: function attach() {
|
||||
if (state === LOGIN_OBSERVER_STATE_DETACHED) {
|
||||
activityDistributor.addObserver(this);
|
||||
state = LOGIN_OBSERVER_STATE_IDLE;
|
||||
timer.initWithCallback(this,
|
||||
captivePortalDetector._pollingTime,
|
||||
timer.TYPE_ONE_SHOT);
|
||||
debug('attach HttpObserver for login activity');
|
||||
}
|
||||
},
|
||||
|
||||
detach: function detach() {
|
||||
if (state !== LOGIN_OBSERVER_STATE_DETACHED) {
|
||||
if (urlFetcher) {
|
||||
urlFetcher.abort();
|
||||
urlFetcher = null;
|
||||
}
|
||||
activityDistributor.removeObserver(this);
|
||||
timer.cancel();
|
||||
state = LOGIN_OBSERVER_STATE_DETACHED;
|
||||
debug('detach HttpObserver for login activity');
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Treat all HTTP transactions as captive portal login activities.
|
||||
*/
|
||||
observeActivity: function observeActivity(aHttpChannel, aActivityType,
|
||||
aActivitySubtype, aTimestamp,
|
||||
aExtraSizeData, aExtraStringData) {
|
||||
if (aActivityType === Ci.nsIHttpActivityObserver.ACTIVITY_TYPE_HTTP_TRANSACTION
|
||||
&& aActivitySubtype === Ci.nsIHttpActivityObserver.ACTIVITY_SUBTYPE_RESPONSE_COMPLETE) {
|
||||
switch (state) {
|
||||
case LOGIN_OBSERVER_STATE_IDLE:
|
||||
case LOGIN_OBSERVER_STATE_VERIFY_NEEDED:
|
||||
state = LOGIN_OBSERVER_STATE_BURST;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Check if login activity is finished according to HTTP burst.
|
||||
*/
|
||||
notify : function notify() {
|
||||
switch(state) {
|
||||
case LOGIN_OBSERVER_STATE_BURST:
|
||||
// Wait while network stays idle for a short period
|
||||
state = LOGIN_OBSERVER_STATE_VERIFY_NEEDED;
|
||||
// Fall though to start polling timer
|
||||
case LOGIN_OBSERVER_STATE_IDLE:
|
||||
timer.initWithCallback(this,
|
||||
captivePortalDetector._pollingTime,
|
||||
timer.TYPE_ONE_SHOT);
|
||||
break;
|
||||
case LOGIN_OBSERVER_STATE_VERIFY_NEEDED:
|
||||
// Polling the canonical website since network stays idle for a while
|
||||
state = LOGIN_OBSERVER_STATE_VERIFYING;
|
||||
checkPageContent();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
return observer;
|
||||
}
|
||||
|
||||
function CaptivePortalDetector() {
|
||||
// Load preference
|
||||
this._canonicalSiteURL = null;
|
||||
this._canonicalSiteExpectedContent = null;
|
||||
|
||||
try {
|
||||
this._canonicalSiteURL =
|
||||
Services.prefs.getCharPref('captivedetect.canonicalURL');
|
||||
this._canonicalSiteExpectedContent =
|
||||
Services.prefs.getCharPref('captivedetect.canonicalContent');
|
||||
} catch(e) {
|
||||
debug('canonicalURL or canonicalContent not set.')
|
||||
}
|
||||
|
||||
this._maxWaitingTime =
|
||||
Services.prefs.getIntPref('captivedetect.maxWaitingTime');
|
||||
this._pollingTime =
|
||||
Services.prefs.getIntPref('captivedetect.pollingTime');
|
||||
this._maxRetryCount =
|
||||
Services.prefs.getIntPref('captivedetect.maxRetryCount');
|
||||
debug('Load Prefs {site=' + this._canonicalSiteURL + ',content='
|
||||
+ this._canonicalSiteExpectedContent + ',time=' + this._maxWaitingTime
|
||||
+ "max-retry=" + this._maxRetryCount + '}');
|
||||
|
||||
// Create HttpObserver for monitoring the login procedure
|
||||
this._loginObserver = LoginObserver(this);
|
||||
|
||||
this._nextRequestId = 0;
|
||||
this._runningRequest = null;
|
||||
this._requestQueue = []; // Maintain a progress table, store callbacks and the ongoing XHR
|
||||
this._interfaceNames = {}; // Maintain names of the requested network interfaces
|
||||
|
||||
debug('CaptiveProtalDetector initiated, waitng for network connection established');
|
||||
}
|
||||
|
||||
CaptivePortalDetector.prototype = {
|
||||
classID: kCAPTIVEPORTALDETECTOR_CID,
|
||||
classInfo: XPCOMUtils.generateCI({classID: kCAPTIVEPORTALDETECTOR_CID,
|
||||
contractID: kCAPTIVEPORTALDETECTOR_CONTRACTID,
|
||||
classDescription: 'Captive Portal Detector',
|
||||
interfaces: [Ci.nsICaptivePortalDetector]}),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsICaptivePortalDetector]),
|
||||
|
||||
// nsICaptivePortalDetector
|
||||
checkCaptivePortal: function checkCaptivePortal(aInterfaceName, aCallback) {
|
||||
if (!this._canonicalSiteURL) {
|
||||
throw Components.Exception('No canonical URL set up.');
|
||||
}
|
||||
|
||||
// Prevent multiple requests on a single network interface
|
||||
if (this._interfaceNames[aInterfaceName]) {
|
||||
throw Components.Exception('Do not allow multiple request on one interface: ' + aInterface);
|
||||
}
|
||||
|
||||
let request = {interfaceName: aInterfaceName};
|
||||
if (aCallback) {
|
||||
let callback = aCallback.QueryInterface(Ci.nsICaptivePortalCallback);
|
||||
request['callback'] = callback;
|
||||
request['retryCount'] = 0;
|
||||
}
|
||||
this._addRequest(request);
|
||||
},
|
||||
|
||||
abort: function abort(aInterfaceName) {
|
||||
debug('abort for ' + aInterfaceName);
|
||||
this._removeRequest(aInterfaceName);
|
||||
},
|
||||
|
||||
finishPreparation: function finishPreparation(aInterfaceName) {
|
||||
debug('finish preparation phase for interface "' + aInterfaceName + '"');
|
||||
if (!this._runningRequest
|
||||
|| this._runningRequest.interfaceName !== aInterfaceName) {
|
||||
debug('invalid finishPreparation for ' + aInterfaceName);
|
||||
throw Components.Exception('only first request is allowed to invoke |finishPreparation|');
|
||||
return;
|
||||
}
|
||||
|
||||
this._startDetection();
|
||||
},
|
||||
|
||||
cancelLogin: function cancelLogin(eventId) {
|
||||
debug('login canceled by user for request "' + eventId + '"');
|
||||
// Captive portal login procedure is canceled by user
|
||||
if (this._runningRequest && this._runningRequest.hasOwnProperty('eventId')) {
|
||||
let id = this._runningRequest.eventId;
|
||||
if (eventId === id) {
|
||||
this.executeCallback(false);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_applyDetection: function _applyDetection() {
|
||||
debug('enter applyDetection('+ this._runningRequest.interfaceName + ')');
|
||||
|
||||
// Execute network interface preparation
|
||||
if (this._runningRequest.hasOwnProperty('callback')) {
|
||||
this._runningRequest.callback.prepare();
|
||||
} else {
|
||||
this._startDetection();
|
||||
}
|
||||
},
|
||||
|
||||
_startDetection: function _startDetection() {
|
||||
debug('startDetection {site=' + this._canonicalSiteURL + ',content='
|
||||
+ this._canonicalSiteExpectedContent + ',time=' + this._maxWaitingTime + '}');
|
||||
let self = this;
|
||||
|
||||
let urlFetcher = new URLFetcher(this._canonicalSiteURL, this._maxWaitingTime);
|
||||
|
||||
let requestDone = this.executeCallback.bind(this, true);
|
||||
urlFetcher.ontimeout = requestDone;
|
||||
urlFetcher.onerror = requestDone;
|
||||
urlFetcher.onsuccess = function (content) {
|
||||
if (self.validateContent(content)) {
|
||||
requestDone();
|
||||
} else {
|
||||
// Content of the canonical website has been overwrite
|
||||
self._startLogin();
|
||||
}
|
||||
};
|
||||
urlFetcher.onredirectorerror = function (status) {
|
||||
if (status >= 300 && status <= 399) {
|
||||
// The canonical website has been redirected to an unknown location
|
||||
self._startLogin();
|
||||
} else if (self._runningRequest.retryCount++ < self._maxRetryCount) {
|
||||
debug('startDetection-retry: ' + self._runningRequest.retryCount);
|
||||
self._startDetection();
|
||||
} else {
|
||||
requestDone();
|
||||
}
|
||||
};
|
||||
|
||||
this._runningRequest['urlFetcher'] = urlFetcher;
|
||||
},
|
||||
|
||||
_startLogin: function _startLogin() {
|
||||
let id = this._allocateRequestId();
|
||||
let details = {
|
||||
type: kOpenCaptivePortalLoginEvent,
|
||||
id: id,
|
||||
url: this._canonicalSiteURL,
|
||||
};
|
||||
this._loginObserver.attach();
|
||||
this._runningRequest['eventId'] = id;
|
||||
this._sendEvent(kOpenCaptivePortalLoginEvent, details);
|
||||
},
|
||||
|
||||
executeCallback: function executeCallback(success) {
|
||||
if (this._runningRequest) {
|
||||
debug('callback executed');
|
||||
if (this._runningRequest.hasOwnProperty('callback')) {
|
||||
this._runningRequest.callback.complete(success);
|
||||
}
|
||||
|
||||
// Continue the following request
|
||||
this._runningRequest['complete'] = true;
|
||||
this._removeRequest(this._runningRequest.interfaceName);
|
||||
}
|
||||
},
|
||||
|
||||
_sendEvent: function _sendEvent(topic, details) {
|
||||
debug('sendEvent "' + JSON.stringify(details) + '"');
|
||||
Services.obs.notifyObservers(this,
|
||||
topic,
|
||||
JSON.stringify(details));
|
||||
},
|
||||
|
||||
validateContent: function validateContent(content) {
|
||||
debug('received content: ' + content);
|
||||
return (content === this._canonicalSiteExpectedContent);
|
||||
},
|
||||
|
||||
_allocateRequestId: function _allocateRequestId() {
|
||||
let newId = this._nextRequestId++;
|
||||
return newId.toString();
|
||||
},
|
||||
|
||||
_runNextRequest: function _runNextRequest() {
|
||||
let nextRequest = this._requestQueue.shift();
|
||||
if (nextRequest) {
|
||||
this._runningRequest = nextRequest;
|
||||
this._applyDetection();
|
||||
}
|
||||
},
|
||||
|
||||
_addRequest: function _addRequest(request) {
|
||||
this._interfaceNames[request.interfaceName] = true;
|
||||
this._requestQueue.push(request);
|
||||
if (!this._runningRequest) {
|
||||
this._runNextRequest();
|
||||
}
|
||||
},
|
||||
|
||||
_removeRequest: function _removeRequest(aInterfaceName) {
|
||||
if (!this._interfaceNames[aInterfaceName]) {
|
||||
return;
|
||||
}
|
||||
|
||||
delete this._interfaceNames[aInterfaceName];
|
||||
|
||||
if (this._runningRequest
|
||||
&& this._runningRequest.interfaceName === aInterfaceName) {
|
||||
this._loginObserver.detach();
|
||||
|
||||
if (!this._runningRequest.complete) {
|
||||
// Abort the user login procedure
|
||||
if (this._runningRequest.hasOwnProperty('eventId')) {
|
||||
let details = {
|
||||
type: kAbortCaptivePortalLoginEvent,
|
||||
id: this._runningRequest.eventId
|
||||
};
|
||||
this._sendEvent(kAbortCaptivePortalLoginEvent, details);
|
||||
}
|
||||
|
||||
// Abort the ongoing HTTP request
|
||||
if (this._runningRequest.hasOwnProperty('urlFetcher')) {
|
||||
this._runningRequest.urlFetcher.abort();
|
||||
}
|
||||
}
|
||||
|
||||
debug('remove running request');
|
||||
this._runningRequest = null;
|
||||
|
||||
// Continue next pending reqeust if the ongoing one has been aborted
|
||||
this._runNextRequest();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if a pending request has been aborted
|
||||
for (let i = 0; i < this._requestQueue.length; i++) {
|
||||
if (this._requestQueue[i].interfaceName == aInterfaceName) {
|
||||
this._requestQueue.splice(i, 1);
|
||||
|
||||
debug('remove pending request #' + i + ', remaining ' + this._requestQueue.length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let debug;
|
||||
if (DEBUG) {
|
||||
debug = function (s) {
|
||||
dump('-*- CaptivePortalDetector component: ' + s + '\n');
|
||||
};
|
||||
} else {
|
||||
debug = function (s) {};
|
||||
}
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([CaptivePortalDetector]);
|
@ -0,0 +1,53 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
[scriptable, uuid(593fdeec-6284-4de8-b416-8e63cbdc695e)]
|
||||
interface nsICaptivePortalCallback : nsISupports
|
||||
{
|
||||
/**
|
||||
* Preparation for network interface before captive portal detection started.
|
||||
*/
|
||||
void prepare();
|
||||
|
||||
/**
|
||||
* Invoke callbacks after captive portal detection finished.
|
||||
*/
|
||||
void complete(in bool success);
|
||||
};
|
||||
|
||||
[scriptable, uuid(2f827c5a-f551-477f-af09-71adbfbd854a)]
|
||||
interface nsICaptivePortalDetector : nsISupports
|
||||
{
|
||||
/**
|
||||
* Perform captive portal detection on specific network interface.
|
||||
* @param ifname The name of network interface, exception will be thrwon
|
||||
* if the same interface has unfinished request.
|
||||
* @param callback Callbacks when detection procedure starts and finishes.
|
||||
*/
|
||||
void checkCaptivePortal(in wstring ifname,
|
||||
in nsICaptivePortalCallback callback);
|
||||
|
||||
/**
|
||||
* Abort captive portal detection for specific network interface
|
||||
* due to system failure, callback will not be invoked.
|
||||
* @param ifname The name of network interface.
|
||||
*/
|
||||
void abort(in wstring ifname);
|
||||
|
||||
/**
|
||||
* Cancel captive portal login procedure by user, callback will be invoked.
|
||||
* @param eventId Login event id provided in |captive-portal-login| event.
|
||||
*/
|
||||
void cancelLogin(in wstring eventId);
|
||||
|
||||
/**
|
||||
* Notify prepare phase is finished, routing and dns must be ready for sending
|
||||
* out XMLHttpRequest. this is callback for CaptivePortalDetector API user.
|
||||
* @param ifname The name of network interface, must be unique.
|
||||
*/
|
||||
void finishPreparation(in wstring ifname);
|
||||
};
|
@ -459,6 +459,7 @@ MAKEFILES_xulapp="
|
||||
toolkit/components/apppicker/Makefile
|
||||
toolkit/components/Makefile
|
||||
toolkit/components/build/Makefile
|
||||
toolkit/components/captivedetect/Makefile
|
||||
toolkit/components/commandlines/Makefile
|
||||
toolkit/components/console/Makefile
|
||||
toolkit/components/contentprefs/Makefile
|
||||
|
Loading…
Reference in New Issue
Block a user