| Index: chrome/browser/safe_browsing/incident_reporting/off_domain_inclusion_detector.cc
|
| diff --git a/chrome/browser/safe_browsing/incident_reporting/off_domain_inclusion_detector.cc b/chrome/browser/safe_browsing/incident_reporting/off_domain_inclusion_detector.cc
|
| index b083c2ea4d27769dd31d1915615c3ca780a9a5cc..1deab62cc401720616e05d8558d0396eb2feee64 100644
|
| --- a/chrome/browser/safe_browsing/incident_reporting/off_domain_inclusion_detector.cc
|
| +++ b/chrome/browser/safe_browsing/incident_reporting/off_domain_inclusion_detector.cc
|
| @@ -12,8 +12,12 @@
|
| #include "base/metrics/histogram.h"
|
| #include "base/single_thread_task_runner.h"
|
| #include "base/thread_task_runner_handle.h"
|
| +#include "chrome/browser/history/history_service.h"
|
| +#include "chrome/browser/history/history_service_factory.h"
|
| +#include "chrome/browser/profiles/profile.h"
|
| #include "chrome/browser/safe_browsing/database_manager.h"
|
| #include "content/public/browser/browser_thread.h"
|
| +#include "content/public/browser/render_process_host.h"
|
| #include "content/public/browser/resource_request_info.h"
|
| #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
|
| #include "net/url_request/url_request.h"
|
| @@ -27,10 +31,16 @@ namespace safe_browsing {
|
| // inclusions across threads, this data will be readable even after the
|
| // underlying request has been deleted.
|
| struct OffDomainInclusionDetector::OffDomainInclusionInfo {
|
| - OffDomainInclusionInfo() {}
|
| + OffDomainInclusionInfo()
|
| + : resource_type(content::RESOURCE_TYPE_LAST_TYPE), render_process_id(0) {}
|
|
|
| content::ResourceType resource_type;
|
|
|
| + // ID of the render process from which this inclusion originates, used to
|
| + // figure out which profile this request belongs to (from the UI thread) if
|
| + // later required during this analysis.
|
| + int render_process_id;
|
| +
|
| // The URL of the off-domain inclusion.
|
| GURL request_url;
|
|
|
| @@ -72,8 +82,14 @@ void OffDomainInclusionDetector::OnResourceRequest(
|
| scoped_ptr<OffDomainInclusionInfo> off_domain_inclusion_info(
|
| new OffDomainInclusionInfo);
|
|
|
| + const content::ResourceRequestInfo* resource_request_info =
|
| + content::ResourceRequestInfo::ForRequest(request);
|
| +
|
| off_domain_inclusion_info->resource_type =
|
| - content::ResourceRequestInfo::ForRequest(request)->GetResourceType();
|
| + resource_request_info->GetResourceType();
|
| +
|
| + off_domain_inclusion_info->render_process_id =
|
| + resource_request_info->GetChildID();
|
|
|
| off_domain_inclusion_info->request_url = request->url();
|
|
|
| @@ -87,6 +103,18 @@ void OffDomainInclusionDetector::OnResourceRequest(
|
| base::Passed(&off_domain_inclusion_info)));
|
| }
|
|
|
| +Profile* OffDomainInclusionDetector::ProfileFromRenderProcessId(
|
| + int render_process_id) {
|
| + DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| +
|
| + content::RenderProcessHost* render_process_host =
|
| + content::RenderProcessHost::FromID(render_process_id);
|
| + if (!render_process_host)
|
| + return nullptr;
|
| +
|
| + return Profile::FromBrowserContext(render_process_host->GetBrowserContext());
|
| +}
|
| +
|
| // static
|
| bool OffDomainInclusionDetector::ShouldAnalyzeRequest(
|
| const net::URLRequest* request) {
|
| @@ -146,14 +174,14 @@ void OffDomainInclusionDetector::BeginAnalysis(
|
| // HTTPS => HTTP requests). Consider adding the original referrer to
|
| // ResourceRequestInfo if that's an issue.
|
| ReportAnalysisResult(off_domain_inclusion_info.Pass(),
|
| - AnalysisEvent::EMPTY_MAIN_FRAME_URL);
|
| + AnalysisEvent::ABORT_EMPTY_MAIN_FRAME_URL);
|
| } else {
|
| // There is no reason for the main frame to start loading resources if its
|
| // own URL is invalid but measure this in the wild to make sure.
|
| // TODO(gab): UMA has proven that this never happens, remove this in a
|
| // follow-up CL.
|
| ReportAnalysisResult(off_domain_inclusion_info.Pass(),
|
| - AnalysisEvent::INVALID_MAIN_FRAME_URL);
|
| + AnalysisEvent::ABORT_INVALID_MAIN_FRAME_URL);
|
| }
|
| return;
|
| }
|
| @@ -191,6 +219,68 @@ void OffDomainInclusionDetector::ContinueAnalysisOnWhitelistResult(
|
| ReportAnalysisResult(off_domain_inclusion_info.Pass(),
|
| AnalysisEvent::OFF_DOMAIN_INCLUSION_WHITELISTED);
|
| } else {
|
| + ContinueAnalysisWithHistoryCheck(off_domain_inclusion_info.Pass());
|
| + }
|
| +}
|
| +
|
| +void OffDomainInclusionDetector::ContinueAnalysisWithHistoryCheck(
|
| + scoped_ptr<const OffDomainInclusionInfo> off_domain_inclusion_info) {
|
| + DCHECK(main_thread_task_runner_->BelongsToCurrentThread());
|
| +
|
| + // The |main_thread_task_runner_| is the UI thread in practice currently so
|
| + // this call can be made synchronously, but this would need to be made
|
| + // asynchronous should this ever change.
|
| + DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| + Profile* profile =
|
| + ProfileFromRenderProcessId(off_domain_inclusion_info->render_process_id);
|
| + if (!profile) {
|
| + ReportAnalysisResult(off_domain_inclusion_info.Pass(),
|
| + AnalysisEvent::ABORT_NO_PROFILE);
|
| + return;
|
| + } else if (profile->IsOffTheRecord()) {
|
| + ReportAnalysisResult(off_domain_inclusion_info.Pass(),
|
| + AnalysisEvent::ABORT_INCOGNITO);
|
| + return;
|
| + }
|
| +
|
| + HistoryService* history_service =
|
| + HistoryServiceFactory::GetForProfileWithoutCreating(profile);
|
| +
|
| + if (!history_service) {
|
| + // Should only happen very early during startup, receiving many such reports
|
| + // relative to other report types would indicate that something is wrong.
|
| + ReportAnalysisResult(off_domain_inclusion_info.Pass(),
|
| + AnalysisEvent::ABORT_NO_HISTORY_SERVICE);
|
| + return;
|
| + }
|
| +
|
| + // Need to explicitly copy |request_url| here or this code would depend on
|
| + // parameter evaluation order as |off_domain_inclusion_info| is being handed
|
| + // off.
|
| + const GURL copied_request_url(off_domain_inclusion_info->request_url);
|
| + history_service->GetVisibleVisitCountToHost(
|
| + copied_request_url,
|
| + base::Bind(&OffDomainInclusionDetector::ContinueAnalysisOnHistoryResult,
|
| + weak_ptr_factory_.GetWeakPtr(),
|
| + base::Passed(&off_domain_inclusion_info)),
|
| + &history_task_tracker_);
|
| +}
|
| +
|
| +void OffDomainInclusionDetector::ContinueAnalysisOnHistoryResult(
|
| + scoped_ptr<const OffDomainInclusionInfo> off_domain_inclusion_info,
|
| + bool success,
|
| + int num_visits,
|
| + base::Time /* first_visit_time */) {
|
| + if (!success) {
|
| + ReportAnalysisResult(off_domain_inclusion_info.Pass(),
|
| + AnalysisEvent::ABORT_HISTORY_LOOKUP_FAILED);
|
| + return;
|
| + }
|
| +
|
| + if (num_visits > 0) {
|
| + ReportAnalysisResult(off_domain_inclusion_info.Pass(),
|
| + AnalysisEvent::OFF_DOMAIN_INCLUSION_IN_HISTORY);
|
| + } else {
|
| ReportAnalysisResult(off_domain_inclusion_info.Pass(),
|
| AnalysisEvent::OFF_DOMAIN_INCLUSION_SUSPICIOUS);
|
| }
|
| @@ -207,30 +297,50 @@ void OffDomainInclusionDetector::ReportAnalysisResult(
|
| off_domain_inclusion_info->resource_type,
|
| content::RESOURCE_TYPE_LAST_TYPE);
|
|
|
| + // Log a histogram for the analysis result along with the associated
|
| + // ResourceType.
|
| + std::string histogram_name;
|
| switch (analysis_event) {
|
| case AnalysisEvent::NO_EVENT:
|
| break;
|
| - case AnalysisEvent::EMPTY_MAIN_FRAME_URL:
|
| - UMA_HISTOGRAM_ENUMERATION("SBOffDomainInclusion.EmptyMainFrameURL",
|
| - off_domain_inclusion_info->resource_type,
|
| - content::RESOURCE_TYPE_LAST_TYPE);
|
| + case AnalysisEvent::ABORT_EMPTY_MAIN_FRAME_URL:
|
| + histogram_name = "SBOffDomainInclusion.Abort.EmptyMainFrameURL";
|
| + break;
|
| + case AnalysisEvent::ABORT_INVALID_MAIN_FRAME_URL:
|
| + // TODO(gab): This never occurs in practice, remove the code handling it.
|
| + break;
|
| + case AnalysisEvent::ABORT_NO_PROFILE:
|
| + histogram_name = "SBOffDomainInclusion.Abort.NoProfile";
|
| break;
|
| - case AnalysisEvent::INVALID_MAIN_FRAME_URL:
|
| - UMA_HISTOGRAM_ENUMERATION("SBOffDomainInclusion.InvalidMainFrameURL",
|
| - off_domain_inclusion_info->resource_type,
|
| - content::RESOURCE_TYPE_LAST_TYPE);
|
| + case AnalysisEvent::ABORT_INCOGNITO:
|
| + histogram_name = "SBOffDomainInclusion.Abort.Incognito";
|
| + break;
|
| + case AnalysisEvent::ABORT_NO_HISTORY_SERVICE:
|
| + histogram_name = "SBOffDomainInclusion.Abort.NoHistoryService";
|
| + break;
|
| + case AnalysisEvent::ABORT_HISTORY_LOOKUP_FAILED:
|
| + histogram_name = "SBOffDomainInclusion.Abort.HistoryLookupFailed";
|
| break;
|
| case AnalysisEvent::OFF_DOMAIN_INCLUSION_WHITELISTED:
|
| - UMA_HISTOGRAM_ENUMERATION("SBOffDomainInclusion.Whitelisted",
|
| - off_domain_inclusion_info->resource_type,
|
| - content::RESOURCE_TYPE_LAST_TYPE);
|
| + histogram_name = "SBOffDomainInclusion.Whitelisted";
|
| + break;
|
| + case AnalysisEvent::OFF_DOMAIN_INCLUSION_IN_HISTORY:
|
| + histogram_name = "SBOffDomainInclusion.InHistory";
|
| break;
|
| case AnalysisEvent::OFF_DOMAIN_INCLUSION_SUSPICIOUS:
|
| - UMA_HISTOGRAM_ENUMERATION("SBOffDomainInclusion.Suspicious",
|
| - off_domain_inclusion_info->resource_type,
|
| - content::RESOURCE_TYPE_LAST_TYPE);
|
| + histogram_name = "SBOffDomainInclusion.Suspicious";
|
| break;
|
| }
|
| + if (!histogram_name.empty()) {
|
| + // Expanded from the UMA_HISTOGRAM_ENUMERATION macro.
|
| + base::LinearHistogram::FactoryGet(
|
| + histogram_name,
|
| + 1, // minimum,
|
| + content::RESOURCE_TYPE_LAST_TYPE, // maximum
|
| + content::RESOURCE_TYPE_LAST_TYPE + 1, // bucket_count
|
| + base::HistogramBase::kUmaTargetedHistogramFlag)
|
| + ->Add(off_domain_inclusion_info->resource_type);
|
| + }
|
|
|
| if (!report_analysis_event_callback_.is_null() &&
|
| analysis_event != AnalysisEvent::NO_EVENT) {
|
|
|