Switch global error menu icon to vectorized MD asset
[chromium-blink-merge.git] / chrome / browser / safe_browsing / srt_fetcher_win.cc
blob66e4b5c9f3e9549ad56cd3d6b09710f5f8b3fd40
1 // Copyright 2015 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.
5 #include "chrome/browser/safe_browsing/srt_fetcher_win.h"
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/callback_helpers.h"
10 #include "base/command_line.h"
11 #include "base/files/file_path.h"
12 #include "base/metrics/field_trial.h"
13 #include "base/metrics/sparse_histogram.h"
14 #include "base/prefs/pref_service.h"
15 #include "base/process/launch.h"
16 #include "base/task_runner_util.h"
17 #include "base/threading/thread_checker.h"
18 #include "base/time/time.h"
19 #include "chrome/browser/browser_process.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/profiles/profile_io_data.h"
22 #include "chrome/browser/safe_browsing/srt_field_trial_win.h"
23 #include "chrome/browser/safe_browsing/srt_global_error_win.h"
24 #include "chrome/browser/ui/browser_finder.h"
25 #include "chrome/browser/ui/browser_list.h"
26 #include "chrome/browser/ui/browser_list_observer.h"
27 #include "chrome/browser/ui/global_error/global_error_service.h"
28 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
29 #include "components/component_updater/pref_names.h"
30 #include "components/variations/net/variations_http_header_provider.h"
31 #include "content/public/browser/browser_thread.h"
32 #include "net/base/load_flags.h"
33 #include "net/http/http_status_code.h"
34 #include "net/url_request/url_fetcher.h"
35 #include "net/url_request/url_fetcher_delegate.h"
36 #include "net/url_request/url_request_context_getter.h"
38 using content::BrowserThread;
40 namespace safe_browsing {
42 namespace {
44 // Overrides for the reporter launcher and prompt triggers free function, used
45 // by tests.
46 ReporterLauncher g_reporter_launcher_;
47 PromptTrigger g_prompt_trigger_;
49 void DisplaySRTPrompt(const base::FilePath& download_path) {
50 // Find the last active browser, which may be NULL, in which case we won't
51 // show the prompt this time and will wait until the next run of the
52 // reporter. We can't use other ways of finding a browser because we don't
53 // have a profile.
54 chrome::HostDesktopType desktop_type = chrome::GetActiveDesktop();
55 Browser* browser = chrome::FindLastActiveWithHostDesktopType(desktop_type);
56 if (!browser)
57 return;
59 Profile* profile = browser->profile();
60 DCHECK(profile);
62 // Make sure we have a tabbed browser since we need to anchor the bubble to
63 // the toolbar's wrench menu. Create one if none exist already.
64 if (browser->type() != Browser::TYPE_TABBED) {
65 browser = chrome::FindTabbedBrowser(profile, false, desktop_type);
66 if (!browser)
67 browser = new Browser(Browser::CreateParams(profile, desktop_type));
69 GlobalErrorService* global_error_service =
70 GlobalErrorServiceFactory::GetForProfile(profile);
71 SRTGlobalError* global_error =
72 new SRTGlobalError(global_error_service, download_path);
74 // Ownership of |global_error| is passed to the service. The error removes
75 // itself from the service and self-destructs when done.
76 global_error_service->AddGlobalError(global_error);
78 bool show_bubble = true;
79 PrefService* local_state = g_browser_process->local_state();
80 if (local_state && local_state->GetBoolean(prefs::kSwReporterPendingPrompt)) {
81 // Don't show the bubble if there's already a pending prompt to only be
82 // sown in the Chrome menu.
83 RecordReporterStepHistogram(SW_REPORTER_ADDED_TO_MENU);
84 show_bubble = false;
85 } else {
86 // Do not try to show bubble if another GlobalError is already showing
87 // one. The bubble will be shown once the others have been dismissed.
88 for (GlobalError* error : global_error_service->errors()) {
89 if (error->GetBubbleView()) {
90 show_bubble = false;
91 break;
95 if (show_bubble)
96 global_error->ShowBubbleView(browser);
99 // This function is called from a worker thread to launch the SwReporter and
100 // wait for termination to collect its exit code. This task could be
101 // interrupted by a shutdown at any time, so it shouldn't depend on anything
102 // external that could be shut down beforehand.
103 int LaunchAndWaitForExit(const base::FilePath& exe_path,
104 const std::string& version) {
105 if (!g_reporter_launcher_.is_null())
106 return g_reporter_launcher_.Run(exe_path, version);
107 base::Process reporter_process =
108 base::LaunchProcess(exe_path.value(), base::LaunchOptions());
109 // This exit code is used to identify that a reporter run didn't happen, so
110 // the result should be ignored and a rerun scheduled for the usual delay.
111 int exit_code = kReporterFailureExitCode;
112 if (reporter_process.IsValid()) {
113 RecordReporterStepHistogram(SW_REPORTER_START_EXECUTION);
114 bool success = reporter_process.WaitForExit(&exit_code);
115 DCHECK(success);
116 } else {
117 RecordReporterStepHistogram(SW_REPORTER_FAILED_TO_START);
119 return exit_code;
122 // Class that will attempt to download the SRT, showing the SRT notification
123 // bubble when the download operation is complete. Instances of SRTFetcher own
124 // themselves, they will self-delete on completion of the network request when
125 // OnURLFetchComplete is called.
126 class SRTFetcher : public net::URLFetcherDelegate {
127 public:
128 explicit SRTFetcher(Profile* profile)
129 : profile_(profile),
130 url_fetcher_(net::URLFetcher::Create(0,
131 GURL(GetSRTDownloadURL()),
132 net::URLFetcher::GET,
133 this)) {
134 url_fetcher_->SetLoadFlags(net::LOAD_DISABLE_CACHE);
135 url_fetcher_->SetMaxRetriesOn5xx(3);
136 url_fetcher_->SaveResponseToTemporaryFile(
137 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
138 url_fetcher_->SetRequestContext(
139 g_browser_process->system_request_context());
140 // Adds the UMA bit to the download request if the user is enrolled in UMA.
141 ProfileIOData* io_data = ProfileIOData::FromResourceContext(
142 profile_->GetResourceContext());
143 net::HttpRequestHeaders headers;
144 variations::VariationsHttpHeaderProvider::GetInstance()->AppendHeaders(
145 url_fetcher_->GetOriginalURL(),
146 io_data->IsOffTheRecord(),
147 io_data->GetMetricsEnabledStateOnIOThread(),
148 &headers);
149 url_fetcher_->SetExtraRequestHeaders(headers.ToString());
150 url_fetcher_->Start();
153 // net::URLFetcherDelegate:
154 void OnURLFetchComplete(const net::URLFetcher* source) override {
155 // Take ownership of the fetcher in this scope (source == url_fetcher_).
156 DCHECK_EQ(url_fetcher_.get(), source);
158 base::FilePath download_path;
159 if (source->GetStatus().is_success() &&
160 source->GetResponseCode() == net::HTTP_OK) {
161 if (source->GetResponseAsFilePath(true, &download_path)) {
162 DCHECK(!download_path.empty());
166 // As long as the fetch didn't fail due to HTTP_NOT_FOUND, show a prompt
167 // (either offering the tool directly or pointing to the download page).
168 // If the fetch failed to find the file, don't prompt the user since the
169 // tool is not currently available.
170 // TODO(mad): Consider implementing another layer of retries / alternate
171 // fetching mechanisms. http://crbug.com/460293
172 // TODO(mad): In the event the browser is closed before the prompt displays,
173 // we will wait until the next scanner run to re-display it.
174 // Improve this. http://crbug.com/460295
175 if (source->GetResponseCode() != net::HTTP_NOT_FOUND)
176 DisplaySRTPrompt(download_path);
177 else
178 RecordSRTPromptHistogram(SRT_PROMPT_DOWNLOAD_UNAVAILABLE);
180 // Explicitly destroy the url_fetcher_ to avoid destruction races.
181 url_fetcher_.reset();
183 // At this point, the url_fetcher_ is gone and this SRTFetcher instance is
184 // no longer needed.
185 delete this;
188 private:
189 ~SRTFetcher() override {}
191 // The user profile.
192 Profile* profile_;
194 // The underlying URL fetcher. The instance is alive from construction through
195 // OnURLFetchComplete.
196 scoped_ptr<net::URLFetcher> url_fetcher_;
198 DISALLOW_COPY_AND_ASSIGN(SRTFetcher);
201 // Try to fetch the SRT, and on success, show the prompt to run it.
202 void MaybeFetchSRT(Browser* browser, const std::string& reporter_version) {
203 DCHECK_CURRENTLY_ON(BrowserThread::UI);
205 if (!g_prompt_trigger_.is_null()) {
206 g_prompt_trigger_.Run(browser, reporter_version);
207 return;
209 Profile* profile = browser->profile();
210 DCHECK(profile);
211 PrefService* prefs = profile->GetPrefs();
212 DCHECK(prefs);
214 // Don't show the prompt again if it's been shown before for this profile
215 // and for the current variations seed, unless there's a pending prompt to
216 // show in the Chrome menu.
217 std::string incoming_seed = GetIncomingSRTSeed();
218 std::string old_seed = prefs->GetString(prefs::kSwReporterPromptSeed);
219 PrefService* local_state = g_browser_process->local_state();
220 bool pending_prompt =
221 local_state && local_state->GetBoolean(prefs::kSwReporterPendingPrompt);
222 if (!incoming_seed.empty() && incoming_seed == old_seed && !pending_prompt) {
223 RecordReporterStepHistogram(SW_REPORTER_ALREADY_PROMPTED);
224 return;
227 if (!incoming_seed.empty())
228 prefs->SetString(prefs::kSwReporterPromptSeed, incoming_seed);
229 prefs->SetString(prefs::kSwReporterPromptVersion, reporter_version);
231 // Download the SRT.
232 RecordReporterStepHistogram(SW_REPORTER_DOWNLOAD_START);
234 // All the work happens in the self-deleting class below.
235 new SRTFetcher(profile);
238 // This class tries to run the reporter and reacts to its exit code. It
239 // schedules subsequent runs as needed, or retries as soon as a browser is
240 // available when none is on first try.
241 class ReporterRunner : public chrome::BrowserListObserver {
242 public:
243 // Starts the sequence of attempts to run the reporter.
244 static void Run(
245 const base::FilePath& exe_path,
246 const std::string& version,
247 const scoped_refptr<base::TaskRunner>& main_thread_task_runner,
248 const scoped_refptr<base::TaskRunner>& blocking_task_runner) {
249 if (!instance_)
250 instance_ = new ReporterRunner;
251 DCHECK(instance_->thread_checker_.CalledOnValidThread());
252 // There's nothing to do if the path and version of the reporter has not
253 // changed, we just keep running the tasks that are running now.
254 if (instance_->exe_path_ == exe_path && instance_->version_ == version)
255 return;
256 bool first_run = instance_->exe_path_.empty();
258 instance_->exe_path_ = exe_path;
259 instance_->version_ = version;
260 instance_->main_thread_task_runner_ = main_thread_task_runner;
261 instance_->blocking_task_runner_ = blocking_task_runner;
263 if (first_run)
264 instance_->TryToRun();
267 private:
268 ReporterRunner() {}
269 ~ReporterRunner() override {}
271 // BrowserListObserver.
272 void OnBrowserSetLastActive(Browser* browser) override {}
273 void OnBrowserRemoved(Browser* browser) override {}
274 void OnBrowserAdded(Browser* browser) override {
275 DCHECK(thread_checker_.CalledOnValidThread());
276 DCHECK(browser);
277 if (browser->host_desktop_type() == chrome::GetActiveDesktop()) {
278 MaybeFetchSRT(browser, version_);
279 BrowserList::RemoveObserver(this);
283 // This method is called on the UI thread when the reporter run has completed.
284 // This is run as a task posted from an interruptible worker thread so should
285 // be resilient to unexpected shutdown.
286 void ReporterDone(int exit_code) {
287 DCHECK(thread_checker_.CalledOnValidThread());
289 // Don't continue when the reporter process failed to launch, but still try
290 // again after the regular delay. It's not worth retrying earlier, risking
291 // running too often if it always fails, since not many users fail here.
292 main_thread_task_runner_->PostDelayedTask(
293 FROM_HERE,
294 base::Bind(&ReporterRunner::TryToRun, base::Unretained(this)),
295 base::TimeDelta::FromDays(days_between_reporter_runs_));
296 if (exit_code == kReporterFailureExitCode)
297 return;
299 UMA_HISTOGRAM_SPARSE_SLOWLY("SoftwareReporter.ExitCode", exit_code);
300 PrefService* local_state = g_browser_process->local_state();
301 if (local_state) {
302 local_state->SetInteger(prefs::kSwReporterLastExitCode, exit_code);
303 local_state->SetInt64(prefs::kSwReporterLastTimeTriggered,
304 base::Time::Now().ToInternalValue());
307 if (!IsInSRTPromptFieldTrialGroups()) {
308 // Knowing about disabled field trial is more important than reporter not
309 // finding anything to remove, so check this case first.
310 RecordReporterStepHistogram(SW_REPORTER_NO_PROMPT_FIELD_TRIAL);
311 return;
314 if (exit_code != kSwReporterPostRebootCleanupNeeded &&
315 exit_code != kSwReporterCleanupNeeded) {
316 RecordReporterStepHistogram(SW_REPORTER_NO_PROMPT_NEEDED);
317 return;
320 // Find the last active browser, which may be NULL, in which case we need
321 // to wait for one to be available. We can't use other ways of finding a
322 // browser because we don't have a profile. And we need a browser to get to
323 // a profile, which we need, to tell whether we should prompt or not.
324 // TODO(mad): crbug.com/503269, investigate whether we should change how we
325 // decide when it's time to download the SRT and when to display the prompt.
326 chrome::HostDesktopType desktop_type = chrome::GetActiveDesktop();
327 Browser* browser = chrome::FindLastActiveWithHostDesktopType(desktop_type);
328 if (!browser) {
329 RecordReporterStepHistogram(SW_REPORTER_NO_BROWSER);
330 BrowserList::AddObserver(this);
331 } else {
332 MaybeFetchSRT(browser, version_);
336 void TryToRun() {
337 DCHECK(thread_checker_.CalledOnValidThread());
338 PrefService* local_state = g_browser_process->local_state();
339 if (version_.empty() || !local_state) {
340 DCHECK(exe_path_.empty());
341 return;
344 // Run the reporter if it hasn't been triggered in the last
345 // |days_between_reporter_runs_| days, which depends if there is a pending
346 // prompt to be added to Chrome's menu.
347 if (local_state->GetBoolean(prefs::kSwReporterPendingPrompt)) {
348 days_between_reporter_runs_ = kDaysBetweenSwReporterRunsForPendingPrompt;
349 safe_browsing::RecordReporterStepHistogram(
350 safe_browsing::SW_REPORTER_RAN_DAILY);
351 } else {
352 days_between_reporter_runs_ = kDaysBetweenSuccessfulSwReporterRuns;
354 const base::Time last_time_triggered = base::Time::FromInternalValue(
355 local_state->GetInt64(prefs::kSwReporterLastTimeTriggered));
356 base::TimeDelta next_trigger_delay(
357 last_time_triggered +
358 base::TimeDelta::FromDays(days_between_reporter_runs_) -
359 base::Time::Now());
360 if (next_trigger_delay.ToInternalValue() <= 0 ||
361 // Also make sure the kSwReporterLastTimeTriggered value is not set in
362 // the future.
363 last_time_triggered > base::Time::Now()) {
364 // It's OK to simply |PostTaskAndReplyWithResult| so that
365 // |LaunchAndWaitForExit| doesn't need to access
366 // |main_thread_task_runner_| since the callback is not delayed and the
367 // test task runner won't need to force it.
368 base::PostTaskAndReplyWithResult(
369 blocking_task_runner_.get(), FROM_HERE,
370 base::Bind(&LaunchAndWaitForExit, exe_path_, version_),
371 base::Bind(&ReporterRunner::ReporterDone, base::Unretained(this)));
372 } else {
373 main_thread_task_runner_->PostDelayedTask(
374 FROM_HERE,
375 base::Bind(&ReporterRunner::TryToRun, base::Unretained(this)),
376 next_trigger_delay);
380 base::FilePath exe_path_;
381 std::string version_;
382 scoped_refptr<base::TaskRunner> main_thread_task_runner_;
383 scoped_refptr<base::TaskRunner> blocking_task_runner_;
385 // This value is used to identify how long to wait before starting a new run
386 // of the reporter. It's initialized with the default value and may be changed
387 // to a different value when a prompt is pending and the reporter should be
388 // run before adding the global error to the Chrome menu.
389 int days_between_reporter_runs_ = kDaysBetweenSuccessfulSwReporterRuns;
391 // A single leaky instance.
392 static ReporterRunner* instance_;
394 base::ThreadChecker thread_checker_;
395 DISALLOW_COPY_AND_ASSIGN(ReporterRunner);
398 ReporterRunner* ReporterRunner::instance_ = nullptr;
400 } // namespace
402 void RunSwReporter(
403 const base::FilePath& exe_path,
404 const std::string& version,
405 const scoped_refptr<base::TaskRunner>& main_thread_task_runner,
406 const scoped_refptr<base::TaskRunner>& blocking_task_runner) {
407 ReporterRunner::Run(exe_path, version, main_thread_task_runner,
408 blocking_task_runner);
411 void SetReporterLauncherForTesting(const ReporterLauncher& reporter_launcher) {
412 g_reporter_launcher_ = reporter_launcher;
415 void SetPromptTriggerForTesting(const PromptTrigger& prompt_trigger) {
416 g_prompt_trigger_ = prompt_trigger;
419 } // namespace safe_browsing