Commit e4c94fbc authored by Mike Dougherty's avatar Mike Dougherty Committed by Commit Bot

Add Captive Portal Metrics Tab Helper.

Users on a Captive Portal network without an internet connection may see
an error interstitial or experience timeouts. These metrics will help to
identify users in these scenarios.

Bug: 783315, 785548
Cq-Include-Trybots: master.tryserver.chromium.mac:ios-simulator-cronet;master.tryserver.chromium.mac:ios-simulator-full-configs
Change-Id: I911b6d9a57a017d2827ee0feeb07d8721acfa849
Reviewed-on: https://chromium-review.googlesource.com/783694
Commit-Queue: Mike Dougherty <michaeldo@chromium.org>
Reviewed-by: default avatarMark Pearson <mpearson@chromium.org>
Reviewed-by: default avatarEugene But <eugenebut@chromium.org>
Reviewed-by: default avatarMike Dougherty <michaeldo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#520748}
parent cb829c00
...@@ -8,6 +8,9 @@ source_set("ssl") { ...@@ -8,6 +8,9 @@ source_set("ssl") {
"captive_portal_detector_tab_helper.h", "captive_portal_detector_tab_helper.h",
"captive_portal_detector_tab_helper.mm", "captive_portal_detector_tab_helper.mm",
"captive_portal_detector_tab_helper_delegate.h", "captive_portal_detector_tab_helper_delegate.h",
"captive_portal_metrics.h",
"captive_portal_metrics_tab_helper.h",
"captive_portal_metrics_tab_helper.mm",
"insecure_input_tab_helper.h", "insecure_input_tab_helper.h",
"insecure_input_tab_helper.mm", "insecure_input_tab_helper.mm",
"ios_captive_portal_blocking_page.h", "ios_captive_portal_blocking_page.h",
......
// 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.
#ifndef IOS_CHROME_BROWSER_SSL_CAPTIVE_PORTAL_METRICS_H_
#define IOS_CHROME_BROWSER_SSL_CAPTIVE_PORTAL_METRICS_H_
// Enum used to record the captive portal detection result.
enum class CaptivePortalStatus {
UNKNOWN = 0,
OFFLINE = 1,
ONLINE = 2,
PORTAL = 3,
PROXY_AUTH_REQUIRED = 4,
COUNT
};
#endif // IOS_CHROME_BROWSER_SSL_CAPTIVE_PORTAL_METRICS_H_
// 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.
#ifndef IOS_CHROME_BROWSER_SSL_CAPTIVE_PORTAL_METRICS_TAB_HELPER_H_
#define IOS_CHROME_BROWSER_SSL_CAPTIVE_PORTAL_METRICS_TAB_HELPER_H_
#include "base/macros.h"
#include "base/timer/timer.h"
#include "components/captive_portal/captive_portal_detector.h"
#include "ios/chrome/browser/ssl/captive_portal_metrics.h"
#include "ios/web/public/web_state/web_state_observer.h"
#import "ios/web/public/web_state/web_state_user_data.h"
namespace web {
class WebState;
}
// Logs the result of a captive portal detector when users enter error states
// which could be caused by being on a Captive Portal network. These metrics
// will be used to identify how many users incorrectly see these error cases
// because they are on captive portal networks without a network connection. In
// the future, these users should be shown the captive portal login interstitial
// if these metrics show that enough users are affected.
// 1. When a -1200 error occurs.
// 2. If a request is taking a long time to complete.
class CaptivePortalMetricsTabHelper
: public web::WebStateObserver,
public web::WebStateUserData<CaptivePortalMetricsTabHelper> {
public:
~CaptivePortalMetricsTabHelper() override;
private:
explicit CaptivePortalMetricsTabHelper(web::WebState* web_state);
friend class web::WebStateUserData<CaptivePortalMetricsTabHelper>;
// Tests if network access is currently blocked by a captive portal.
void TestForCaptivePortal(
const captive_portal::CaptivePortalDetector::DetectionCallback& callback);
// Tests for a captive portal and logs the resulting metric.
void TimerTriggered();
// Returns the associated CaptivePortalStatus value for logging to UMA metrics
// based on detection |results|.
static CaptivePortalStatus CaptivePortalStatusFromDetectionResult(
const captive_portal::CaptivePortalDetector::Results& results);
// Logs the |results| of the captive portal detector to the UMA metric for a
// failed secure connection.
static void HandleSecureConnectionFailedCaptivePortalDetectionResult(
const captive_portal::CaptivePortalDetector::Results& results);
// Logs the |results| of the captive portal detector to the UMA metric for a
// potential request timeout.
static void HandleTimeoutCaptivePortalDetectionResult(
const captive_portal::CaptivePortalDetector::Results& results);
// web::WebStateObserver implementation.
void DidStartNavigation(web::WebState* web_state,
web::NavigationContext* navigation_context) override;
void DidFinishNavigation(web::WebState* web_state,
web::NavigationContext* navigation_context) override;
void WebStateDestroyed(web::WebState* web_state) override;
// A timer to test for a captive portal blocking an ongoing request.
base::OneShotTimer timer_;
// The web state associated with this tab helper.
web::WebState* web_state_;
DISALLOW_COPY_AND_ASSIGN(CaptivePortalMetricsTabHelper);
};
#endif // IOS_CHROME_BROWSER_SSL_CAPTIVE_PORTAL_METRICS_TAB_HELPER_H_
// 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/mac/bind_objc_block.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::Bind(&HandleTimeoutCaptivePortalDetectionResult));
}
void CaptivePortalMetricsTabHelper::TestForCaptivePortal(
const 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), 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;
}
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "components/security_interstitials/core/ssl_error_ui.h" #include "components/security_interstitials/core/ssl_error_ui.h"
#include "ios/chrome/browser/ssl/captive_portal_detector_tab_helper.h" #include "ios/chrome/browser/ssl/captive_portal_detector_tab_helper.h"
#include "ios/chrome/browser/ssl/captive_portal_features.h" #include "ios/chrome/browser/ssl/captive_portal_features.h"
#include "ios/chrome/browser/ssl/captive_portal_metrics.h"
#include "ios/chrome/browser/ssl/ios_captive_portal_blocking_page.h" #include "ios/chrome/browser/ssl/ios_captive_portal_blocking_page.h"
#include "ios/chrome/browser/ssl/ios_ssl_blocking_page.h" #include "ios/chrome/browser/ssl/ios_ssl_blocking_page.h"
#include "ios/web/public/browser_state.h" #include "ios/web/public/browser_state.h"
...@@ -26,16 +27,6 @@ ...@@ -26,16 +27,6 @@
DEFINE_WEB_STATE_USER_DATA_KEY(IOSSSLErrorHandler); DEFINE_WEB_STATE_USER_DATA_KEY(IOSSSLErrorHandler);
// Enum used to record the captive portal detection result.
enum class CaptivePortalStatus {
UNKNOWN = 0,
OFFLINE = 1,
ONLINE = 2,
PORTAL = 3,
PROXY_AUTH_REQUIRED = 4,
COUNT
};
const char kSessionDetectionResultHistogram[] = const char kSessionDetectionResultHistogram[] =
"CaptivePortal.Session.DetectionResult"; "CaptivePortal.Session.DetectionResult";
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include "ios/chrome/browser/reading_list/reading_list_model_factory.h" #include "ios/chrome/browser/reading_list/reading_list_model_factory.h"
#import "ios/chrome/browser/reading_list/reading_list_web_state_observer.h" #import "ios/chrome/browser/reading_list/reading_list_web_state_observer.h"
#import "ios/chrome/browser/sessions/ios_chrome_session_tab_helper.h" #import "ios/chrome/browser/sessions/ios_chrome_session_tab_helper.h"
#import "ios/chrome/browser/ssl/captive_portal_metrics_tab_helper.h"
#import "ios/chrome/browser/ssl/insecure_input_tab_helper.h" #import "ios/chrome/browser/ssl/insecure_input_tab_helper.h"
#import "ios/chrome/browser/ssl/ios_security_state_tab_helper.h" #import "ios/chrome/browser/ssl/ios_security_state_tab_helper.h"
#import "ios/chrome/browser/store_kit/store_kit_tab_helper.h" #import "ios/chrome/browser/store_kit/store_kit_tab_helper.h"
...@@ -76,6 +77,7 @@ void AttachTabHelpers(web::WebState* web_state) { ...@@ -76,6 +77,7 @@ void AttachTabHelpers(web::WebState* web_state) {
PagePlaceholderTabHelper::CreateForWebState(web_state, tab); PagePlaceholderTabHelper::CreateForWebState(web_state, tab);
HistoryTabHelper::CreateForWebState(web_state); HistoryTabHelper::CreateForWebState(web_state);
LoadTimingTabHelper::CreateForWebState(web_state); LoadTimingTabHelper::CreateForWebState(web_state);
CaptivePortalMetricsTabHelper::CreateForWebState(web_state);
ReadingListModel* model = ReadingListModel* model =
ReadingListModelFactory::GetForBrowserState(browser_state); ReadingListModelFactory::GetForBrowserState(browser_state);
......
...@@ -8424,6 +8424,26 @@ http://cs/file:chrome/histograms.xml - but prefer this file for new entries. ...@@ -8424,6 +8424,26 @@ http://cs/file:chrome/histograms.xml - but prefer this file for new entries.
</summary> </summary>
</histogram> </histogram>
<histogram name="CaptivePortal.Session.SecureConnectionFailed"
enum="CaptivePortalStatus">
<owner>michaeldo@chromium.org</owner>
<summary>
The result of captive portal detection attempts performed in user session.
Detection result is recorded when portal detection is completed for a failed
secure connection.
</summary>
</histogram>
<histogram name="CaptivePortal.Session.TimeoutDetectionResult"
enum="CaptivePortalStatus">
<owner>michaeldo@chromium.org</owner>
<summary>
The result of captive portal detection attempts performed in user session.
Detection result is recorded when portal detection is completed for a
request which timed out or which is likely to timeout.
</summary>
</histogram>
<histogram name="Cast.Channel.Certificate" enum="CastCertificateStatus"> <histogram name="Cast.Channel.Certificate" enum="CastCertificateStatus">
<owner>ryanchung@google.com</owner> <owner>ryanchung@google.com</owner>
<summary> <summary>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment