Add ICU message format support
[chromium-blink-merge.git] / chrome / browser / safe_browsing / srt_fetcher_win.cc
blob9a9a8a3c6d2a092a090c990213d24d0f663f878e
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/memory/singleton.h"
13 #include "base/metrics/field_trial.h"
14 #include "base/metrics/sparse_histogram.h"
15 #include "base/prefs/pref_service.h"
16 #include "base/process/launch.h"
17 #include "base/threading/worker_pool.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/safe_browsing/srt_field_trial_win.h"
22 #include "chrome/browser/safe_browsing/srt_global_error_win.h"
23 #include "chrome/browser/ui/browser_finder.h"
24 #include "chrome/browser/ui/browser_list.h"
25 #include "chrome/browser/ui/browser_list_observer.h"
26 #include "chrome/browser/ui/global_error/global_error_service.h"
27 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
28 #include "components/component_updater/pref_names.h"
29 #include "content/public/browser/browser_thread.h"
30 #include "net/base/load_flags.h"
31 #include "net/http/http_status_code.h"
32 #include "net/url_request/url_fetcher.h"
33 #include "net/url_request/url_fetcher_delegate.h"
34 #include "net/url_request/url_request_context_getter.h"
36 using content::BrowserThread;
38 namespace safe_browsing {
40 namespace {
42 // The number of days to wait before triggering another sw_reporter run.
43 const int kDaysBetweenSuccessfulSwReporterRuns = 7;
45 void DisplaySRTPrompt(const base::FilePath& download_path) {
46 // Find the last active browser, which may be NULL, in which case we won't
47 // show the prompt this time and will wait until the next run of the
48 // reporter. We can't use other ways of finding a browser because we don't
49 // have a profile.
50 chrome::HostDesktopType desktop_type = chrome::GetActiveDesktop();
51 Browser* browser = chrome::FindLastActiveWithHostDesktopType(desktop_type);
52 if (!browser)
53 return;
55 Profile* profile = browser->profile();
56 DCHECK(profile);
58 // Make sure we have a tabbed browser since we need to anchor the bubble to
59 // the toolbar's wrench menu. Create one if none exist already.
60 if (browser->type() != Browser::TYPE_TABBED) {
61 browser = chrome::FindTabbedBrowser(profile, false, desktop_type);
62 if (!browser)
63 browser = new Browser(Browser::CreateParams(profile, desktop_type));
65 GlobalErrorService* global_error_service =
66 GlobalErrorServiceFactory::GetForProfile(profile);
67 SRTGlobalError* global_error =
68 new SRTGlobalError(global_error_service, download_path);
70 // Ownership of |global_error| is passed to the service. The error removes
71 // itself from the service and self-destructs when done.
72 global_error_service->AddGlobalError(global_error);
74 // Do not try to show bubble if another GlobalError is already showing
75 // one. The bubble will be shown once the others have been dismissed.
76 bool need_to_show_bubble = true;
77 for (GlobalError* error : global_error_service->errors()) {
78 if (error->GetBubbleView()) {
79 need_to_show_bubble = false;
80 break;
83 if (need_to_show_bubble)
84 global_error->ShowBubbleView(browser);
87 // Class that will attempt to download the SRT, showing the SRT notification
88 // bubble when the download operation is complete. Instances of SRTFetcher own
89 // themselves, they will self-delete on completion of the network request when
90 // OnURLFetchComplete is called.
91 class SRTFetcher : public net::URLFetcherDelegate {
92 public:
93 SRTFetcher()
94 : url_fetcher_(net::URLFetcher::Create(0,
95 GURL(GetSRTDownloadURL()),
96 net::URLFetcher::GET,
97 this)) {
98 url_fetcher_->SetLoadFlags(net::LOAD_DISABLE_CACHE);
99 url_fetcher_->SetMaxRetriesOn5xx(3);
100 url_fetcher_->SaveResponseToTemporaryFile(
101 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
102 url_fetcher_->SetRequestContext(
103 g_browser_process->system_request_context());
104 url_fetcher_->Start();
107 // net::URLFetcherDelegate:
108 void OnURLFetchComplete(const net::URLFetcher* source) override {
109 // Take ownership of the fetcher in this scope (source == url_fetcher_).
110 DCHECK_EQ(url_fetcher_.get(), source);
112 base::FilePath download_path;
113 if (source->GetStatus().is_success() &&
114 source->GetResponseCode() == net::HTTP_OK) {
115 if (source->GetResponseAsFilePath(true, &download_path)) {
116 DCHECK(!download_path.empty());
120 // As long as the fetch didn't fail due to HTTP_NOT_FOUND, show a prompt
121 // (either offering the tool directly or pointing to the download page).
122 // If the fetch failed to find the file, don't prompt the user since the
123 // tool is not currently available.
124 // TODO(mad): Consider implementing another layer of retries / alternate
125 // fetching mechanisms. http://crbug.com/460293
126 // TODO(mad): In the event the browser is closed before the prompt displays,
127 // we will wait until the next scanner run to re-display it.
128 // Improve this. http://crbug.com/460295
129 if (source->GetResponseCode() != net::HTTP_NOT_FOUND)
130 DisplaySRTPrompt(download_path);
131 else
132 RecordSRTPromptHistogram(SRT_PROMPT_DOWNLOAD_UNAVAILABLE);
134 // Explicitly destroy the url_fetcher_ to avoid destruction races.
135 url_fetcher_.reset();
137 // At this point, the url_fetcher_ is gone and this SRTFetcher instance is
138 // no longer needed.
139 delete this;
142 private:
143 ~SRTFetcher() override {}
145 // The underlying URL fetcher. The instance is alive from construction through
146 // OnURLFetchComplete.
147 scoped_ptr<net::URLFetcher> url_fetcher_;
149 DISALLOW_COPY_AND_ASSIGN(SRTFetcher);
152 // This class tries to run the reporter and reacts to its exit code. It
153 // schedules subsequent runs as needed, or retries as soon as a browser is
154 // available when none is on first try.
155 class ReporterRunner : public chrome::BrowserListObserver {
156 public:
157 static ReporterRunner* GetInstance() {
158 return Singleton<ReporterRunner,
159 LeakySingletonTraits<ReporterRunner>>::get();
162 // Starts the sequence of attempts to run the reporter.
163 void Run(const base::FilePath& exe_path, const std::string& version) {
164 DCHECK_CURRENTLY_ON(BrowserThread::UI);
165 exe_path_ = exe_path;
166 version_ = version;
167 TryToRun();
170 private:
171 friend struct DefaultSingletonTraits<ReporterRunner>;
172 ReporterRunner() {}
173 ~ReporterRunner() override {}
175 // BrowserListObserver.
176 void OnBrowserSetLastActive(Browser* browser) override {}
177 void OnBrowserRemoved(Browser* browser) override {}
178 void OnBrowserAdded(Browser* browser) override {
179 DCHECK(browser);
180 if (browser->host_desktop_type() == chrome::GetActiveDesktop()) {
181 MaybeFetchSRT(browser);
182 BrowserList::RemoveObserver(this);
186 // Identifies that we completed the run of the reporter and schedule the next
187 // run in kDaysBetweenSuccessfulSwReporterRuns.
188 void CompletedRun() {
189 DCHECK_CURRENTLY_ON(BrowserThread::UI);
190 g_browser_process->local_state()->SetInt64(
191 prefs::kSwReporterLastTimeTriggered,
192 base::Time::Now().ToInternalValue());
194 BrowserThread::PostDelayedTask(
195 BrowserThread::UI, FROM_HERE,
196 base::Bind(&ReporterRunner::TryToRun, base::Unretained(this)),
197 base::TimeDelta::FromDays(kDaysBetweenSuccessfulSwReporterRuns));
200 // This method is called on the UI thread when the reporter run has completed.
201 // This is run as a task posted from an interruptible worker thread so should
202 // be resilient to unexpected shutdown.
203 void ReporterDone(int exit_code) {
204 DCHECK_CURRENTLY_ON(BrowserThread::UI);
205 UMA_HISTOGRAM_SPARSE_SLOWLY("SoftwareReporter.ExitCode", exit_code);
207 if (g_browser_process && g_browser_process->local_state()) {
208 g_browser_process->local_state()->SetInteger(
209 prefs::kSwReporterLastExitCode, exit_code);
212 // To complete the run if we exit without releasing this scoped closure.
213 base::ScopedClosureRunner completed(
214 base::Bind(&ReporterRunner::CompletedRun, base::Unretained(this)));
216 if (!IsInSRTPromptFieldTrialGroups()) {
217 // Knowing about disabled field trial is more important than reporter not
218 // finding anything to remove, so check this case first.
219 RecordReporterStepHistogram(SW_REPORTER_NO_PROMPT_FIELD_TRIAL);
220 return;
223 if (exit_code != kSwReporterPostRebootCleanupNeeded &&
224 exit_code != kSwReporterCleanupNeeded) {
225 RecordReporterStepHistogram(SW_REPORTER_NO_PROMPT_NEEDED);
226 return;
229 // Find the last active browser, which may be NULL, in which case we need
230 // to wait for one to be available. We can't use other ways of finding a
231 // browser because we don't have a profile. And we need a browser to get to
232 // a profile, which we need, to tell whether we should prompt or not.
233 // TODO(mad): crbug.com/503269, investigate whether we should change how we
234 // decide when it's time to download the SRT and when to display the prompt.
235 chrome::HostDesktopType desktop_type = chrome::GetActiveDesktop();
236 Browser* browser = chrome::FindLastActiveWithHostDesktopType(desktop_type);
237 if (!browser) {
238 RecordReporterStepHistogram(SW_REPORTER_NO_BROWSER);
239 BrowserList::AddObserver(GetInstance());
240 } else {
241 MaybeFetchSRT(browser);
245 static void MaybeFetchSRT(Browser* browser) {
246 DCHECK_CURRENTLY_ON(BrowserThread::UI);
248 Profile* profile = browser->profile();
249 DCHECK(profile);
250 PrefService* prefs = profile->GetPrefs();
251 DCHECK(prefs);
253 // Don't show the prompt again if it's been shown before for this profile
254 // and for the current Finch seed.
255 std::string incoming_seed = GetIncomingSRTSeed();
256 std::string old_seed = prefs->GetString(prefs::kSwReporterPromptSeed);
257 if (!incoming_seed.empty() && incoming_seed == old_seed) {
258 RecordReporterStepHistogram(SW_REPORTER_ALREADY_PROMPTED);
259 return;
262 if (!incoming_seed.empty())
263 prefs->SetString(prefs::kSwReporterPromptSeed, incoming_seed);
264 prefs->SetString(prefs::kSwReporterPromptVersion, GetInstance()->version_);
266 // Download the SRT.
267 RecordReporterStepHistogram(SW_REPORTER_DOWNLOAD_START);
269 // All the work happens in the self-deleting class above.
270 new SRTFetcher();
273 // This method is called from a worker thread to launch the SwReporter and
274 // wait for termination to collect its exit code. This task could be
275 // interrupted by a shutdown at anytime, so it shouldn't depend on anything
276 // external that could be shutdown beforehand. It's static to make sure it
277 // won't access data members, except for the need for a this pointer to call
278 // back member functions on the UI thread. We can safely use GetInstance()
279 // here because the singleton is leaky.
280 static void LaunchAndWaitForExit(const base::FilePath& exe_path,
281 const std::string& version) {
282 const base::CommandLine reporter_command_line(exe_path);
283 base::Process reporter_process =
284 base::LaunchProcess(reporter_command_line, base::LaunchOptions());
285 if (!reporter_process.IsValid()) {
286 RecordReporterStepHistogram(SW_REPORTER_FAILED_TO_START);
287 // Don't call ReporterDone when the reporter process failed to launch, but
288 // try again after the regular delay. It's not worth retrying earlier,
289 // risking running too often if it always fail, since not many users fail
290 // here, less than 1%.
291 BrowserThread::PostDelayedTask(
292 BrowserThread::UI, FROM_HERE,
293 base::Bind(&ReporterRunner::TryToRun,
294 base::Unretained(GetInstance())),
295 base::TimeDelta::FromDays(kDaysBetweenSuccessfulSwReporterRuns));
296 return;
298 RecordReporterStepHistogram(SW_REPORTER_START_EXECUTION);
300 int exit_code = -1;
301 bool success = reporter_process.WaitForExit(&exit_code);
302 DCHECK(success);
303 BrowserThread::PostTask(
304 BrowserThread::UI, FROM_HERE,
305 base::Bind(&ReporterRunner::ReporterDone,
306 base::Unretained(GetInstance()), exit_code));
309 void TryToRun() {
310 DCHECK_CURRENTLY_ON(BrowserThread::UI);
311 if (version_.empty()) {
312 DCHECK(exe_path_.empty());
313 return;
316 // Run the reporter if it hasn't been triggered in the
317 // kDaysBetweenSuccessfulSwReporterRuns days.
318 const base::Time last_time_triggered = base::Time::FromInternalValue(
319 g_browser_process->local_state()->GetInt64(
320 prefs::kSwReporterLastTimeTriggered));
321 base::TimeDelta next_trigger_delay(
322 last_time_triggered +
323 base::TimeDelta::FromDays(kDaysBetweenSuccessfulSwReporterRuns) -
324 base::Time::Now());
325 if (next_trigger_delay.ToInternalValue() <= 0) {
326 // Use a worker pool because this task is blocking and may take a long
327 // time to complete.
328 base::WorkerPool::PostTask(
329 FROM_HERE, base::Bind(&ReporterRunner::LaunchAndWaitForExit,
330 exe_path_, version_),
331 true);
332 } else {
333 BrowserThread::PostDelayedTask(
334 BrowserThread::UI, FROM_HERE,
335 base::Bind(&ReporterRunner::TryToRun,
336 base::Unretained(GetInstance())),
337 next_trigger_delay);
341 base::FilePath exe_path_;
342 std::string version_;
344 DISALLOW_COPY_AND_ASSIGN(ReporterRunner);
347 } // namespace
349 void RunSwReporter(const base::FilePath& exe_path, const std::string& version) {
350 DCHECK_CURRENTLY_ON(BrowserThread::UI);
351 ReporterRunner::GetInstance()->Run(exe_path, version);
354 } // namespace safe_browsing