LCOV - code coverage report
Current view: top level - netwerk/base - CaptivePortalService.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 51 155 32.9 %
Date: 2017-07-14 16:53:18 Functions: 11 19 57.9 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       4             : 
       5             : #include "mozilla/net/CaptivePortalService.h"
       6             : #include "mozilla/Services.h"
       7             : #include "mozilla/Preferences.h"
       8             : #include "nsIObserverService.h"
       9             : #include "nsServiceManagerUtils.h"
      10             : #include "nsXULAppAPI.h"
      11             : 
      12             : static const char16_t kInterfaceName[] = u"captive-portal-inteface";
      13             : 
      14             : static const char kOpenCaptivePortalLoginEvent[] = "captive-portal-login";
      15             : static const char kAbortCaptivePortalLoginEvent[] = "captive-portal-login-abort";
      16             : static const char kCaptivePortalLoginSuccessEvent[] = "captive-portal-login-success";
      17             : 
      18             : static const uint32_t kDefaultInterval = 60*1000; // check every 60 seconds
      19             : 
      20             : namespace mozilla {
      21             : namespace net {
      22             : 
      23             : static LazyLogModule gCaptivePortalLog("CaptivePortalService");
      24             : #undef LOG
      25             : #define LOG(args) MOZ_LOG(gCaptivePortalLog, mozilla::LogLevel::Debug, args)
      26             : 
      27          81 : NS_IMPL_ISUPPORTS(CaptivePortalService, nsICaptivePortalService, nsIObserver,
      28             :                   nsISupportsWeakReference, nsITimerCallback,
      29             :                   nsICaptivePortalCallback)
      30             : 
      31           3 : CaptivePortalService::CaptivePortalService()
      32             :   : mState(UNKNOWN)
      33             :   , mStarted(false)
      34             :   , mInitialized(false)
      35             :   , mRequestInProgress(false)
      36             :   , mEverBeenCaptive(false)
      37             :   , mDelay(kDefaultInterval)
      38             :   , mSlackCount(0)
      39             :   , mMinInterval(kDefaultInterval)
      40             :   , mMaxInterval(25*kDefaultInterval)
      41           3 :   , mBackoffFactor(5.0)
      42             : {
      43           3 :   mLastChecked = TimeStamp::Now();
      44           3 : }
      45             : 
      46           0 : CaptivePortalService::~CaptivePortalService()
      47             : {
      48           0 :   LOG(("CaptivePortalService::~CaptivePortalService isParentProcess:%d\n",
      49             :        XRE_GetProcessType() == GeckoProcessType_Default));
      50           0 : }
      51             : 
      52             : nsresult
      53           1 : CaptivePortalService::PerformCheck()
      54             : {
      55           1 :   LOG(("CaptivePortalService::PerformCheck mRequestInProgress:%d mInitialized:%d mStarted:%d\n",
      56             :         mRequestInProgress, mInitialized, mStarted));
      57             :   // Don't issue another request if last one didn't complete
      58           1 :   if (mRequestInProgress || !mInitialized || !mStarted) {
      59           1 :     return NS_OK;
      60             :   }
      61           0 :   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
      62             :   nsresult rv;
      63           0 :   if (!mCaptivePortalDetector) {
      64             :     mCaptivePortalDetector =
      65           0 :       do_GetService("@mozilla.org/toolkit/captive-detector;1", &rv);
      66           0 :     if (NS_FAILED(rv)) {
      67           0 :         LOG(("Unable to get a captive portal detector\n"));
      68           0 :         return rv;
      69             :     }
      70             :   }
      71             : 
      72           0 :   LOG(("CaptivePortalService::PerformCheck - Calling CheckCaptivePortal\n"));
      73           0 :   mRequestInProgress = true;
      74           0 :   mCaptivePortalDetector->CheckCaptivePortal(kInterfaceName, this);
      75           0 :   return NS_OK;
      76             : }
      77             : 
      78             : nsresult
      79           1 : CaptivePortalService::RearmTimer()
      80             : {
      81           1 :   LOG(("CaptivePortalService::RearmTimer\n"));
      82             :   // Start a timer to recheck
      83           1 :   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
      84           1 :   if (mTimer) {
      85           0 :     mTimer->Cancel();
      86             :   }
      87             : 
      88             :   // If we have successfully determined the state, and we have never detected
      89             :   // a captive portal, we don't need to keep polling, but will rely on events
      90             :   // to trigger detection.
      91           1 :   if (mState == NOT_CAPTIVE) {
      92           0 :     return NS_OK;
      93             :   }
      94             : 
      95           1 :   if (!mTimer) {
      96           1 :     mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
      97             :   }
      98             : 
      99           1 :   if (mTimer && mDelay > 0) {
     100           1 :     LOG(("CaptivePortalService - Reloading timer with delay %u\n", mDelay));
     101           1 :     return mTimer->InitWithCallback(this, mDelay, nsITimer::TYPE_ONE_SHOT);
     102             :   }
     103             : 
     104           0 :   return NS_OK;
     105             : }
     106             : 
     107             : nsresult
     108           1 : CaptivePortalService::Initialize()
     109             : {
     110           1 :   if (mInitialized) {
     111           0 :     return NS_OK;
     112             :   }
     113           1 :   mInitialized = true;
     114             : 
     115             :   // Only the main process service should actually do anything. The service in
     116             :   // the content process only mirrors the CP state in the main process.
     117           1 :   if (XRE_GetProcessType() != GeckoProcessType_Default) {
     118           0 :     return NS_OK;
     119             :   }
     120             : 
     121             :   nsCOMPtr<nsIObserverService> observerService =
     122           2 :     mozilla::services::GetObserverService();
     123           1 :   if (observerService) {
     124           1 :     observerService->AddObserver(this, kOpenCaptivePortalLoginEvent, true);
     125           1 :     observerService->AddObserver(this, kAbortCaptivePortalLoginEvent, true);
     126           1 :     observerService->AddObserver(this, kCaptivePortalLoginSuccessEvent, true);
     127             :   }
     128             : 
     129           1 :   LOG(("Initialized CaptivePortalService\n"));
     130           1 :   return NS_OK;
     131             : }
     132             : 
     133             : nsresult
     134           0 : CaptivePortalService::Start()
     135             : {
     136           0 :   if (!mInitialized) {
     137           0 :     return NS_ERROR_NOT_INITIALIZED;
     138             :   }
     139             : 
     140           0 :   if (XRE_GetProcessType() != GeckoProcessType_Default) {
     141             :     // Doesn't do anything if called in the content process.
     142           0 :     return NS_OK;
     143             :   }
     144             : 
     145           0 :   if (mStarted) {
     146           0 :     return NS_OK;
     147             :   }
     148             : 
     149           0 :   MOZ_ASSERT(mState == UNKNOWN, "Initial state should be UNKNOWN");
     150           0 :   mStarted = true;
     151           0 :   mEverBeenCaptive = false;
     152             : 
     153             :   // Get the delay prefs
     154           0 :   Preferences::GetUint("network.captive-portal-service.minInterval", &mMinInterval);
     155           0 :   Preferences::GetUint("network.captive-portal-service.maxInterval", &mMaxInterval);
     156           0 :   Preferences::GetFloat("network.captive-portal-service.backoffFactor", &mBackoffFactor);
     157             : 
     158           0 :   LOG(("CaptivePortalService::Start min:%u max:%u backoff:%.2f\n",
     159             :        mMinInterval, mMaxInterval, mBackoffFactor));
     160             : 
     161           0 :   mSlackCount = 0;
     162           0 :   mDelay = mMinInterval;
     163             : 
     164             :   // When Start is called, perform a check immediately
     165           0 :   PerformCheck();
     166           0 :   RearmTimer();
     167           0 :   return NS_OK;
     168             : }
     169             : 
     170             : nsresult
     171           1 : CaptivePortalService::Stop()
     172             : {
     173           1 :   LOG(("CaptivePortalService::Stop\n"));
     174             : 
     175           1 :   if (XRE_GetProcessType() != GeckoProcessType_Default) {
     176             :     // Doesn't do anything when called in the content process.
     177           0 :     return NS_OK;
     178             :   }
     179             : 
     180           1 :   if (!mStarted) {
     181           1 :     return NS_OK;
     182             :   }
     183             : 
     184           0 :   if (mTimer) {
     185           0 :     mTimer->Cancel();
     186             :   }
     187           0 :   mTimer = nullptr;
     188           0 :   mRequestInProgress = false;
     189           0 :   mStarted = false;
     190           0 :   if (mCaptivePortalDetector) {
     191           0 :     mCaptivePortalDetector->Abort(kInterfaceName);
     192             :   }
     193           0 :   mCaptivePortalDetector = nullptr;
     194             : 
     195             :   // Clear the state in case anyone queries the state while detection is off.
     196           0 :   mState = UNKNOWN;
     197           0 :   return NS_OK;
     198             : }
     199             : 
     200             : void
     201           2 : CaptivePortalService::SetStateInChild(int32_t aState)
     202             : {
     203             :   // This should only be called in the content process, from ContentChild.cpp
     204             :   // in order to mirror the captive portal state set in the chrome process.
     205           2 :   MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Default);
     206             : 
     207           2 :   mState = aState;
     208           2 :   mLastChecked = TimeStamp::Now();
     209           2 : }
     210             : 
     211             : //-----------------------------------------------------------------------------
     212             : // CaptivePortalService::nsICaptivePortalService
     213             : //-----------------------------------------------------------------------------
     214             : 
     215             : NS_IMETHODIMP
     216           4 : CaptivePortalService::GetState(int32_t *aState)
     217             : {
     218           4 :   *aState = mState;
     219           4 :   return NS_OK;
     220             : }
     221             : 
     222             : NS_IMETHODIMP
     223           1 : CaptivePortalService::RecheckCaptivePortal()
     224             : {
     225           1 :   LOG(("CaptivePortalService::RecheckCaptivePortal\n"));
     226             : 
     227           1 :   if (XRE_GetProcessType() != GeckoProcessType_Default) {
     228             :     // Doesn't do anything if called in the content process.
     229           0 :     return NS_OK;
     230             :   }
     231             : 
     232             :   // This is called for user activity. We need to reset the slack count,
     233             :   // so the checks continue to be quite frequent.
     234           1 :   mSlackCount = 0;
     235           1 :   mDelay = mMinInterval;
     236             : 
     237           1 :   PerformCheck();
     238           1 :   RearmTimer();
     239           1 :   return NS_OK;
     240             : }
     241             : 
     242             : NS_IMETHODIMP
     243           0 : CaptivePortalService::GetLastChecked(uint64_t *aLastChecked)
     244             : {
     245           0 :   double duration = (TimeStamp::Now() - mLastChecked).ToMilliseconds();
     246           0 :   *aLastChecked = static_cast<uint64_t>(duration);
     247           0 :   return NS_OK;
     248             : }
     249             : 
     250             : //-----------------------------------------------------------------------------
     251             : // CaptivePortalService::nsITimer
     252             : // This callback gets called every mDelay miliseconds
     253             : // It issues a checkCaptivePortal operation if one isn't already in progress
     254             : //-----------------------------------------------------------------------------
     255             : NS_IMETHODIMP
     256           0 : CaptivePortalService::Notify(nsITimer *aTimer)
     257             : {
     258           0 :   LOG(("CaptivePortalService::Notify\n"));
     259           0 :   MOZ_ASSERT(aTimer == mTimer);
     260           0 :   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
     261             : 
     262           0 :   PerformCheck();
     263             : 
     264             :   // This is needed because we don't want to always make requests very often.
     265             :   // Every 10 checks, we the delay is increased mBackoffFactor times
     266             :   // to a maximum delay of mMaxInterval
     267           0 :   mSlackCount++;
     268           0 :   if (mSlackCount % 10 == 0) {
     269           0 :     mDelay = mDelay * mBackoffFactor;
     270             :   }
     271           0 :   if (mDelay > mMaxInterval) {
     272           0 :     mDelay = mMaxInterval;
     273             :   }
     274             : 
     275             :   // Note - if mDelay is 0, the timer will not be rearmed.
     276           0 :   RearmTimer();
     277             : 
     278           0 :   return NS_OK;
     279             : }
     280             : 
     281             : //-----------------------------------------------------------------------------
     282             : // CaptivePortalService::nsIObserver
     283             : //-----------------------------------------------------------------------------
     284             : NS_IMETHODIMP
     285           0 : CaptivePortalService::Observe(nsISupports *aSubject,
     286             :                               const char * aTopic,
     287             :                               const char16_t * aData)
     288             : {
     289           0 :   if (XRE_GetProcessType() != GeckoProcessType_Default) {
     290             :     // Doesn't do anything if called in the content process.
     291           0 :     return NS_OK;
     292             :   }
     293             : 
     294           0 :   LOG(("CaptivePortalService::Observe() topic=%s\n", aTopic));
     295           0 :   if (!strcmp(aTopic, kOpenCaptivePortalLoginEvent)) {
     296             :     // A redirect or altered content has been detected.
     297             :     // The user needs to log in. We are in a captive portal.
     298           0 :     mState = LOCKED_PORTAL;
     299           0 :     mLastChecked = TimeStamp::Now();
     300           0 :     mEverBeenCaptive = true;
     301           0 :   } else if (!strcmp(aTopic, kCaptivePortalLoginSuccessEvent)) {
     302             :     // The user has successfully logged in. We have connectivity.
     303           0 :     mState = UNLOCKED_PORTAL;
     304           0 :     mLastChecked = TimeStamp::Now();
     305           0 :     mSlackCount = 0;
     306           0 :     mDelay = mMinInterval;
     307             : 
     308           0 :     RearmTimer();
     309           0 :   } else if (!strcmp(aTopic, kAbortCaptivePortalLoginEvent)) {
     310             :     // The login has been aborted
     311           0 :     mState = UNKNOWN;
     312           0 :     mLastChecked = TimeStamp::Now();
     313           0 :     mSlackCount = 0;
     314             :   }
     315             : 
     316             :   // Send notification so that the captive portal state is mirrored in the
     317             :   // content process.
     318           0 :   nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
     319           0 :   if (observerService) {
     320           0 :     nsCOMPtr<nsICaptivePortalService> cps(this);
     321           0 :     observerService->NotifyObservers(cps, NS_IPC_CAPTIVE_PORTAL_SET_STATE, nullptr);
     322             :   }
     323             : 
     324           0 :   return NS_OK;
     325             : }
     326             : 
     327             : //-----------------------------------------------------------------------------
     328             : // CaptivePortalService::nsICaptivePortalCallback
     329             : //-----------------------------------------------------------------------------
     330             : NS_IMETHODIMP
     331           0 : CaptivePortalService::Prepare()
     332             : {
     333           0 :   LOG(("CaptivePortalService::Prepare\n"));
     334           0 :   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
     335             :   // XXX: Finish preparation shouldn't be called until dns and routing is available.
     336           0 :   if (mCaptivePortalDetector) {
     337           0 :     mCaptivePortalDetector->FinishPreparation(kInterfaceName);
     338             :   }
     339           0 :   return NS_OK;
     340             : }
     341             : 
     342             : NS_IMETHODIMP
     343           0 : CaptivePortalService::Complete(bool success)
     344             : {
     345           0 :   LOG(("CaptivePortalService::Complete(success=%d) mState=%d\n", success, mState));
     346           0 :   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
     347           0 :   mLastChecked = TimeStamp::Now();
     348             : 
     349             :   // Note: this callback gets called when:
     350             :   // 1. the request is completed, and content is valid (success == true)
     351             :   // 2. when the request is aborted or times out (success == false)
     352             : 
     353           0 :   if (success) {
     354           0 :     if (mEverBeenCaptive) {
     355           0 :       mState = UNLOCKED_PORTAL;
     356             :     } else {
     357           0 :       mState = NOT_CAPTIVE;
     358             :     }
     359             :   }
     360             : 
     361           0 :   mRequestInProgress = false;
     362           0 :   return NS_OK;
     363             : }
     364             : 
     365             : } // namespace net
     366             : } // namespace mozilla

Generated by: LCOV version 1.13