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/redirect_info.h"
18 #include "net/url_request/url_request.h"
20 // Maximum time in milliseconds to wait for the safe browsing service to
21 // verify a URL. After this amount of time the outstanding check will be
22 // aborted, and the URL will be treated as if it were safe.
23 static const int kCheckUrlTimeoutMs
= 5000;
25 // TODO(eroman): Downgrade these CHECK()s to DCHECKs once there is more
26 // unit test coverage.
28 SafeBrowsingResourceThrottle::SafeBrowsingResourceThrottle(
29 const net::URLRequest
* request
,
30 content::ResourceType resource_type
,
31 SafeBrowsingService
* safe_browsing
,
33 : defer_at_start_(defer_at_start
),
35 defer_state_(DEFERRED_NONE
),
36 threat_type_(SB_THREAT_TYPE_SAFE
),
37 database_manager_(safe_browsing
->database_manager()),
38 ui_manager_(safe_browsing
->ui_manager()),
40 is_subresource_(resource_type
!= content::RESOURCE_TYPE_MAIN_FRAME
),
41 is_subframe_(resource_type
== content::RESOURCE_TYPE_SUB_FRAME
) {
44 SafeBrowsingResourceThrottle::~SafeBrowsingResourceThrottle() {
45 if (state_
== STATE_CHECKING_URL
)
46 database_manager_
->CancelCheck(this);
49 void SafeBrowsingResourceThrottle::WillStartRequest(bool* defer
) {
50 // We need to check the new URL before starting the request.
51 if (CheckUrl(request_
->url()))
57 // If the URL couldn't be verified synchronously, defer starting the
58 // request until the check has completed.
59 defer_state_
= DEFERRED_START
;
60 defer_start_time_
= base::TimeTicks::Now();
64 void SafeBrowsingResourceThrottle::WillProcessResponse(bool* defer
) {
65 CHECK_EQ(defer_state_
, DEFERRED_NONE
);
69 if (state_
== STATE_CHECKING_URL
||
70 state_
== STATE_DISPLAYING_BLOCKING_PAGE
) {
71 defer_state_
= DEFERRED_PROCESSING
;
72 defer_start_time_
= base::TimeTicks::Now();
77 void SafeBrowsingResourceThrottle::WillRedirectRequest(
78 const net::RedirectInfo
& redirect_info
,
80 CHECK_EQ(defer_state_
, DEFERRED_NONE
);
82 // Prev check completed and was safe.
83 if (state_
== STATE_NONE
) {
84 // Save the redirect urls for possible malware detail reporting later.
85 redirect_urls_
.push_back(redirect_info
.new_url
);
87 // We need to check the new URL before following the redirect.
88 if (CheckUrl(redirect_info
.new_url
))
90 defer_state_
= DEFERRED_REDIRECT
;
92 CHECK(state_
== STATE_CHECKING_URL
||
93 state_
== STATE_DISPLAYING_BLOCKING_PAGE
);
94 // We can't check this new URL until we have finished checking
95 // the prev one, or resumed from the blocking page.
96 unchecked_redirect_url_
= redirect_info
.new_url
;
97 defer_state_
= DEFERRED_UNCHECKED_REDIRECT
;
100 defer_start_time_
= base::TimeTicks::Now();
104 const char* SafeBrowsingResourceThrottle::GetNameForLogging() const {
105 return "SafeBrowsingResourceThrottle";
108 // SafeBrowsingService::Client implementation, called on the IO thread once
109 // the URL has been classified.
110 void SafeBrowsingResourceThrottle::OnCheckBrowseUrlResult(
112 SBThreatType threat_type
,
113 const std::string
& metadata
) {
114 CHECK_EQ(state_
, STATE_CHECKING_URL
);
115 CHECK_EQ(url
, url_being_checked_
);
117 timer_
.Stop(); // Cancel the timeout timer.
118 threat_type_
= threat_type
;
121 if (threat_type
== SB_THREAT_TYPE_SAFE
) {
122 if (defer_state_
!= DEFERRED_NONE
) {
123 // Log how much time the safe browsing check cost us.
124 ui_manager_
->LogPauseDelay(base::TimeTicks::Now() - defer_start_time_
);
127 ui_manager_
->LogPauseDelay(base::TimeDelta());
132 if (request_
->load_flags() & net::LOAD_PREFETCH
) {
133 // Don't prefetch resources that fail safe browsing, disallow them.
134 controller()->Cancel();
138 const content::ResourceRequestInfo
* info
=
139 content::ResourceRequestInfo::ForRequest(request_
);
141 SafeBrowsingUIManager::UnsafeResource resource
;
143 resource
.original_url
= request_
->original_url();
144 resource
.redirect_urls
= redirect_urls_
;
145 resource
.is_subresource
= is_subresource_
;
146 resource
.is_subframe
= is_subframe_
;
147 resource
.threat_type
= threat_type
;
148 resource
.threat_metadata
= metadata
;
149 resource
.callback
= base::Bind(
150 &SafeBrowsingResourceThrottle::OnBlockingPageComplete
, AsWeakPtr());
151 resource
.render_process_host_id
= info
->GetChildID();
152 resource
.render_view_id
= info
->GetRouteID();
154 state_
= STATE_DISPLAYING_BLOCKING_PAGE
;
156 content::BrowserThread::PostTask(
157 content::BrowserThread::UI
,
159 base::Bind(&SafeBrowsingResourceThrottle::StartDisplayingBlockingPage
,
160 AsWeakPtr(), ui_manager_
, resource
));
163 void SafeBrowsingResourceThrottle::StartDisplayingBlockingPage(
164 const base::WeakPtr
<SafeBrowsingResourceThrottle
>& throttle
,
165 scoped_refptr
<SafeBrowsingUIManager
> ui_manager
,
166 const SafeBrowsingUIManager::UnsafeResource
& resource
) {
167 content::RenderViewHost
* rvh
= content::RenderViewHost::FromID(
168 resource
.render_process_host_id
, resource
.render_view_id
);
170 content::WebContents
* web_contents
=
171 content::WebContents::FromRenderViewHost(rvh
);
172 prerender::PrerenderContents
* prerender_contents
=
173 prerender::PrerenderContents::FromWebContents(web_contents
);
175 if (prerender_contents
) {
176 prerender_contents
->Destroy(prerender::FINAL_STATUS_SAFE_BROWSING
);
178 ui_manager
->DisplayBlockingPage(resource
);
183 // Tab is gone or it's being prerendered.
184 content::BrowserThread::PostTask(
185 content::BrowserThread::IO
,
187 base::Bind(&SafeBrowsingResourceThrottle::Cancel
, throttle
));
190 void SafeBrowsingResourceThrottle::Cancel() {
191 controller()->Cancel();
194 // SafeBrowsingService::UrlCheckCallback implementation, called on the IO
195 // thread when the user has decided to proceed with the current request, or
197 void SafeBrowsingResourceThrottle::OnBlockingPageComplete(bool proceed
) {
198 CHECK_EQ(state_
, STATE_DISPLAYING_BLOCKING_PAGE
);
202 threat_type_
= SB_THREAT_TYPE_SAFE
;
203 if (defer_state_
!= DEFERRED_NONE
) {
207 controller()->Cancel();
211 bool SafeBrowsingResourceThrottle::CheckUrl(const GURL
& url
) {
212 CHECK_EQ(state_
, STATE_NONE
);
213 bool succeeded_synchronously
= database_manager_
->CheckBrowseUrl(url
, this);
214 if (succeeded_synchronously
) {
215 threat_type_
= SB_THREAT_TYPE_SAFE
;
216 ui_manager_
->LogPauseDelay(base::TimeDelta()); // No delay.
220 state_
= STATE_CHECKING_URL
;
221 url_being_checked_
= url
;
223 // Start a timer to abort the check if it takes too long.
224 // TODO(nparker): Set this only when we defer, based on remaining time,
225 // so we don't cancel earlier than necessary.
226 timer_
.Start(FROM_HERE
,
227 base::TimeDelta::FromMilliseconds(kCheckUrlTimeoutMs
),
228 this, &SafeBrowsingResourceThrottle::OnCheckUrlTimeout
);
233 void SafeBrowsingResourceThrottle::OnCheckUrlTimeout() {
234 CHECK_EQ(state_
, STATE_CHECKING_URL
);
236 database_manager_
->CancelCheck(this);
237 OnCheckBrowseUrlResult(
238 url_being_checked_
, SB_THREAT_TYPE_SAFE
, std::string());
241 void SafeBrowsingResourceThrottle::ResumeRequest() {
242 CHECK_EQ(state_
, STATE_NONE
);
243 CHECK_NE(defer_state_
, DEFERRED_NONE
);
246 if (defer_state_
== DEFERRED_UNCHECKED_REDIRECT
) {
247 // Save the redirect urls for possible malware detail reporting later.
248 redirect_urls_
.push_back(unchecked_redirect_url_
);
249 if (!CheckUrl(unchecked_redirect_url_
)) {
250 // We're now waiting for the unchecked_redirect_url_.
251 defer_state_
= DEFERRED_REDIRECT
;
257 defer_state_
= DEFERRED_NONE
;
258 controller()->Resume();