Bug 1526591 - Remove devtools.inspector.shapesHighlighter.enabled pref. r=rcaliman
[gecko.git] / netwerk / base / CaptivePortalService.cpp
blob555e134136d23e7bb8c0497041bd4460c93e0ce8
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "mozilla/net/CaptivePortalService.h"
6 #include "mozilla/ClearOnShutdown.h"
7 #include "mozilla/Services.h"
8 #include "mozilla/Preferences.h"
9 #include "nsIObserverService.h"
10 #include "nsServiceManagerUtils.h"
11 #include "nsXULAppAPI.h"
12 #include "xpcpublic.h"
14 static NS_NAMED_LITERAL_STRING(kInterfaceName, u"captive-portal-inteface");
16 static const char kOpenCaptivePortalLoginEvent[] = "captive-portal-login";
17 static const char kAbortCaptivePortalLoginEvent[] =
18 "captive-portal-login-abort";
19 static const char kCaptivePortalLoginSuccessEvent[] =
20 "captive-portal-login-success";
22 static const uint32_t kDefaultInterval = 60 * 1000; // check every 60 seconds
24 namespace mozilla {
25 namespace net {
27 static LazyLogModule gCaptivePortalLog("CaptivePortalService");
28 #undef LOG
29 #define LOG(args) MOZ_LOG(gCaptivePortalLog, mozilla::LogLevel::Debug, args)
31 NS_IMPL_ISUPPORTS(CaptivePortalService, nsICaptivePortalService, nsIObserver,
32 nsISupportsWeakReference, nsITimerCallback,
33 nsICaptivePortalCallback, nsINamed)
35 static StaticRefPtr<CaptivePortalService> gCPService;
37 // static
38 already_AddRefed<nsICaptivePortalService> CaptivePortalService::GetSingleton() {
39 if (gCPService) {
40 return do_AddRef(gCPService);
43 gCPService = new CaptivePortalService();
44 ClearOnShutdown(&gCPService);
45 return do_AddRef(gCPService);
48 CaptivePortalService::CaptivePortalService()
49 : mState(UNKNOWN),
50 mStarted(false),
51 mInitialized(false),
52 mRequestInProgress(false),
53 mEverBeenCaptive(false),
54 mDelay(kDefaultInterval),
55 mSlackCount(0),
56 mMinInterval(kDefaultInterval),
57 mMaxInterval(25 * kDefaultInterval),
58 mBackoffFactor(5.0) {
59 mLastChecked = TimeStamp::Now();
62 CaptivePortalService::~CaptivePortalService() {
63 LOG(("CaptivePortalService::~CaptivePortalService isParentProcess:%d\n",
64 XRE_GetProcessType() == GeckoProcessType_Default));
67 nsresult CaptivePortalService::PerformCheck() {
68 LOG(
69 ("CaptivePortalService::PerformCheck mRequestInProgress:%d "
70 "mInitialized:%d mStarted:%d\n",
71 mRequestInProgress, mInitialized, mStarted));
72 // Don't issue another request if last one didn't complete
73 if (mRequestInProgress || !mInitialized || !mStarted) {
74 return NS_OK;
76 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
77 nsresult rv;
78 if (!mCaptivePortalDetector) {
79 mCaptivePortalDetector =
80 do_GetService("@mozilla.org/toolkit/captive-detector;1", &rv);
81 if (NS_FAILED(rv)) {
82 LOG(("Unable to get a captive portal detector\n"));
83 return rv;
87 LOG(("CaptivePortalService::PerformCheck - Calling CheckCaptivePortal\n"));
88 mRequestInProgress = true;
89 mCaptivePortalDetector->CheckCaptivePortal(kInterfaceName, this);
90 return NS_OK;
93 nsresult CaptivePortalService::RearmTimer() {
94 LOG(("CaptivePortalService::RearmTimer\n"));
95 // Start a timer to recheck
96 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
97 if (mTimer) {
98 mTimer->Cancel();
101 // If we have successfully determined the state, and we have never detected
102 // a captive portal, we don't need to keep polling, but will rely on events
103 // to trigger detection.
104 if (mState == NOT_CAPTIVE) {
105 return NS_OK;
108 if (!mTimer) {
109 mTimer = NS_NewTimer();
112 if (mTimer && mDelay > 0) {
113 LOG(("CaptivePortalService - Reloading timer with delay %u\n", mDelay));
114 return mTimer->InitWithCallback(this, mDelay, nsITimer::TYPE_ONE_SHOT);
117 return NS_OK;
120 nsresult CaptivePortalService::Initialize() {
121 if (mInitialized) {
122 return NS_OK;
124 mInitialized = true;
126 // Only the main process service should actually do anything. The service in
127 // the content process only mirrors the CP state in the main process.
128 if (XRE_GetProcessType() != GeckoProcessType_Default) {
129 return NS_OK;
132 nsCOMPtr<nsIObserverService> observerService =
133 mozilla::services::GetObserverService();
134 if (observerService) {
135 observerService->AddObserver(this, kOpenCaptivePortalLoginEvent, true);
136 observerService->AddObserver(this, kAbortCaptivePortalLoginEvent, true);
137 observerService->AddObserver(this, kCaptivePortalLoginSuccessEvent, true);
140 LOG(("Initialized CaptivePortalService\n"));
141 return NS_OK;
144 nsresult CaptivePortalService::Start() {
145 if (!mInitialized) {
146 return NS_ERROR_NOT_INITIALIZED;
149 if (xpc::AreNonLocalConnectionsDisabled() &&
150 !Preferences::GetBool("network.captive-portal-service.testMode", false)) {
151 return NS_ERROR_NOT_AVAILABLE;
154 if (XRE_GetProcessType() != GeckoProcessType_Default) {
155 // Doesn't do anything if called in the content process.
156 return NS_OK;
159 if (mStarted) {
160 return NS_OK;
163 MOZ_ASSERT(mState == UNKNOWN, "Initial state should be UNKNOWN");
164 mStarted = true;
165 mEverBeenCaptive = false;
167 // Get the delay prefs
168 Preferences::GetUint("network.captive-portal-service.minInterval",
169 &mMinInterval);
170 Preferences::GetUint("network.captive-portal-service.maxInterval",
171 &mMaxInterval);
172 Preferences::GetFloat("network.captive-portal-service.backoffFactor",
173 &mBackoffFactor);
175 LOG(("CaptivePortalService::Start min:%u max:%u backoff:%.2f\n", mMinInterval,
176 mMaxInterval, mBackoffFactor));
178 mSlackCount = 0;
179 mDelay = mMinInterval;
181 // When Start is called, perform a check immediately
182 PerformCheck();
183 RearmTimer();
184 return NS_OK;
187 nsresult CaptivePortalService::Stop() {
188 LOG(("CaptivePortalService::Stop\n"));
190 if (XRE_GetProcessType() != GeckoProcessType_Default) {
191 // Doesn't do anything when called in the content process.
192 return NS_OK;
195 if (!mStarted) {
196 return NS_OK;
199 if (mTimer) {
200 mTimer->Cancel();
202 mTimer = nullptr;
203 mRequestInProgress = false;
204 mStarted = false;
205 if (mCaptivePortalDetector) {
206 mCaptivePortalDetector->Abort(kInterfaceName);
208 mCaptivePortalDetector = nullptr;
210 // Clear the state in case anyone queries the state while detection is off.
211 mState = UNKNOWN;
212 return NS_OK;
215 void CaptivePortalService::SetStateInChild(int32_t aState) {
216 // This should only be called in the content process, from ContentChild.cpp
217 // in order to mirror the captive portal state set in the chrome process.
218 MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Default);
220 mState = aState;
221 mLastChecked = TimeStamp::Now();
224 //-----------------------------------------------------------------------------
225 // CaptivePortalService::nsICaptivePortalService
226 //-----------------------------------------------------------------------------
228 NS_IMETHODIMP
229 CaptivePortalService::GetState(int32_t *aState) {
230 *aState = mState;
231 return NS_OK;
234 NS_IMETHODIMP
235 CaptivePortalService::RecheckCaptivePortal() {
236 LOG(("CaptivePortalService::RecheckCaptivePortal\n"));
238 if (XRE_GetProcessType() != GeckoProcessType_Default) {
239 // Doesn't do anything if called in the content process.
240 return NS_OK;
243 // This is called for user activity. We need to reset the slack count,
244 // so the checks continue to be quite frequent.
245 mSlackCount = 0;
246 mDelay = mMinInterval;
248 PerformCheck();
249 RearmTimer();
250 return NS_OK;
253 NS_IMETHODIMP
254 CaptivePortalService::GetLastChecked(uint64_t *aLastChecked) {
255 double duration = (TimeStamp::Now() - mLastChecked).ToMilliseconds();
256 *aLastChecked = static_cast<uint64_t>(duration);
257 return NS_OK;
260 //-----------------------------------------------------------------------------
261 // CaptivePortalService::nsITimer
262 // This callback gets called every mDelay miliseconds
263 // It issues a checkCaptivePortal operation if one isn't already in progress
264 //-----------------------------------------------------------------------------
265 NS_IMETHODIMP
266 CaptivePortalService::Notify(nsITimer *aTimer) {
267 LOG(("CaptivePortalService::Notify\n"));
268 MOZ_ASSERT(aTimer == mTimer);
269 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
271 PerformCheck();
273 // This is needed because we don't want to always make requests very often.
274 // Every 10 checks, we the delay is increased mBackoffFactor times
275 // to a maximum delay of mMaxInterval
276 mSlackCount++;
277 if (mSlackCount % 10 == 0) {
278 mDelay = mDelay * mBackoffFactor;
280 if (mDelay > mMaxInterval) {
281 mDelay = mMaxInterval;
284 // Note - if mDelay is 0, the timer will not be rearmed.
285 RearmTimer();
287 return NS_OK;
290 //-----------------------------------------------------------------------------
291 // CaptivePortalService::nsINamed
292 //-----------------------------------------------------------------------------
294 NS_IMETHODIMP
295 CaptivePortalService::GetName(nsACString &aName) {
296 aName.AssignLiteral("CaptivePortalService");
297 return NS_OK;
300 //-----------------------------------------------------------------------------
301 // CaptivePortalService::nsIObserver
302 //-----------------------------------------------------------------------------
303 NS_IMETHODIMP
304 CaptivePortalService::Observe(nsISupports *aSubject, const char *aTopic,
305 const char16_t *aData) {
306 if (XRE_GetProcessType() != GeckoProcessType_Default) {
307 // Doesn't do anything if called in the content process.
308 return NS_OK;
311 LOG(("CaptivePortalService::Observe() topic=%s\n", aTopic));
312 if (!strcmp(aTopic, kOpenCaptivePortalLoginEvent)) {
313 // A redirect or altered content has been detected.
314 // The user needs to log in. We are in a captive portal.
315 mState = LOCKED_PORTAL;
316 mLastChecked = TimeStamp::Now();
317 mEverBeenCaptive = true;
318 } else if (!strcmp(aTopic, kCaptivePortalLoginSuccessEvent)) {
319 // The user has successfully logged in. We have connectivity.
320 mState = UNLOCKED_PORTAL;
321 mLastChecked = TimeStamp::Now();
322 mSlackCount = 0;
323 mDelay = mMinInterval;
325 RearmTimer();
326 } else if (!strcmp(aTopic, kAbortCaptivePortalLoginEvent)) {
327 // The login has been aborted
328 mState = UNKNOWN;
329 mLastChecked = TimeStamp::Now();
330 mSlackCount = 0;
333 // Send notification so that the captive portal state is mirrored in the
334 // content process.
335 nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
336 if (observerService) {
337 nsCOMPtr<nsICaptivePortalService> cps(this);
338 observerService->NotifyObservers(cps, NS_IPC_CAPTIVE_PORTAL_SET_STATE,
339 nullptr);
342 return NS_OK;
345 void CaptivePortalService::NotifyConnectivityAvailable(bool aCaptive) {
346 nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
347 if (observerService) {
348 nsCOMPtr<nsICaptivePortalService> cps(this);
349 observerService->NotifyObservers(cps, NS_CAPTIVE_PORTAL_CONNECTIVITY,
350 aCaptive ? u"captive" : u"clear");
354 //-----------------------------------------------------------------------------
355 // CaptivePortalService::nsICaptivePortalCallback
356 //-----------------------------------------------------------------------------
357 NS_IMETHODIMP
358 CaptivePortalService::Prepare() {
359 LOG(("CaptivePortalService::Prepare\n"));
360 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
361 // XXX: Finish preparation shouldn't be called until dns and routing is
362 // available.
363 if (mCaptivePortalDetector) {
364 mCaptivePortalDetector->FinishPreparation(kInterfaceName);
366 return NS_OK;
369 NS_IMETHODIMP
370 CaptivePortalService::Complete(bool success) {
371 LOG(("CaptivePortalService::Complete(success=%d) mState=%d\n", success,
372 mState));
373 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
374 mLastChecked = TimeStamp::Now();
376 // Note: this callback gets called when:
377 // 1. the request is completed, and content is valid (success == true)
378 // 2. when the request is aborted or times out (success == false)
380 if (success) {
381 if (mEverBeenCaptive) {
382 mState = UNLOCKED_PORTAL;
383 NotifyConnectivityAvailable(true);
384 } else {
385 mState = NOT_CAPTIVE;
386 NotifyConnectivityAvailable(false);
390 mRequestInProgress = false;
391 return NS_OK;
394 } // namespace net
395 } // namespace mozilla