ExtensionSyncService: listen for relevant changes instead of being explicitly called...
[chromium-blink-merge.git] / chrome / browser / renderer_host / safe_browsing_resource_throttle.cc
blobef89752b863186ea01b54db20e6fe5525e68cfd8
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 // static
29 SafeBrowsingResourceThrottle* SafeBrowsingResourceThrottle::MaybeCreate(
30 net::URLRequest* request,
31 content::ResourceType resource_type,
32 SafeBrowsingService* sb_service) {
33 #if defined(SAFE_BROWSING_DB_LOCAL)
34 // Throttle consults a local database before starting the resource request.
35 return new SafeBrowsingResourceThrottle(request, resource_type, sb_service,
36 true /* defer_at_start */);
37 #elif defined(SAFE_BROWSING_DB_REMOTE)
38 if (sb_service->IsAndroidFieldTrialEnabled()) {
39 // Throttle consults a remote database before processing the response.
40 return new SafeBrowsingResourceThrottle(request, resource_type, sb_service,
41 false /* defer_at_start */);
42 } else {
43 return nullptr;
45 #else
46 #error "Incompatible compile flags for safe_browsing_resource_throttle"
47 #endif
50 SafeBrowsingResourceThrottle::SafeBrowsingResourceThrottle(
51 const net::URLRequest* request,
52 content::ResourceType resource_type,
53 SafeBrowsingService* sb_service,
54 bool defer_at_start)
55 : defer_at_start_(defer_at_start),
56 state_(STATE_NONE),
57 defer_state_(DEFERRED_NONE),
58 threat_type_(SB_THREAT_TYPE_SAFE),
59 database_manager_(sb_service->database_manager()),
60 ui_manager_(sb_service->ui_manager()),
61 request_(request),
62 is_subresource_(resource_type != content::RESOURCE_TYPE_MAIN_FRAME),
63 is_subframe_(resource_type == content::RESOURCE_TYPE_SUB_FRAME) {
66 SafeBrowsingResourceThrottle::~SafeBrowsingResourceThrottle() {
67 if (state_ == STATE_CHECKING_URL)
68 database_manager_->CancelCheck(this);
71 void SafeBrowsingResourceThrottle::WillStartRequest(bool* defer) {
72 // We need to check the new URL before starting the request.
73 if (CheckUrl(request_->url()))
74 return;
76 if (!defer_at_start_)
77 return;
79 // If the URL couldn't be verified synchronously, defer starting the
80 // request until the check has completed.
81 defer_state_ = DEFERRED_START;
82 defer_start_time_ = base::TimeTicks::Now();
83 *defer = true;
86 void SafeBrowsingResourceThrottle::WillProcessResponse(bool* defer) {
87 CHECK_EQ(defer_state_, DEFERRED_NONE);
88 if (defer_at_start_)
89 return;
91 if (state_ == STATE_CHECKING_URL ||
92 state_ == STATE_DISPLAYING_BLOCKING_PAGE) {
93 defer_state_ = DEFERRED_PROCESSING;
94 defer_start_time_ = base::TimeTicks::Now();
95 *defer = true;
99 void SafeBrowsingResourceThrottle::WillRedirectRequest(
100 const net::RedirectInfo& redirect_info,
101 bool* defer) {
102 CHECK_EQ(defer_state_, DEFERRED_NONE);
104 // Prev check completed and was safe.
105 if (state_ == STATE_NONE) {
106 // Save the redirect urls for possible malware detail reporting later.
107 redirect_urls_.push_back(redirect_info.new_url);
109 // We need to check the new URL before following the redirect.
110 if (CheckUrl(redirect_info.new_url))
111 return;
112 defer_state_ = DEFERRED_REDIRECT;
113 } else {
114 CHECK(state_ == STATE_CHECKING_URL ||
115 state_ == STATE_DISPLAYING_BLOCKING_PAGE);
116 // We can't check this new URL until we have finished checking
117 // the prev one, or resumed from the blocking page.
118 unchecked_redirect_url_ = redirect_info.new_url;
119 defer_state_ = DEFERRED_UNCHECKED_REDIRECT;
122 defer_start_time_ = base::TimeTicks::Now();
123 *defer = true;
126 const char* SafeBrowsingResourceThrottle::GetNameForLogging() const {
127 return "SafeBrowsingResourceThrottle";
130 // SafeBrowsingService::Client implementation, called on the IO thread once
131 // the URL has been classified.
132 void SafeBrowsingResourceThrottle::OnCheckBrowseUrlResult(
133 const GURL& url,
134 SBThreatType threat_type,
135 const std::string& metadata) {
136 CHECK_EQ(state_, STATE_CHECKING_URL);
137 CHECK_EQ(url, url_being_checked_);
139 timer_.Stop(); // Cancel the timeout timer.
140 threat_type_ = threat_type;
141 state_ = STATE_NONE;
143 if (threat_type == SB_THREAT_TYPE_SAFE) {
144 if (defer_state_ != DEFERRED_NONE) {
145 // Log how much time the safe browsing check cost us.
146 ui_manager_->LogPauseDelay(base::TimeTicks::Now() - defer_start_time_);
147 ResumeRequest();
148 } else {
149 ui_manager_->LogPauseDelay(base::TimeDelta());
151 return;
154 if (request_->load_flags() & net::LOAD_PREFETCH) {
155 // Don't prefetch resources that fail safe browsing, disallow them.
156 controller()->Cancel();
157 return;
160 const content::ResourceRequestInfo* info =
161 content::ResourceRequestInfo::ForRequest(request_);
163 SafeBrowsingUIManager::UnsafeResource resource;
164 resource.url = url;
165 resource.original_url = request_->original_url();
166 resource.redirect_urls = redirect_urls_;
167 resource.is_subresource = is_subresource_;
168 resource.is_subframe = is_subframe_;
169 resource.threat_type = threat_type;
170 resource.threat_metadata = metadata;
171 resource.callback = base::Bind(
172 &SafeBrowsingResourceThrottle::OnBlockingPageComplete, AsWeakPtr());
173 resource.render_process_host_id = info->GetChildID();
174 resource.render_view_id = info->GetRouteID();
175 resource.threat_source = SafeBrowsingUIManager::FROM_DEVICE;
177 state_ = STATE_DISPLAYING_BLOCKING_PAGE;
179 content::BrowserThread::PostTask(
180 content::BrowserThread::UI,
181 FROM_HERE,
182 base::Bind(&SafeBrowsingResourceThrottle::StartDisplayingBlockingPage,
183 AsWeakPtr(), ui_manager_, resource));
186 void SafeBrowsingResourceThrottle::StartDisplayingBlockingPage(
187 const base::WeakPtr<SafeBrowsingResourceThrottle>& throttle,
188 scoped_refptr<SafeBrowsingUIManager> ui_manager,
189 const SafeBrowsingUIManager::UnsafeResource& resource) {
190 content::RenderViewHost* rvh = content::RenderViewHost::FromID(
191 resource.render_process_host_id, resource.render_view_id);
192 if (rvh) {
193 content::WebContents* web_contents =
194 content::WebContents::FromRenderViewHost(rvh);
195 prerender::PrerenderContents* prerender_contents =
196 prerender::PrerenderContents::FromWebContents(web_contents);
198 if (prerender_contents) {
199 prerender_contents->Destroy(prerender::FINAL_STATUS_SAFE_BROWSING);
200 } else {
201 ui_manager->DisplayBlockingPage(resource);
202 return;
206 // Tab is gone or it's being prerendered.
207 content::BrowserThread::PostTask(
208 content::BrowserThread::IO,
209 FROM_HERE,
210 base::Bind(&SafeBrowsingResourceThrottle::Cancel, throttle));
213 void SafeBrowsingResourceThrottle::Cancel() {
214 controller()->Cancel();
217 // SafeBrowsingService::UrlCheckCallback implementation, called on the IO
218 // thread when the user has decided to proceed with the current request, or
219 // go back.
220 void SafeBrowsingResourceThrottle::OnBlockingPageComplete(bool proceed) {
221 CHECK_EQ(state_, STATE_DISPLAYING_BLOCKING_PAGE);
222 state_ = STATE_NONE;
224 if (proceed) {
225 threat_type_ = SB_THREAT_TYPE_SAFE;
226 if (defer_state_ != DEFERRED_NONE) {
227 ResumeRequest();
229 } else {
230 controller()->Cancel();
234 bool SafeBrowsingResourceThrottle::CheckUrl(const GURL& url) {
235 CHECK_EQ(state_, STATE_NONE);
236 bool succeeded_synchronously = database_manager_->CheckBrowseUrl(url, this);
237 if (succeeded_synchronously) {
238 threat_type_ = SB_THREAT_TYPE_SAFE;
239 ui_manager_->LogPauseDelay(base::TimeDelta()); // No delay.
240 return true;
243 state_ = STATE_CHECKING_URL;
244 url_being_checked_ = url;
246 // Start a timer to abort the check if it takes too long.
247 // TODO(nparker): Set this only when we defer, based on remaining time,
248 // so we don't cancel earlier than necessary.
249 timer_.Start(FROM_HERE,
250 base::TimeDelta::FromMilliseconds(kCheckUrlTimeoutMs),
251 this, &SafeBrowsingResourceThrottle::OnCheckUrlTimeout);
253 return false;
256 void SafeBrowsingResourceThrottle::OnCheckUrlTimeout() {
257 CHECK_EQ(state_, STATE_CHECKING_URL);
259 database_manager_->CancelCheck(this);
260 OnCheckBrowseUrlResult(
261 url_being_checked_, SB_THREAT_TYPE_SAFE, std::string());
264 void SafeBrowsingResourceThrottle::ResumeRequest() {
265 CHECK_EQ(state_, STATE_NONE);
266 CHECK_NE(defer_state_, DEFERRED_NONE);
268 bool resume = true;
269 if (defer_state_ == DEFERRED_UNCHECKED_REDIRECT) {
270 // Save the redirect urls for possible malware detail reporting later.
271 redirect_urls_.push_back(unchecked_redirect_url_);
272 if (!CheckUrl(unchecked_redirect_url_)) {
273 // We're now waiting for the unchecked_redirect_url_.
274 defer_state_ = DEFERRED_REDIRECT;
275 resume = false;
279 if (resume) {
280 defer_state_ = DEFERRED_NONE;
281 controller()->Resume();