mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1048131 - Implement CaptivePortalService using nsICaptivePortalDetector r=mcmanus
This commit is contained in:
parent
34f2bb349d
commit
c50130a020
@ -940,10 +940,6 @@ pref("consoleservice.buffered", false);
|
||||
pref("toolkit.storage.pageSize", 2048);
|
||||
#endif
|
||||
|
||||
// Enable captive portal detection.
|
||||
pref("captivedetect.canonicalURL", "http://detectportal.firefox.com/success.txt");
|
||||
pref("captivedetect.canonicalContent", "success\n");
|
||||
|
||||
// The url of the manifest we use for ADU pings.
|
||||
pref("ping.manifestURL", "https://marketplace.firefox.com/packaged.webapp");
|
||||
|
||||
|
@ -4620,6 +4620,14 @@ pref("ui.touch_activation.duration_ms", 10);
|
||||
// actions when the fifo is written to. Disable this in general.
|
||||
pref("memory_info_dumper.watch_fifo.enabled", false);
|
||||
|
||||
// If minInterval is 0, the check will only happen
|
||||
// when the service has a strong suspicion we are in a captive portal
|
||||
pref("network.captive-portal-service.minInterval", 60000); // 60 seconds
|
||||
pref("network.captive-portal-service.maxInterval", 1500000); // 25 minutes
|
||||
// Every 10 checks, the delay is increased by a factor of 5
|
||||
pref("network.captive-portal-service.backoffFactor", "5.0");
|
||||
pref("network.captive-portal-service.enabled", false);
|
||||
|
||||
pref("captivedetect.canonicalURL", "http://detectportal.firefox.com/success.txt");
|
||||
pref("captivedetect.canonicalContent", "success\n");
|
||||
pref("captivedetect.maxWaitingTime", 5000);
|
||||
|
311
netwerk/base/CaptivePortalService.cpp
Normal file
311
netwerk/base/CaptivePortalService.cpp
Normal file
@ -0,0 +1,311 @@
|
||||
/* 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 "CaptivePortalService.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
|
||||
#define kInterfaceName "captive-portal-inteface"
|
||||
|
||||
static const char kOpenCaptivePortalLoginEvent[] = "captive-portal-login";
|
||||
static const char kAbortCaptivePortalLoginEvent[] = "captive-portal-login-abort";
|
||||
static const char kCaptivePortalLoginSuccessEvent[] = "captive-portal-login-success";
|
||||
|
||||
static const uint32_t kDefaultInterval = 60*1000; // check every 60 seconds
|
||||
|
||||
static PRLogModuleInfo *gCaptivePortalLog = nullptr;
|
||||
#undef LOG
|
||||
#define LOG(args) MOZ_LOG(gCaptivePortalLog, mozilla::LogLevel::Debug, args)
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
NS_IMPL_ISUPPORTS(CaptivePortalService, nsICaptivePortalService, nsIObserver,
|
||||
nsISupportsWeakReference, nsITimerCallback,
|
||||
nsICaptivePortalCallback)
|
||||
|
||||
CaptivePortalService::CaptivePortalService()
|
||||
: mState(UNKNOWN)
|
||||
, mStarted(false)
|
||||
, mInitialized(false)
|
||||
, mRequestInProgress(false)
|
||||
, mEverBeenCaptive(false)
|
||||
, mDelay(kDefaultInterval)
|
||||
, mSlackCount(0)
|
||||
, mMinInterval(kDefaultInterval)
|
||||
, mMaxInterval(25*kDefaultInterval)
|
||||
, mBackoffFactor(5.0)
|
||||
{
|
||||
mLastChecked = TimeStamp::Now();
|
||||
}
|
||||
|
||||
CaptivePortalService::~CaptivePortalService()
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
CaptivePortalService::PerformCheck()
|
||||
{
|
||||
LOG(("CaptivePortalService::PerformCheck mRequestInProgress:%d mInitialized:%d mStarted:%d\n",
|
||||
mRequestInProgress, mInitialized, mStarted));
|
||||
// Don't issue another request if last one didn't complete
|
||||
if (mRequestInProgress || !mInitialized || !mStarted) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
if (!mCaptivePortalDetector) {
|
||||
mCaptivePortalDetector =
|
||||
do_GetService("@mozilla.org/toolkit/captive-detector;1", &rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(("Unable to get a captive portal detector\n"));
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
LOG(("CaptivePortalService::PerformCheck - Calling CheckCaptivePortal\n"));
|
||||
mRequestInProgress = true;
|
||||
mCaptivePortalDetector->CheckCaptivePortal(
|
||||
NS_LITERAL_STRING(kInterfaceName).get(), this);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CaptivePortalService::RearmTimer()
|
||||
{
|
||||
// Start a timer to recheck
|
||||
if (mTimer) {
|
||||
mTimer->Cancel();
|
||||
}
|
||||
|
||||
if (!mTimer) {
|
||||
mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
|
||||
}
|
||||
|
||||
if (mTimer && mDelay > 0) {
|
||||
LOG(("CaptivePortalService - Reloading timer with delay %u\n", mDelay));
|
||||
return mTimer->InitWithCallback(this, mDelay, nsITimer::TYPE_ONE_SHOT);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CaptivePortalService::Initialize()
|
||||
{
|
||||
if (mInitialized || XRE_GetProcessType() != GeckoProcessType_Default) {
|
||||
return NS_OK;
|
||||
}
|
||||
mInitialized = true;
|
||||
|
||||
if (!gCaptivePortalLog) {
|
||||
gCaptivePortalLog = PR_NewLogModule("CaptivePortalService");
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
if (observerService) {
|
||||
observerService->AddObserver(this, kOpenCaptivePortalLoginEvent, true);
|
||||
observerService->AddObserver(this, kAbortCaptivePortalLoginEvent, true);
|
||||
observerService->AddObserver(this, kCaptivePortalLoginSuccessEvent, true);
|
||||
}
|
||||
|
||||
LOG(("Initialized CaptivePortalService\n"));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CaptivePortalService::Start()
|
||||
{
|
||||
if (!mInitialized) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (mStarted) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mStarted = true;
|
||||
mEverBeenCaptive = false;
|
||||
|
||||
// Get the delay prefs
|
||||
Preferences::GetUint("network.captive-portal-service.minInterval", &mMinInterval);
|
||||
Preferences::GetUint("network.captive-portal-service.maxInterval", &mMaxInterval);
|
||||
Preferences::GetFloat("network.captive-portal-service.backoffFactor", &mBackoffFactor);
|
||||
|
||||
LOG(("CaptivePortalService::Start min:%u max:%u backoff:%.2f\n",
|
||||
mMinInterval, mMaxInterval, mBackoffFactor));
|
||||
|
||||
mSlackCount = 0;
|
||||
mDelay = mMinInterval;
|
||||
|
||||
// When Start is called, perform a check immediately
|
||||
PerformCheck();
|
||||
RearmTimer();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CaptivePortalService::Stop()
|
||||
{
|
||||
LOG(("CaptivePortalService::Stop\n"));
|
||||
|
||||
if (!mStarted) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (mTimer) {
|
||||
mTimer->Cancel();
|
||||
}
|
||||
mTimer = nullptr;
|
||||
mRequestInProgress = false;
|
||||
mStarted = false;
|
||||
if (mCaptivePortalDetector) {
|
||||
mCaptivePortalDetector->Abort(NS_LITERAL_STRING(kInterfaceName).get());
|
||||
}
|
||||
mCaptivePortalDetector = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CaptivePortalService::nsICaptivePortalService
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
CaptivePortalService::GetState(int32_t *aState)
|
||||
{
|
||||
*aState = UNKNOWN;
|
||||
if (!mInitialized) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
*aState = mState;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CaptivePortalService::RecheckCaptivePortal()
|
||||
{
|
||||
LOG(("CaptivePortalService::RecheckCaptivePortal\n"));
|
||||
|
||||
// This is called for user activity. We need to reset the slack count,
|
||||
// so the checks continue to be quite frequent.
|
||||
mSlackCount = 0;
|
||||
mDelay = mMinInterval;
|
||||
|
||||
PerformCheck();
|
||||
RearmTimer();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CaptivePortalService::GetLastChecked(uint64_t *aLastChecked)
|
||||
{
|
||||
double duration = (TimeStamp::Now() - mLastChecked).ToMilliseconds();
|
||||
*aLastChecked = static_cast<uint64_t>(duration);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CaptivePortalService::nsITimer
|
||||
// This callback gets called every mDelay miliseconds
|
||||
// It issues a checkCaptivePortal operation if one isn't already in progress
|
||||
//-----------------------------------------------------------------------------
|
||||
NS_IMETHODIMP
|
||||
CaptivePortalService::Notify(nsITimer *aTimer)
|
||||
{
|
||||
LOG(("CaptivePortalService::Notify\n"));
|
||||
MOZ_ASSERT(aTimer == mTimer);
|
||||
|
||||
PerformCheck();
|
||||
|
||||
// This is needed because we don't want to always make requests very often.
|
||||
// Every 10 checks, we the delay is increased mBackoffFactor times
|
||||
// to a maximum delay of mMaxInterval
|
||||
mSlackCount++;
|
||||
if (mSlackCount % 10 == 0) {
|
||||
mDelay = mDelay * mBackoffFactor;
|
||||
}
|
||||
if (mDelay > mMaxInterval) {
|
||||
mDelay = mMaxInterval;
|
||||
}
|
||||
|
||||
// Note - if mDelay is 0, the timer will not be rearmed.
|
||||
RearmTimer();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CaptivePortalService::nsIObserver
|
||||
//-----------------------------------------------------------------------------
|
||||
NS_IMETHODIMP
|
||||
CaptivePortalService::Observe(nsISupports *aSubject,
|
||||
const char * aTopic,
|
||||
const char16_t * aData)
|
||||
{
|
||||
LOG(("CaptivePortalService::Observe() topic=%s\n", aTopic));
|
||||
if (!strcmp(aTopic, kOpenCaptivePortalLoginEvent)) {
|
||||
// A redirect or altered content has been detected.
|
||||
// The user needs to log in. We are in a captive portal.
|
||||
mState = LOCKED_PORTAL;
|
||||
mLastChecked = TimeStamp::Now();
|
||||
mEverBeenCaptive = true;
|
||||
} else if (!strcmp(aTopic, kCaptivePortalLoginSuccessEvent)) {
|
||||
// The user has successfully logged in. We have connectivity.
|
||||
mState = UNLOCKED_PORTAL;
|
||||
mLastChecked = TimeStamp::Now();
|
||||
mRequestInProgress = false;
|
||||
mSlackCount = 0;
|
||||
mDelay = mMinInterval;
|
||||
RearmTimer();
|
||||
} else if (!strcmp(aTopic, kAbortCaptivePortalLoginEvent)) {
|
||||
// The login has been aborted
|
||||
mRequestInProgress = false;
|
||||
mState = UNKNOWN;
|
||||
mLastChecked = TimeStamp::Now();
|
||||
mSlackCount = 0;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CaptivePortalService::nsICaptivePortalCallback
|
||||
//-----------------------------------------------------------------------------
|
||||
NS_IMETHODIMP
|
||||
CaptivePortalService::Prepare()
|
||||
{
|
||||
LOG(("CaptivePortalService::Prepare\n"));
|
||||
// XXX: Finish preparation shouldn't be called until dns and routing is available.
|
||||
if (mCaptivePortalDetector) {
|
||||
mCaptivePortalDetector->FinishPreparation(NS_LITERAL_STRING(kInterfaceName).get());
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CaptivePortalService::Complete(bool success)
|
||||
{
|
||||
LOG(("CaptivePortalService::Complete(success=%d) mState=%d\n", success, mState));
|
||||
mLastChecked = TimeStamp::Now();
|
||||
if ((mState == UNKNOWN || mState == NOT_CAPTIVE) && success) {
|
||||
mState = NOT_CAPTIVE;
|
||||
// If this check succeeded and we have never been in a captive portal
|
||||
// since the service was started, there is no need to keep polling
|
||||
if (!mEverBeenCaptive) {
|
||||
mDelay = 0;
|
||||
if (mTimer) {
|
||||
mTimer->Cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mRequestInProgress = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
65
netwerk/base/CaptivePortalService.h
Normal file
65
netwerk/base/CaptivePortalService.h
Normal file
@ -0,0 +1,65 @@
|
||||
/* 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/. */
|
||||
|
||||
#ifndef CaptivePortalService_h_
|
||||
#define CaptivePortalService_h_
|
||||
|
||||
#include "nsICaptivePortalService.h"
|
||||
#include "nsICaptivePortalDetector.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsCOMArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class CaptivePortalService
|
||||
: public nsICaptivePortalService
|
||||
, public nsIObserver
|
||||
, public nsSupportsWeakReference
|
||||
, public nsITimerCallback
|
||||
, public nsICaptivePortalCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSICAPTIVEPORTALSERVICE
|
||||
NS_DECL_NSIOBSERVER
|
||||
NS_DECL_NSITIMERCALLBACK
|
||||
NS_DECL_NSICAPTIVEPORTALCALLBACK
|
||||
|
||||
CaptivePortalService();
|
||||
nsresult Initialize();
|
||||
nsresult Start();
|
||||
nsresult Stop();
|
||||
private:
|
||||
virtual ~CaptivePortalService();
|
||||
nsresult PerformCheck();
|
||||
nsresult RearmTimer();
|
||||
|
||||
nsCOMPtr<nsICaptivePortalDetector> mCaptivePortalDetector;
|
||||
int32_t mState;
|
||||
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
bool mStarted;
|
||||
bool mInitialized;
|
||||
bool mRequestInProgress;
|
||||
bool mEverBeenCaptive;
|
||||
|
||||
uint32_t mDelay;
|
||||
int32_t mSlackCount;
|
||||
|
||||
uint32_t mMinInterval;
|
||||
uint32_t mMaxInterval;
|
||||
float mBackoffFactor;
|
||||
|
||||
// This holds a timestamp when the last time when the captive portal check
|
||||
// has changed state.
|
||||
mozilla::TimeStamp mLastChecked;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // CaptivePortalService_h_
|
@ -27,6 +27,7 @@ XPIDL_SOURCES += [
|
||||
'nsICacheInfoChannel.idl',
|
||||
'nsICachingChannel.idl',
|
||||
'nsICancelable.idl',
|
||||
'nsICaptivePortalService.idl',
|
||||
'nsIChannel.idl',
|
||||
'nsIChannelEventSink.idl',
|
||||
'nsIChildChannel.idl',
|
||||
@ -164,6 +165,7 @@ EXPORTS.mozilla += [
|
||||
]
|
||||
|
||||
EXPORTS.mozilla.net += [
|
||||
'CaptivePortalService.h',
|
||||
'ChannelDiverterChild.h',
|
||||
'ChannelDiverterParent.h',
|
||||
'Dashboard.h',
|
||||
@ -181,6 +183,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
||||
UNIFIED_SOURCES += [
|
||||
'ArrayBufferInputStream.cpp',
|
||||
'BackgroundFileSaver.cpp',
|
||||
'CaptivePortalService.cpp',
|
||||
'ChannelDiverterChild.cpp',
|
||||
'ChannelDiverterParent.cpp',
|
||||
'Dashboard.cpp',
|
||||
|
44
netwerk/base/nsICaptivePortalService.idl
Normal file
44
netwerk/base/nsICaptivePortalService.idl
Normal file
@ -0,0 +1,44 @@
|
||||
/* 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(b5fd5629-d04c-4138-9529-9311f291ecd4)]
|
||||
interface nsICaptivePortalServiceCallback : nsISupports
|
||||
{
|
||||
/**
|
||||
* Invoke callbacks after captive portal detection finished.
|
||||
*/
|
||||
void complete(in bool success, in nsresult error);
|
||||
};
|
||||
|
||||
/**
|
||||
* Service used for captive portal detection.
|
||||
*/
|
||||
[scriptable, uuid(bdbe0555-fc3d-4f7b-9205-c309ceb2d641)]
|
||||
interface nsICaptivePortalService : nsISupports
|
||||
{
|
||||
const long UNKNOWN = 0;
|
||||
const long NOT_CAPTIVE = 1;
|
||||
const long UNLOCKED_PORTAL = 2;
|
||||
const long LOCKED_PORTAL = 3;
|
||||
|
||||
/**
|
||||
* Called from XPCOM to trigger a captive portal recheck.
|
||||
* A network request will only be performed if no other checks are currently
|
||||
* ongoing.
|
||||
*/
|
||||
void recheckCaptivePortal();
|
||||
|
||||
/**
|
||||
* Returns the state of the captive portal.
|
||||
*/
|
||||
readonly attribute long state;
|
||||
|
||||
/**
|
||||
* Returns the time difference between NOW and the last time a request was
|
||||
* completed in milliseconds.
|
||||
*/
|
||||
readonly attribute unsigned long long lastChecked;
|
||||
};
|
@ -469,6 +469,17 @@
|
||||
{ 0xae, 0xcf, 0x05, 0xf8, 0xfa, 0xf0, 0x0c, 0x9b } \
|
||||
}
|
||||
|
||||
// captive portal service implementing nsICaptivePortalService
|
||||
#define NS_CAPTIVEPORTAL_CONTRACTID \
|
||||
"@mozilla.org/network/captive-portal-service;1"
|
||||
#define NS_CAPTIVEPORTAL_CID \
|
||||
{ /* bdbe0555-fc3d-4f7b-9205-c309ceb2d641 */ \
|
||||
0xbdbe0555, \
|
||||
0xfc3d, \
|
||||
0x4f7b, \
|
||||
{ 0x92, 0x05, 0xc3, 0x09, 0xce, 0xb2, 0xd6, 0x41 } \
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* netwerk/cache/ classes
|
||||
*/
|
||||
|
@ -132,6 +132,16 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(RedirectChannelRegistrar)
|
||||
typedef mozilla::net::CacheStorageService CacheStorageService;
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(CacheStorageService)
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "mozilla/net/CaptivePortalService.h"
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(CaptivePortalService)
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
extern nsresult
|
||||
@ -809,6 +819,7 @@ NS_DEFINE_NAMED_CID(NS_SERIALIZATION_HELPER_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_REDIRECTCHANNELREGISTRAR_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_CACHE_STORAGE_SERVICE_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_NETWORKPREDICTOR_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_CAPTIVEPORTAL_CID);
|
||||
|
||||
static const mozilla::Module::CIDEntry kNeckoCIDs[] = {
|
||||
{ &kNS_IOSERVICE_CID, false, nullptr, nsIOServiceConstructor },
|
||||
@ -956,6 +967,7 @@ static const mozilla::Module::CIDEntry kNeckoCIDs[] = {
|
||||
{ &kNS_REDIRECTCHANNELREGISTRAR_CID, false, nullptr, RedirectChannelRegistrarConstructor },
|
||||
{ &kNS_CACHE_STORAGE_SERVICE_CID, false, nullptr, CacheStorageServiceConstructor },
|
||||
{ &kNS_NETWORKPREDICTOR_CID, false, nullptr, mozilla::net::Predictor::Create },
|
||||
{ &kNS_CAPTIVEPORTAL_CID, false, nullptr, mozilla::net::CaptivePortalServiceConstructor },
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
@ -1107,6 +1119,7 @@ static const mozilla::Module::ContractIDEntry kNeckoContracts[] = {
|
||||
{ NS_CACHE_STORAGE_SERVICE_CONTRACTID, &kNS_CACHE_STORAGE_SERVICE_CID },
|
||||
{ NS_CACHE_STORAGE_SERVICE_CONTRACTID2, &kNS_CACHE_STORAGE_SERVICE_CID },
|
||||
{ NS_NETWORKPREDICTOR_CONTRACTID, &kNS_NETWORKPREDICTOR_CID },
|
||||
{ NS_CAPTIVEPORTAL_CONTRACTID, &kNS_CAPTIVEPORTAL_CID },
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user