1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/safe_browsing/content/triggers/ad_popup_trigger.h"
6
7 #include <string>
8
9 #include "base/bind.h"
10 #include "base/feature_list.h"
11 #include "base/memory/ptr_util.h"
12 #include "base/metrics/field_trial_params.h"
13 #include "base/metrics/histogram_macros.h"
14 #include "base/rand_util.h"
15 #include "base/single_thread_task_runner.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/task/post_task.h"
18 #include "components/safe_browsing/content/triggers/trigger_util.h"
19 #include "components/safe_browsing/core/features.h"
20 #include "components/safe_browsing/core/triggers/trigger_manager.h"
21 #include "components/safe_browsing/core/triggers/trigger_throttler.h"
22 #include "components/security_interstitials/content/unsafe_resource_util.h"
23 #include "components/security_interstitials/core/unsafe_resource.h"
24 #include "content/public/browser/browser_task_traits.h"
25 #include "content/public/browser/browser_thread.h"
26 #include "content/public/browser/navigation_handle.h"
27 #include "content/public/browser/render_frame_host.h"
28 #include "content/public/browser/render_process_host.h"
29 #include "content/public/browser/web_contents.h"
30 #include "services/network/public/cpp/shared_url_loader_factory.h"
31 #include "services/network/public/cpp/simple_url_loader.h"
32
33 namespace safe_browsing {
34
35 namespace {
36
37 // Number of milliseconds to allow data collection to run before sending a
38 // report (since this trigger runs in the background).
39 const int64_t kAdPopupCollectionPeriodMilliseconds = 5000;
40
41 // Range of number of milliseconds to wait after a page finished loading before
42 // starting a report. Allows ads which load in the background to finish loading.
43 const int64_t kMaxAdPopupCollectionStartDelayMilliseconds = 5000;
44 const int64_t kMinAdPopupCollectionStartDelayMilliseconds = 500;
45
RecordAdPopupTriggerAction(AdPopupTriggerAction action)46 void RecordAdPopupTriggerAction(AdPopupTriggerAction action) {
47 UMA_HISTOGRAM_ENUMERATION(kAdPopupTriggerActionMetricName, action);
48 }
49
50 } // namespace
51
52 // Metric for tracking what the Ad Popup trigger does on each navigation.
53 const char kAdPopupTriggerActionMetricName[] =
54 "SafeBrowsing.Triggers.AdPopup.Action";
55
AdPopupTrigger(content::WebContents * web_contents,TriggerManager * trigger_manager,PrefService * prefs,scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,history::HistoryService * history_service)56 AdPopupTrigger::AdPopupTrigger(
57 content::WebContents* web_contents,
58 TriggerManager* trigger_manager,
59 PrefService* prefs,
60 scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
61 history::HistoryService* history_service)
62 : web_contents_(web_contents),
63 start_report_delay_ms_(
64 base::RandInt(kMinAdPopupCollectionStartDelayMilliseconds,
65 kMaxAdPopupCollectionStartDelayMilliseconds)),
66 finish_report_delay_ms_(kAdPopupCollectionPeriodMilliseconds),
67 trigger_manager_(trigger_manager),
68 prefs_(prefs),
69 url_loader_factory_(url_loader_factory),
70 history_service_(history_service),
71 task_runner_(
72 base::CreateSingleThreadTaskRunner({content::BrowserThread::UI})) {}
73
~AdPopupTrigger()74 AdPopupTrigger::~AdPopupTrigger() {}
75
76 // static
CreateForWebContents(content::WebContents * web_contents,TriggerManager * trigger_manager,PrefService * prefs,scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,history::HistoryService * history_service)77 void AdPopupTrigger::CreateForWebContents(
78 content::WebContents* web_contents,
79 TriggerManager* trigger_manager,
80 PrefService* prefs,
81 scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
82 history::HistoryService* history_service) {
83 DCHECK(web_contents);
84 if (!FromWebContents(web_contents)) {
85 web_contents->SetUserData(UserDataKey(),
86 base::WrapUnique(new AdPopupTrigger(
87 web_contents, trigger_manager, prefs,
88 url_loader_factory, history_service)));
89 }
90 }
91
CreateAdPopupReport()92 void AdPopupTrigger::CreateAdPopupReport() {
93 SBErrorOptions error_options =
94 TriggerManager::GetSBErrorDisplayOptions(*prefs_, web_contents_);
95 security_interstitials::UnsafeResource resource;
96 resource.threat_type = SB_THREAT_TYPE_BLOCKED_AD_POPUP;
97 resource.url = web_contents_->GetURL();
98 resource.web_contents_getter = security_interstitials::GetWebContentsGetter(
99 web_contents_->GetMainFrame()->GetProcess()->GetID(),
100 web_contents_->GetMainFrame()->GetRoutingID());
101 TriggerManagerReason reason = TriggerManagerReason::NO_REASON;
102 if (!trigger_manager_->StartCollectingThreatDetailsWithReason(
103 TriggerType::AD_POPUP, web_contents_, resource, url_loader_factory_,
104 history_service_, error_options, &reason)) {
105 if (reason == TriggerManagerReason::DAILY_QUOTA_EXCEEDED) {
106 RecordAdPopupTriggerAction(
107 AdPopupTriggerAction::POPUP_DAILY_QUOTA_EXCEEDED);
108 } else {
109 RecordAdPopupTriggerAction(
110 AdPopupTriggerAction::POPUP_COULD_NOT_START_REPORT);
111 }
112 return;
113 }
114 // Call into TriggerManager to finish the reports after a short delay. Any
115 // ads that are detected during this delay will be rejected by TriggerManager
116 // because a report is already being collected, so we won't send multiple
117 // reports for the same page.
118 task_runner_->PostDelayedTask(
119 FROM_HERE,
120 base::BindOnce(
121 IgnoreResult(&TriggerManager::FinishCollectingThreatDetails),
122 base::Unretained(trigger_manager_), TriggerType::AD_POPUP,
123 base::Unretained(web_contents_), base::TimeDelta(),
124 /*did_proceed=*/false, /*num_visits=*/0, error_options),
125 base::TimeDelta::FromMilliseconds(finish_report_delay_ms_));
126
127 RecordAdPopupTriggerAction(AdPopupTriggerAction::POPUP_REPORTED);
128 }
129
PopupWasBlocked(content::RenderFrameHost * render_frame)130 void AdPopupTrigger::PopupWasBlocked(content::RenderFrameHost* render_frame) {
131 RecordAdPopupTriggerAction(AdPopupTriggerAction::POPUP_CHECK);
132 if (!DetectGoogleAd(render_frame, web_contents_->GetURL())) {
133 RecordAdPopupTriggerAction(AdPopupTriggerAction::POPUP_NO_GOOGLE_AD);
134 return;
135 }
136 // Create a report after a short delay. The delay gives more time for ads to
137 // finish loading in the background. This is best-effort.
138 task_runner_->PostDelayedTask(
139 FROM_HERE,
140 base::BindOnce(&AdPopupTrigger::CreateAdPopupReport,
141 weak_ptr_factory_.GetWeakPtr()),
142 base::TimeDelta::FromMilliseconds(start_report_delay_ms_));
143 }
144
SetTaskRunnerForTest(scoped_refptr<base::SingleThreadTaskRunner> task_runner)145 void AdPopupTrigger::SetTaskRunnerForTest(
146 scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
147 task_runner_ = task_runner;
148 }
149
150 WEB_CONTENTS_USER_DATA_KEY_IMPL(AdPopupTrigger)
151
152 } // namespace safe_browsing
153