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