blob: df23e61d65bd62cb5f35d78ebea52f472f753dac [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.
#include "chrome/browser/net/loading_predictor_observer.h"
#include <memory>
#include <string>
#include <utility>
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/resource_request_info.h"
#include "net/url_request/url_request.h"
#include "url/gurl.h"
namespace content {
class WebContents;
}
using content::BrowserThread;
using predictors::LoadingPredictor;
using predictors::LoadingDataCollector;
using predictors::URLRequestSummary;
namespace {
// Enum for measuring statistics pertaining to observed request, responses and
// redirects.
enum RequestStats {
REQUEST_STATS_TOTAL_RESPONSES = 0,
REQUEST_STATS_TOTAL_PROCESSED_RESPONSES = 1,
REQUEST_STATS_NO_RESOURCE_REQUEST_INFO = 2, // Not recorded (never was).
REQUEST_STATS_NO_RENDER_FRAME_ID_FROM_REQUEST_INFO = 3, // Not recorded.
REQUEST_STATS_MAX = 4,
};
// Specific to main frame requests.
enum MainFrameRequestStats {
MAIN_FRAME_REQUEST_STATS_TOTAL_REQUESTS = 0,
MAIN_FRAME_REQUEST_STATS_PROCESSED_REQUESTS = 1,
MAIN_FRAME_REQUEST_STATS_TOTAL_REDIRECTS = 2,
MAIN_FRAME_REQUEST_STATS_PROCESSED_REDIRECTS = 3,
MAIN_FRAME_REQUEST_STATS_TOTAL_RESPONSES = 4,
MAIN_FRAME_REQUEST_STATS_PROCESSED_RESPONSES = 5,
MAIN_FRAME_REQUEST_STATS_MAX = 6,
};
void ReportRequestStats(RequestStats stat) {
UMA_HISTOGRAM_ENUMERATION("ResourcePrefetchPredictor.RequestStats", stat,
REQUEST_STATS_MAX);
}
void ReportMainFrameRequestStats(MainFrameRequestStats stat) {
UMA_HISTOGRAM_ENUMERATION("ResourcePrefetchPredictor.MainFrameRequestStats",
stat, MAIN_FRAME_REQUEST_STATS_MAX);
}
bool TryToFillNavigationID(
predictors::NavigationID* navigation_id,
const content::ResourceRequestInfo::WebContentsGetter& web_contents_getter,
const GURL& main_frame_url,
const base::TimeTicks& creation_time) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
content::WebContents* web_contents = web_contents_getter.Run();
if (!web_contents)
return false;
*navigation_id =
predictors::NavigationID(web_contents, main_frame_url, creation_time);
// A WebContents might be associated with something that is not a tab.
// In this case tab_id will be -1 and is_valid() will return false.
return navigation_id->is_valid();
}
} // namespace
namespace chrome_browser_net {
LoadingPredictorObserver::LoadingPredictorObserver(LoadingPredictor* predictor)
: predictor_(predictor->GetWeakPtr()) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
}
LoadingPredictorObserver::~LoadingPredictorObserver() {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
BrowserThread::CurrentlyOn(BrowserThread::IO));
}
void LoadingPredictorObserver::OnRequestStarted(
net::URLRequest* request,
content::ResourceType resource_type,
const content::ResourceRequestInfo::WebContentsGetter&
web_contents_getter) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (resource_type == content::RESOURCE_TYPE_MAIN_FRAME)
ReportMainFrameRequestStats(MAIN_FRAME_REQUEST_STATS_TOTAL_REQUESTS);
if (!LoadingDataCollector::ShouldRecordRequest(request, resource_type))
return;
auto summary = base::MakeUnique<URLRequestSummary>();
summary->resource_url = request->original_url();
summary->resource_type = resource_type;
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::BindOnce(&LoadingPredictorObserver::OnRequestStartedOnUIThread,
base::Unretained(this), base::Passed(std::move(summary)),
web_contents_getter, request->site_for_cookies(),
request->creation_time()));
if (resource_type == content::RESOURCE_TYPE_MAIN_FRAME)
ReportMainFrameRequestStats(MAIN_FRAME_REQUEST_STATS_PROCESSED_REQUESTS);
}
void LoadingPredictorObserver::OnRequestRedirected(
net::URLRequest* request,
const GURL& redirect_url,
const content::ResourceRequestInfo::WebContentsGetter&
web_contents_getter) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
const content::ResourceRequestInfo* request_info =
content::ResourceRequestInfo::ForRequest(request);
if (request_info &&
request_info->GetResourceType() == content::RESOURCE_TYPE_MAIN_FRAME) {
ReportMainFrameRequestStats(MAIN_FRAME_REQUEST_STATS_TOTAL_REDIRECTS);
}
if (!LoadingDataCollector::ShouldRecordRedirect(request))
return;
auto summary = base::MakeUnique<URLRequestSummary>();
if (!URLRequestSummary::SummarizeResponse(*request, summary.get())) {
return;
}
summary->redirect_url = redirect_url;
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::BindOnce(&LoadingPredictorObserver::OnRequestRedirectedOnUIThread,
base::Unretained(this), base::Passed(std::move(summary)),
web_contents_getter, request->site_for_cookies(),
request->creation_time()));
if (request_info &&
request_info->GetResourceType() == content::RESOURCE_TYPE_MAIN_FRAME) {
ReportMainFrameRequestStats(MAIN_FRAME_REQUEST_STATS_PROCESSED_REDIRECTS);
}
}
void LoadingPredictorObserver::OnResponseStarted(
net::URLRequest* request,
const content::ResourceRequestInfo::WebContentsGetter&
web_contents_getter) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
ReportRequestStats(REQUEST_STATS_TOTAL_RESPONSES);
const content::ResourceRequestInfo* request_info =
content::ResourceRequestInfo::ForRequest(request);
if (request_info &&
request_info->GetResourceType() == content::RESOURCE_TYPE_MAIN_FRAME) {
ReportMainFrameRequestStats(MAIN_FRAME_REQUEST_STATS_TOTAL_RESPONSES);
}
if (!LoadingDataCollector::ShouldRecordResponse(request))
return;
auto summary = base::MakeUnique<URLRequestSummary>();
if (!URLRequestSummary::SummarizeResponse(*request, summary.get())) {
return;
}
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::BindOnce(&LoadingPredictorObserver::OnResponseStartedOnUIThread,
base::Unretained(this), base::Passed(std::move(summary)),
web_contents_getter, request->site_for_cookies(),
request->creation_time()));
ReportRequestStats(REQUEST_STATS_TOTAL_PROCESSED_RESPONSES);
if (request_info &&
request_info->GetResourceType() == content::RESOURCE_TYPE_MAIN_FRAME) {
ReportMainFrameRequestStats(MAIN_FRAME_REQUEST_STATS_PROCESSED_RESPONSES);
}
}
void LoadingPredictorObserver::OnRequestStartedOnUIThread(
std::unique_ptr<URLRequestSummary> summary,
const content::ResourceRequestInfo::WebContentsGetter& web_contents_getter,
const GURL& main_frame_url,
const base::TimeTicks& creation_time) const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!TryToFillNavigationID(&summary->navigation_id, web_contents_getter,
main_frame_url, creation_time)) {
return;
}
if (summary->resource_type == content::RESOURCE_TYPE_MAIN_FRAME)
predictor_->OnMainFrameRequest(*summary);
predictor_->loading_data_collector()->RecordURLRequest(*summary);
}
void LoadingPredictorObserver::OnRequestRedirectedOnUIThread(
std::unique_ptr<URLRequestSummary> summary,
const content::ResourceRequestInfo::WebContentsGetter& web_contents_getter,
const GURL& main_frame_url,
const base::TimeTicks& creation_time) const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!TryToFillNavigationID(&summary->navigation_id, web_contents_getter,
main_frame_url, creation_time)) {
return;
}
if (summary->resource_type == content::RESOURCE_TYPE_MAIN_FRAME)
predictor_->OnMainFrameRedirect(*summary);
predictor_->loading_data_collector()->RecordURLRedirect(*summary);
}
void LoadingPredictorObserver::OnResponseStartedOnUIThread(
std::unique_ptr<URLRequestSummary> summary,
const content::ResourceRequestInfo::WebContentsGetter& web_contents_getter,
const GURL& main_frame_url,
const base::TimeTicks& creation_time) const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!TryToFillNavigationID(&summary->navigation_id, web_contents_getter,
main_frame_url, creation_time)) {
return;
}
if (summary->resource_type == content::RESOURCE_TYPE_MAIN_FRAME)
predictor_->OnMainFrameResponse(*summary);
predictor_->loading_data_collector()->RecordURLResponse(*summary);
}
} // namespace chrome_browser_net