1 // Copyright (c) 2012 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/renderer_host/safe_browsing_resource_throttle.h"
7 #include "base/logging.h"
8 #include "chrome/browser/browser_process.h"
9 #include "chrome/browser/prerender/prerender_contents.h"
10 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
11 #include "content/public/browser/browser_thread.h"
12 #include "content/public/browser/render_view_host.h"
13 #include "content/public/browser/resource_controller.h"
14 #include "content/public/browser/resource_request_info.h"
15 #include "content/public/browser/web_contents.h"
16 #include "net/base/load_flags.h"
17 #include "net/url_request/url_request.h"
19 // Maximum time in milliseconds to wait for the safe browsing service to
20 // verify a URL. After this amount of time the outstanding check will be
21 // aborted, and the URL will be treated as if it were safe.
22 static const int kCheckUrlTimeoutMs
= 5000;
24 // TODO(eroman): Downgrade these CHECK()s to DCHECKs once there is more
25 // unit test coverage.
27 SafeBrowsingResourceThrottle::SafeBrowsingResourceThrottle(
28 const net::URLRequest
* request
,
30 SafeBrowsingService
* safe_browsing
)
32 defer_state_(DEFERRED_NONE
),
33 threat_type_(SB_THREAT_TYPE_SAFE
),
34 database_manager_(safe_browsing
->database_manager()),
35 ui_manager_(safe_browsing
->ui_manager()),
37 is_subresource_(is_subresource
) {
40 SafeBrowsingResourceThrottle::~SafeBrowsingResourceThrottle() {
41 if (state_
== STATE_CHECKING_URL
)
42 database_manager_
->CancelCheck(this);
45 void SafeBrowsingResourceThrottle::WillStartRequest(bool* defer
) {
46 // We need to check the new URL before starting the request.
47 if (CheckUrl(request_
->url()))
50 // If the URL couldn't be verified synchronously, defer starting the
51 // request until the check has completed.
52 defer_state_
= DEFERRED_START
;
56 void SafeBrowsingResourceThrottle::WillRedirectRequest(const GURL
& new_url
,
58 CHECK(state_
== STATE_NONE
);
59 CHECK(defer_state_
== DEFERRED_NONE
);
61 // Save the redirect urls for possible malware detail reporting later.
62 redirect_urls_
.push_back(new_url
);
64 // We need to check the new URL before following the redirect.
65 if (CheckUrl(new_url
))
68 // If the URL couldn't be verified synchronously, defer following the
69 // redirect until the SafeBrowsing check is complete. Store the redirect
70 // context so we can pass it on to other handlers once we have completed
72 defer_state_
= DEFERRED_REDIRECT
;
76 const char* SafeBrowsingResourceThrottle::GetNameForLogging() const {
77 return "SafeBrowsingResourceThrottle";
80 // SafeBrowsingService::Client implementation, called on the IO thread once
81 // the URL has been classified.
82 void SafeBrowsingResourceThrottle::OnCheckBrowseUrlResult(
83 const GURL
& url
, SBThreatType threat_type
) {
84 CHECK(state_
== STATE_CHECKING_URL
);
85 CHECK(defer_state_
!= DEFERRED_NONE
);
86 CHECK(url
== url_being_checked_
) << "Was expecting: " << url_being_checked_
87 << " but got: " << url
;
89 #if defined(OS_ANDROID)
90 // Temporarily disable SB interstitial during Finch experiment.
91 // The database check is still exercised, but the interstitial never shown.
92 threat_type
= SB_THREAT_TYPE_SAFE
;
95 timer_
.Stop(); // Cancel the timeout timer.
96 threat_type_
= threat_type
;
99 if (threat_type
== SB_THREAT_TYPE_SAFE
) {
100 // Log how much time the safe browsing check cost us.
101 ui_manager_
->LogPauseDelay(base::TimeTicks::Now() - url_check_start_time_
);
103 // Continue the request.
108 if (request_
->load_flags() & net::LOAD_PREFETCH
) {
109 // Don't prefetch resources that fail safe browsing, disallow
111 controller()->Cancel();
115 const content::ResourceRequestInfo
* info
=
116 content::ResourceRequestInfo::ForRequest(request_
);
118 SafeBrowsingUIManager::UnsafeResource resource
;
120 resource
.original_url
= request_
->original_url();
121 resource
.redirect_urls
= redirect_urls_
;
122 resource
.is_subresource
= is_subresource_
;
123 resource
.threat_type
= threat_type
;
124 resource
.callback
= base::Bind(
125 &SafeBrowsingResourceThrottle::OnBlockingPageComplete
, AsWeakPtr());
126 resource
.render_process_host_id
= info
->GetChildID();
127 resource
.render_view_id
= info
->GetRouteID();
129 state_
= STATE_DISPLAYING_BLOCKING_PAGE
;
131 content::BrowserThread::PostTask(
132 content::BrowserThread::UI
,
134 base::Bind(&SafeBrowsingResourceThrottle::StartDisplayingBlockingPage
,
135 AsWeakPtr(), ui_manager_
, resource
));
138 void SafeBrowsingResourceThrottle::StartDisplayingBlockingPage(
139 const base::WeakPtr
<SafeBrowsingResourceThrottle
>& throttle
,
140 scoped_refptr
<SafeBrowsingUIManager
> ui_manager
,
141 const SafeBrowsingUIManager::UnsafeResource
& resource
) {
142 bool should_show_blocking_page
= true;
144 content::RenderViewHost
* rvh
= content::RenderViewHost::FromID(
145 resource
.render_process_host_id
, resource
.render_view_id
);
147 content::WebContents
* web_contents
=
148 content::WebContents::FromRenderViewHost(rvh
);
149 prerender::PrerenderContents
* prerender_contents
=
150 prerender::PrerenderContents::FromWebContents(web_contents
);
151 if (prerender_contents
) {
152 prerender_contents
->Destroy(prerender::FINAL_STATUS_SAFE_BROWSING
);
153 should_show_blocking_page
= false;
156 if (should_show_blocking_page
) {
157 ui_manager
->DisplayBlockingPage(resource
);
162 // Tab is gone or it's being prerendered.
163 content::BrowserThread::PostTask(
164 content::BrowserThread::IO
,
166 base::Bind(&SafeBrowsingResourceThrottle::Cancel
, throttle
));
169 void SafeBrowsingResourceThrottle::Cancel() {
170 controller()->Cancel();
173 // SafeBrowsingService::UrlCheckCallback implementation, called on the IO
174 // thread when the user has decided to proceed with the current request, or
176 void SafeBrowsingResourceThrottle::OnBlockingPageComplete(bool proceed
) {
177 CHECK(state_
== STATE_DISPLAYING_BLOCKING_PAGE
);
181 threat_type_
= SB_THREAT_TYPE_SAFE
;
184 controller()->Cancel();
188 bool SafeBrowsingResourceThrottle::CheckUrl(const GURL
& url
) {
189 CHECK(state_
== STATE_NONE
);
190 bool succeeded_synchronously
= database_manager_
->CheckBrowseUrl(url
, this);
191 if (succeeded_synchronously
) {
192 threat_type_
= SB_THREAT_TYPE_SAFE
;
193 ui_manager_
->LogPauseDelay(base::TimeDelta()); // No delay.
197 state_
= STATE_CHECKING_URL
;
198 url_being_checked_
= url
;
200 // Record the start time of the check.
201 url_check_start_time_
= base::TimeTicks::Now();
203 // Start a timer to abort the check if it takes too long.
204 timer_
.Start(FROM_HERE
,
205 base::TimeDelta::FromMilliseconds(kCheckUrlTimeoutMs
),
206 this, &SafeBrowsingResourceThrottle::OnCheckUrlTimeout
);
211 void SafeBrowsingResourceThrottle::OnCheckUrlTimeout() {
212 CHECK(state_
== STATE_CHECKING_URL
);
213 CHECK(defer_state_
!= DEFERRED_NONE
);
215 database_manager_
->CancelCheck(this);
216 OnCheckBrowseUrlResult(url_being_checked_
, SB_THREAT_TYPE_SAFE
);
219 void SafeBrowsingResourceThrottle::ResumeRequest() {
220 CHECK(state_
== STATE_NONE
);
221 CHECK(defer_state_
!= DEFERRED_NONE
);
223 defer_state_
= DEFERRED_NONE
;
224 controller()->Resume();