blob: b6ac4551fb2c8ce817e1c3a606e97635b6fc91ee [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/ssl/captive_portal_metrics_tab_helper.h"
#include "base/metrics/histogram_macros.h"
#include "components/captive_portal/captive_portal_detector.h"
#import "ios/chrome/browser/ssl/captive_portal_detector_tab_helper.h"
#import "ios/web/public/web_state/navigation_context.h"
#import "ios/web/public/web_state/web_state.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
DEFINE_WEB_STATE_USER_DATA_KEY(CaptivePortalMetricsTabHelper);
// Result of captive portal network check after a request takes longer than
// |kCaptivePortalTestDelayInMilliseconds|.
const char kCaptivePortalCausingTimeoutHistogram[] =
"CaptivePortal.Session.TimeoutDetectionResult";
// Result of captive portal network check after a request fails with
// NSURLErrorSecureConnectionFailed.
const char kCaptivePortalSecureConnectionFailedHistogram[] =
"CaptivePortal.Session.SecureConnectionFailed";
// Time in milliseconds of still ongoing request before testing if the user is
// behind a captive portal.
const int64_t kCaptivePortalTestDelayInMilliseconds = 8000;
CaptivePortalMetricsTabHelper::CaptivePortalMetricsTabHelper(
web::WebState* web_state)
: web_state_(web_state) {
web_state_->AddObserver(this);
}
CaptivePortalMetricsTabHelper::~CaptivePortalMetricsTabHelper() = default;
void CaptivePortalMetricsTabHelper::TimerTriggered() {
TestForCaptivePortal(
base::BindOnce(&HandleTimeoutCaptivePortalDetectionResult));
}
void CaptivePortalMetricsTabHelper::TestForCaptivePortal(
captive_portal::CaptivePortalDetector::DetectionCallback callback) {
CaptivePortalDetectorTabHelper* tab_helper =
CaptivePortalDetectorTabHelper::FromWebState(web_state_);
// TODO(crbug.com/760873): replace test with DCHECK when this method is only
// called on WebStates attached to tabs.
if (tab_helper) {
tab_helper->detector()->DetectCaptivePortal(
GURL(captive_portal::CaptivePortalDetector::kDefaultURL),
std::move(callback), NO_TRAFFIC_ANNOTATION_YET);
}
}
// static
CaptivePortalStatus
CaptivePortalMetricsTabHelper::CaptivePortalStatusFromDetectionResult(
const captive_portal::CaptivePortalDetector::Results& results) {
CaptivePortalStatus status;
switch (results.result) {
case captive_portal::RESULT_INTERNET_CONNECTED:
status = CaptivePortalStatus::ONLINE;
break;
case captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL:
status = CaptivePortalStatus::PORTAL;
break;
default:
status = CaptivePortalStatus::UNKNOWN;
break;
}
return status;
}
// static
void CaptivePortalMetricsTabHelper::
HandleSecureConnectionFailedCaptivePortalDetectionResult(
const captive_portal::CaptivePortalDetector::Results& results) {
CaptivePortalStatus status =
CaptivePortalMetricsTabHelper::CaptivePortalStatusFromDetectionResult(
results);
UMA_HISTOGRAM_ENUMERATION(kCaptivePortalSecureConnectionFailedHistogram,
static_cast<int>(status),
static_cast<int>(CaptivePortalStatus::COUNT));
}
// static
void CaptivePortalMetricsTabHelper::HandleTimeoutCaptivePortalDetectionResult(
const captive_portal::CaptivePortalDetector::Results& results) {
CaptivePortalStatus status =
CaptivePortalMetricsTabHelper::CaptivePortalStatusFromDetectionResult(
results);
UMA_HISTOGRAM_ENUMERATION(kCaptivePortalCausingTimeoutHistogram,
static_cast<int>(status),
static_cast<int>(CaptivePortalStatus::COUNT));
}
// WebStateObserver
void CaptivePortalMetricsTabHelper::DidStartNavigation(
web::WebState* web_state,
web::NavigationContext* navigation_context) {
timer_.Stop();
timer_.Start(
FROM_HERE,
base::TimeDelta::FromMilliseconds(kCaptivePortalTestDelayInMilliseconds),
this, &CaptivePortalMetricsTabHelper::TimerTriggered);
}
void CaptivePortalMetricsTabHelper::DidFinishNavigation(
web::WebState* web_state,
web::NavigationContext* navigation_context) {
timer_.Stop();
NSError* error = navigation_context->GetError();
if ([error.domain isEqualToString:NSURLErrorDomain] &&
error.code == NSURLErrorSecureConnectionFailed) {
TestForCaptivePortal(
base::Bind(&HandleSecureConnectionFailedCaptivePortalDetectionResult));
} else if ([error.domain isEqualToString:NSURLErrorDomain] &&
error.code == NSURLErrorTimedOut) {
TestForCaptivePortal(
base::Bind(&HandleTimeoutCaptivePortalDetectionResult));
}
}
void CaptivePortalMetricsTabHelper::WebStateDestroyed(
web::WebState* web_state) {
DCHECK_EQ(web_state_, web_state);
timer_.Stop();
// Ensure the captive portal detection is canceled if it never completed.
CaptivePortalDetectorTabHelper* tab_helper =
CaptivePortalDetectorTabHelper::FromWebState(web_state_);
// TODO(crbug.com/760873): replace test with DCHECK when this method is only
// called on WebStates attached to tabs.
if (tab_helper) {
tab_helper->detector()->Cancel();
}
web_state_->RemoveObserver(this);
web_state_ = nullptr;
}