| OLD | NEW | 
|---|
| 1 // Copyright (c) 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. | 
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be | 
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. | 
| 4 | 4 | 
| 5 #include "components/safe_browsing/base_resource_throttle.h" | 5 #include "components/safe_browsing/request_checker.h" | 
|  | 6 | 
|  | 7 #include <memory> | 
|  | 8 #include <utility> | 
| 6 | 9 | 
| 7 #include "base/metrics/histogram_macros.h" | 10 #include "base/metrics/histogram_macros.h" | 
| 8 #include "base/trace_event/trace_event.h" | 11 #include "base/trace_event/trace_event.h" | 
| 9 #include "base/values.h" | 12 #include "base/values.h" | 
| 10 #include "components/safe_browsing/base_ui_manager.h" | 13 #include "components/safe_browsing/base_ui_manager.h" | 
|  | 14 #include "components/safe_browsing_db/database_manager.h" | 
| 11 #include "components/safe_browsing_db/util.h" | 15 #include "components/safe_browsing_db/util.h" | 
| 12 #include "components/security_interstitials/content/unsafe_resource.h" | 16 #include "components/security_interstitials/content/unsafe_resource.h" | 
| 13 #include "content/public/browser/browser_thread.h" | 17 #include "content/public/browser/browser_thread.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" | 18 #include "net/base/load_flags.h" | 
| 17 #include "net/log/net_log_capture_mode.h" | 19 #include "net/log/net_log_capture_mode.h" | 
| 18 #include "net/log/net_log_source.h" | 20 #include "net/log/net_log_source.h" | 
| 19 #include "net/log/net_log_source_type.h" | 21 #include "net/log/net_log_source_type.h" | 
| 20 #include "net/url_request/redirect_info.h" | 22 #include "net/url_request/redirect_info.h" | 
| 21 #include "net/url_request/url_request.h" | 23 #include "net/url_request/url_request.h" | 
| 22 | 24 | 
| 23 using net::NetLogEventType; | 25 using net::NetLogEventType; | 
| 24 using net::NetLogSourceType; | 26 using net::NetLogSourceType; | 
| 25 | 27 | 
| 26 namespace safe_browsing { | 28 namespace safe_browsing { | 
| 27 | 29 | 
| 28 namespace { | 30 namespace { | 
| 29 | 31 | 
| 30 // Maximum time in milliseconds to wait for the safe browsing service to | 32 // Maximum time in milliseconds to wait for the safe browsing service to | 
| 31 // verify a URL. After this amount of time the outstanding check will be | 33 // verify a URL. After this amount of time the outstanding check will be | 
| 32 // aborted, and the URL will be treated as if it were safe. | 34 // aborted, and the URL will be treated as if it were safe. | 
| 33 const int kCheckUrlTimeoutMs = 5000; | 35 // Mutable for testing purposes. | 
|  | 36 int g_check_url_timeout_ms = 5000; | 
| 34 | 37 | 
| 35 // Return a dictionary with "url"=|url-spec| and optionally | 38 // Return a dictionary with "url"=|url-spec| and optionally | 
| 36 // |name|=|value| (if not null), for netlogging. | 39 // |name|=|value| (if not null), for netlogging. | 
| 37 // This will also add a reference to the original request's net_log ID. | 40 // This will also add a reference to the original request's net_log ID. | 
| 38 std::unique_ptr<base::Value> NetLogUrlCallback( | 41 std::unique_ptr<base::Value> NetLogUrlCallback( | 
| 39     const net::URLRequest* request, | 42     const net::URLRequest* request, | 
| 40     const GURL& url, | 43     const GURL& url, | 
| 41     const char* name, | 44     const char* name, | 
| 42     const char* value, | 45     const char* value, | 
| 43     net::NetLogCaptureMode /* capture_mode */) { | 46     net::NetLogCaptureMode /* capture_mode */) { | 
| (...skipping 12 matching lines...) Expand all  Loading... | 
| 56                                                   net::NetLogCaptureMode) { | 59                                                   net::NetLogCaptureMode) { | 
| 57   std::unique_ptr<base::DictionaryValue> event_params( | 60   std::unique_ptr<base::DictionaryValue> event_params( | 
| 58       new base::DictionaryValue()); | 61       new base::DictionaryValue()); | 
| 59   if (name && value) | 62   if (name && value) | 
| 60     event_params->SetString(name, value); | 63     event_params->SetString(name, value); | 
| 61   return std::move(event_params); | 64   return std::move(event_params); | 
| 62 } | 65 } | 
| 63 | 66 | 
| 64 }  // namespace | 67 }  // namespace | 
| 65 | 68 | 
| 66 // TODO(eroman): Downgrade these CHECK()s to DCHECKs once there is more | 69 RequestChecker::RequestChecker( | 
| 67 //               unit test coverage. |  | 
| 68 |  | 
| 69 BaseResourceThrottle::BaseResourceThrottle( |  | 
| 70     const net::URLRequest* request, | 70     const net::URLRequest* request, | 
| 71     content::ResourceType resource_type, | 71     content::ResourceType resource_type, | 
| 72     scoped_refptr<SafeBrowsingDatabaseManager> database_manager, | 72     scoped_refptr<SafeBrowsingDatabaseManager> database_manager, | 
| 73     scoped_refptr<BaseUIManager> ui_manager) | 73     scoped_refptr<BaseUIManager> ui_manager, | 
| 74     : ui_manager_(ui_manager), | 74     Delegate* delegate) | 
| 75       threat_type_(SB_THREAT_TYPE_SAFE), | 75     : state_(STATE_NONE), | 
| 76       database_manager_(database_manager), |  | 
| 77       request_(request), |  | 
| 78       state_(STATE_NONE), |  | 
| 79       defer_state_(DEFERRED_NONE), | 76       defer_state_(DEFERRED_NONE), | 
| 80       resource_type_(resource_type), | 77       resource_type_(resource_type), | 
|  | 78       threat_type_(SB_THREAT_TYPE_SAFE), | 
|  | 79       request_(request), | 
|  | 80       ui_manager_(std::move(ui_manager)), | 
|  | 81       database_manager_(std::move(database_manager)), | 
| 81       net_log_with_source_( | 82       net_log_with_source_( | 
| 82           net::NetLogWithSource::Make(request->net_log().net_log(), | 83           net::NetLogWithSource::Make(request->net_log().net_log(), | 
| 83                                       NetLogSourceType::SAFE_BROWSING)) {} | 84                                       NetLogSourceType::SAFE_BROWSING)), | 
|  | 85       delegate_(std::move(delegate)), | 
|  | 86       weak_factory_(this) {} | 
| 84 | 87 | 
| 85 // static | 88 RequestChecker::~RequestChecker() { | 
| 86 BaseResourceThrottle* BaseResourceThrottle::MaybeCreate( |  | 
| 87     net::URLRequest* request, |  | 
| 88     content::ResourceType resource_type, |  | 
| 89     scoped_refptr<SafeBrowsingDatabaseManager> database_manager, |  | 
| 90     scoped_refptr<BaseUIManager> ui_manager) { |  | 
| 91   if (database_manager->IsSupported()) { |  | 
| 92     return new BaseResourceThrottle(request, resource_type, |  | 
| 93                                     database_manager, ui_manager); |  | 
| 94   } |  | 
| 95   return nullptr; |  | 
| 96 } |  | 
| 97 |  | 
| 98 BaseResourceThrottle::~BaseResourceThrottle() { |  | 
| 99   if (defer_state_ != DEFERRED_NONE) { | 89   if (defer_state_ != DEFERRED_NONE) { | 
| 100     EndNetLogEvent(NetLogEventType::SAFE_BROWSING_DEFERRED, nullptr, nullptr); | 90     EndNetLogEvent(NetLogEventType::SAFE_BROWSING_DEFERRED, nullptr, nullptr); | 
| 101   } | 91   } | 
| 102 | 92 | 
| 103   if (state_ == STATE_CHECKING_URL) { | 93   if (state_ == STATE_CHECKING_URL) { | 
| 104     database_manager_->CancelCheck(this); | 94     database_manager_->CancelCheck(this); | 
| 105     EndNetLogEvent(NetLogEventType::SAFE_BROWSING_CHECKING_URL, "result", | 95     EndNetLogEvent(NetLogEventType::SAFE_BROWSING_CHECKING_URL, "result", | 
| 106                    "request_canceled"); | 96                    "request_canceled"); | 
| 107   } | 97   } | 
| 108 } | 98 } | 
| 109 | 99 | 
| 110 // Note on net_log calls: SAFE_BROWSING_DEFERRED events must be wholly | 100 RequestChecker::DeferDecision RequestChecker::CheckNewRequest() { | 
| 111 // nested within SAFE_BROWSING_CHECKING_URL events.  Synchronous checks |  | 
| 112 // are not logged at all. |  | 
| 113 void BaseResourceThrottle::BeginNetLogEvent(NetLogEventType type, |  | 
| 114                                             const GURL& url, |  | 
| 115                                             const char* name, |  | 
| 116                                             const char* value) { |  | 
| 117   net_log_with_source_.BeginEvent( |  | 
| 118       type, base::Bind(&NetLogUrlCallback, request_, url, name, value)); |  | 
| 119   request_->net_log().AddEvent( |  | 
| 120       type, net_log_with_source_.source().ToEventParametersCallback()); |  | 
| 121 } |  | 
| 122 |  | 
| 123 void BaseResourceThrottle::EndNetLogEvent(NetLogEventType type, |  | 
| 124                                           const char* name, |  | 
| 125                                           const char* value) { |  | 
| 126   net_log_with_source_.EndEvent(type, |  | 
| 127                                 base::Bind(&NetLogStringCallback, name, value)); |  | 
| 128   request_->net_log().AddEvent( |  | 
| 129       type, net_log_with_source_.source().ToEventParametersCallback()); |  | 
| 130 } |  | 
| 131 |  | 
| 132 void BaseResourceThrottle::WillStartRequest(bool* defer) { |  | 
| 133   // We need to check the new URL before starting the request. | 101   // We need to check the new URL before starting the request. | 
| 134   if (CheckUrl(request_->url())) | 102   DeferDecision decision = CheckUrl(request_->url()); | 
| 135     return; | 103   DCHECK_NE(decision, PROCEED_ALWAYS_ASYNC); | 
|  | 104   if (decision != DEFER) | 
|  | 105     return decision; | 
| 136 | 106 | 
| 137   // We let the check run in parallel with resource load only if this | 107   // We let the check run in parallel with resource load only if this | 
| 138   // db_manager only supports asynchronous checks, like on mobile. | 108   // db_manager only supports asynchronous checks, like on mobile. | 
| 139   // Otherwise, we defer now. | 109   // Otherwise, we defer now. | 
| 140   if (database_manager_->ChecksAreAlwaysAsync()) | 110   if (database_manager_->ChecksAreAlwaysAsync()) | 
| 141     return; | 111     return PROCEED_ALWAYS_ASYNC; | 
| 142 | 112 | 
| 143   // If the URL couldn't be verified synchronously, defer starting the | 113   // If the URL couldn't be verified synchronously, defer starting the | 
| 144   // request until the check has completed. | 114   // request until the check has completed. | 
| 145   defer_state_ = DEFERRED_START; | 115   defer_state_ = DEFERRED_START; | 
| 146   defer_start_time_ = base::TimeTicks::Now(); | 116   defer_start_time_ = base::TimeTicks::Now(); | 
| 147   *defer = true; |  | 
| 148   BeginNetLogEvent(NetLogEventType::SAFE_BROWSING_DEFERRED, request_->url(), | 117   BeginNetLogEvent(NetLogEventType::SAFE_BROWSING_DEFERRED, request_->url(), | 
| 149                    "defer_reason", "at_start"); | 118                    "defer_reason", "at_start"); | 
|  | 119   return DEFER; | 
| 150 } | 120 } | 
| 151 | 121 | 
| 152 void BaseResourceThrottle::WillProcessResponse(bool* defer) { | 122 RequestChecker::DeferDecision RequestChecker::CheckRedirect( | 
| 153   CHECK_EQ(defer_state_, DEFERRED_NONE); | 123     const GURL& new_url) { | 
| 154   // TODO(nparker): Maybe remove this check, since it should have no effect. |  | 
| 155   if (!database_manager_->ChecksAreAlwaysAsync()) |  | 
| 156     return; |  | 
| 157 |  | 
| 158   if (state_ == STATE_CHECKING_URL || |  | 
| 159       state_ == STATE_DISPLAYING_BLOCKING_PAGE) { |  | 
| 160     defer_state_ = DEFERRED_PROCESSING; |  | 
| 161     defer_start_time_ = base::TimeTicks::Now(); |  | 
| 162     *defer = true; |  | 
| 163     BeginNetLogEvent(NetLogEventType::SAFE_BROWSING_DEFERRED, request_->url(), |  | 
| 164                      "defer_reason", "at_response"); |  | 
| 165   } |  | 
| 166 } |  | 
| 167 |  | 
| 168 bool BaseResourceThrottle::MustProcessResponseBeforeReadingBody() { |  | 
| 169   // On Android, SafeBrowsing may only decide to cancel the request when the |  | 
| 170   // response has been received. Therefore, no part of it should be cached |  | 
| 171   // until this ResourceThrottle has been able to check the response. This |  | 
| 172   // prevents the following scenario: |  | 
| 173   //   1) A request is made for foo.com which has been hacked. |  | 
| 174   //   2) The request is only canceled at WillProcessResponse stage, but part of |  | 
| 175   //   it has been cached. |  | 
| 176   //   3) foo.com is no longer hacked and removed from the SafeBrowsing list. |  | 
| 177   //   4) The user requests foo.com, which is not on the SafeBrowsing list. This |  | 
| 178   //   is deemed safe. However, the resource is actually served from cache, |  | 
| 179   //   using the version that was previously stored. |  | 
| 180   //   5) This results in the  user accessing an unsafe resource without being |  | 
| 181   //   notified that it's dangerous. |  | 
| 182   // TODO(clamy): Add a browser test that checks this specific scenario. |  | 
| 183   return true; |  | 
| 184 } |  | 
| 185 |  | 
| 186 void BaseResourceThrottle::WillRedirectRequest( |  | 
| 187     const net::RedirectInfo& redirect_info, |  | 
| 188     bool* defer) { |  | 
| 189   CHECK_EQ(defer_state_, DEFERRED_NONE); | 124   CHECK_EQ(defer_state_, DEFERRED_NONE); | 
| 190 | 125 | 
| 191   // Prev check completed and was safe. | 126   // Prev check completed and was safe. | 
| 192   if (state_ == STATE_NONE) { | 127   if (state_ == STATE_NONE) { | 
| 193     // Save the redirect urls for possible malware detail reporting later. | 128     // Save the redirect urls for possible malware detail reporting later. | 
| 194     redirect_urls_.push_back(redirect_info.new_url); | 129     redirect_urls_.push_back(new_url); | 
| 195 | 130 | 
| 196     // We need to check the new URL before following the redirect. | 131     // We need to check the new URL before following the redirect. | 
| 197     if (CheckUrl(redirect_info.new_url)) | 132     DeferDecision decision = CheckUrl(new_url); | 
| 198       return; | 133     if (decision == PROCEED_SAFE) | 
|  | 134       return PROCEED_SAFE; | 
|  | 135     DCHECK_EQ(decision, DEFER); | 
| 199     defer_state_ = DEFERRED_REDIRECT; | 136     defer_state_ = DEFERRED_REDIRECT; | 
| 200   } else { | 137   } else { | 
| 201     CHECK(state_ == STATE_CHECKING_URL || | 138     DCHECK(state_ == STATE_CHECKING_URL || | 
| 202           state_ == STATE_DISPLAYING_BLOCKING_PAGE); | 139            state_ == STATE_DISPLAYING_BLOCKING_PAGE); | 
|  | 140     DCHECK(database_manager_->ChecksAreAlwaysAsync()); | 
| 203     // We can't check this new URL until we have finished checking | 141     // We can't check this new URL until we have finished checking | 
| 204     // the prev one, or resumed from the blocking page. | 142     // the prev one, or resumed from the blocking page. | 
| 205     unchecked_redirect_url_ = redirect_info.new_url; | 143     unchecked_redirect_url_ = new_url; | 
| 206     defer_state_ = DEFERRED_UNCHECKED_REDIRECT; | 144     defer_state_ = DEFERRED_UNCHECKED_REDIRECT; | 
| 207   } | 145   } | 
| 208 | 146 | 
| 209   defer_start_time_ = base::TimeTicks::Now(); | 147   defer_start_time_ = base::TimeTicks::Now(); | 
| 210   *defer = true; |  | 
| 211   BeginNetLogEvent( | 148   BeginNetLogEvent( | 
| 212       NetLogEventType::SAFE_BROWSING_DEFERRED, redirect_info.new_url, | 149       NetLogEventType::SAFE_BROWSING_DEFERRED, new_url, "defer_reason", | 
| 213       "defer_reason", |  | 
| 214       defer_state_ == DEFERRED_REDIRECT ? "redirect" : "unchecked_redirect"); | 150       defer_state_ == DEFERRED_REDIRECT ? "redirect" : "unchecked_redirect"); | 
|  | 151   return DEFER; | 
| 215 } | 152 } | 
| 216 | 153 | 
| 217 const char* BaseResourceThrottle::GetNameForLogging() const { | 154 RequestChecker::DeferDecision RequestChecker::ShouldDeferResponse() { | 
| 218   return "BaseResourceThrottle"; | 155   CHECK_EQ(defer_state_, DEFERRED_NONE); | 
|  | 156   if (state_ == STATE_CHECKING_URL || | 
|  | 157       state_ == STATE_DISPLAYING_BLOCKING_PAGE) { | 
|  | 158     DCHECK(database_manager_->ChecksAreAlwaysAsync()); | 
|  | 159     defer_state_ = DEFERRED_PROCESSING; | 
|  | 160     defer_start_time_ = base::TimeTicks::Now(); | 
|  | 161     BeginNetLogEvent(NetLogEventType::SAFE_BROWSING_DEFERRED, request_->url(), | 
|  | 162                      "defer_reason", "at_response"); | 
|  | 163     return DEFER; | 
|  | 164   } | 
|  | 165 | 
|  | 166   return PROCEED_SAFE; | 
| 219 } | 167 } | 
| 220 | 168 | 
| 221 void BaseResourceThrottle::MaybeDestroyPrerenderContents( |  | 
| 222     const content::ResourceRequestInfo* info) {} |  | 
| 223 |  | 
| 224 // SafeBrowsingService::Client implementation, called on the IO thread once | 169 // SafeBrowsingService::Client implementation, called on the IO thread once | 
| 225 // the URL has been classified. | 170 // the URL has been classified. | 
| 226 void BaseResourceThrottle::OnCheckBrowseUrlResult( | 171 void RequestChecker::OnCheckBrowseUrlResult(const GURL& url, | 
| 227     const GURL& url, | 172                                             SBThreatType threat_type, | 
| 228     SBThreatType threat_type, | 173                                             const ThreatMetadata& metadata) { | 
| 229     const ThreatMetadata& metadata) { | 174   DCHECK_EQ(state_, STATE_CHECKING_URL); | 
| 230   CHECK_EQ(state_, STATE_CHECKING_URL); | 175   DCHECK(url.is_valid()); | 
| 231   CHECK(url.is_valid()); | 176   DCHECK(url_being_checked_.is_valid()); | 
| 232   CHECK(url_being_checked_.is_valid()); | 177   DCHECK_EQ(url, url_being_checked_); | 
| 233   CHECK_EQ(url, url_being_checked_); |  | 
| 234 | 178 | 
| 235   timer_.Stop();  // Cancel the timeout timer. | 179   timer_.Stop();  // Cancel the timeout timer. | 
| 236   threat_type_ = threat_type; | 180   threat_type_ = threat_type; | 
| 237   state_ = STATE_NONE; | 181   state_ = STATE_NONE; | 
| 238 | 182 | 
| 239   if (defer_state_ != DEFERRED_NONE) { | 183   if (defer_state_ != DEFERRED_NONE) { | 
| 240     EndNetLogEvent(NetLogEventType::SAFE_BROWSING_DEFERRED, nullptr, nullptr); | 184     EndNetLogEvent(NetLogEventType::SAFE_BROWSING_DEFERRED, nullptr, nullptr); | 
| 241   } | 185   } | 
| 242   EndNetLogEvent( | 186   EndNetLogEvent(NetLogEventType::SAFE_BROWSING_CHECKING_URL, "result", | 
| 243       NetLogEventType::SAFE_BROWSING_CHECKING_URL, "result", | 187                  threat_type_ == SB_THREAT_TYPE_SAFE ? "safe" : "unsafe"); | 
| 244       threat_type_ == SB_THREAT_TYPE_SAFE ? "safe" : "unsafe"); |  | 
| 245 | 188 | 
| 246   if (threat_type == SB_THREAT_TYPE_SAFE) { | 189   if (threat_type == SB_THREAT_TYPE_SAFE) { | 
| 247     if (defer_state_ != DEFERRED_NONE) { | 190     if (defer_state_ != DEFERRED_NONE) { | 
| 248       // Log how much time the safe browsing check cost us. | 191       // Log how much time the safe browsing check cost us. | 
| 249       ui_manager_->LogPauseDelay(base::TimeTicks::Now() - defer_start_time_); | 192       ui_manager_->LogPauseDelay(base::TimeTicks::Now() - defer_start_time_); | 
| 250       ResumeRequest(); | 193       ResumeRequest(); | 
| 251     } else { | 194     } else { | 
| 252       ui_manager_->LogPauseDelay(base::TimeDelta()); | 195       ui_manager_->LogPauseDelay(base::TimeDelta()); | 
| 253     } | 196     } | 
| 254     return; | 197     return; | 
| 255   } | 198   } | 
| 256 | 199 | 
| 257   const content::ResourceRequestInfo* info = |  | 
| 258       content::ResourceRequestInfo::ForRequest(request_); |  | 
| 259 |  | 
| 260   if (request_->load_flags() & net::LOAD_PREFETCH) { | 200   if (request_->load_flags() & net::LOAD_PREFETCH) { | 
| 261     // Destroy the prefetch with FINAL_STATUS_SAFEBROSWING. | 201     // Destroy the prefetch with FINAL_STATUS_SAFEBROWSING. | 
| 262     if (resource_type_ == content::RESOURCE_TYPE_MAIN_FRAME) { | 202     if (resource_type_ == content::RESOURCE_TYPE_MAIN_FRAME) { | 
| 263       MaybeDestroyPrerenderContents(info); | 203       auto web_contents_getter = | 
|  | 204           delegate_->GetWebContentsGetterForRequest(request_); | 
|  | 205       delegate_->MaybeDestroyPrerenderContents(web_contents_getter); | 
| 264     } | 206     } | 
| 265     // Don't prefetch resources that fail safe browsing, disallow them. | 207     // Don't prefetch resources that fail safe browsing, disallow them. | 
| 266     Cancel(); |  | 
| 267     UMA_HISTOGRAM_ENUMERATION("SB2.ResourceTypes2.UnsafePrefetchCanceled", | 208     UMA_HISTOGRAM_ENUMERATION("SB2.ResourceTypes2.UnsafePrefetchCanceled", | 
| 268                               resource_type_, content::RESOURCE_TYPE_LAST_TYPE); | 209                               resource_type_, content::RESOURCE_TYPE_LAST_TYPE); | 
|  | 210     delegate_->CancelResourceLoad(); | 
|  | 211     // |this| may be deleted here. | 
| 269     return; | 212     return; | 
| 270   } | 213   } | 
| 271 | 214 | 
| 272   UMA_HISTOGRAM_ENUMERATION("SB2.ResourceTypes2.Unsafe", resource_type_, | 215   UMA_HISTOGRAM_ENUMERATION("SB2.ResourceTypes2.Unsafe", resource_type_, | 
| 273                             content::RESOURCE_TYPE_LAST_TYPE); | 216                             content::RESOURCE_TYPE_LAST_TYPE); | 
| 274 | 217 | 
| 275   security_interstitials::UnsafeResource resource; | 218   security_interstitials::UnsafeResource resource; | 
| 276   resource.url = url; | 219   resource.url = url; | 
| 277   resource.original_url = request_->original_url(); | 220   resource.original_url = request_->original_url(); | 
| 278   resource.redirect_urls = redirect_urls_; | 221   resource.redirect_urls = redirect_urls_; | 
| 279   resource.is_subresource = resource_type_ != content::RESOURCE_TYPE_MAIN_FRAME; | 222   resource.is_subresource = resource_type_ != content::RESOURCE_TYPE_MAIN_FRAME; | 
| 280   resource.is_subframe = resource_type_ == content::RESOURCE_TYPE_SUB_FRAME; | 223   resource.is_subframe = resource_type_ == content::RESOURCE_TYPE_SUB_FRAME; | 
| 281   resource.threat_type = threat_type; | 224   resource.threat_type = threat_type; | 
| 282   resource.threat_metadata = metadata; | 225   resource.threat_metadata = metadata; | 
| 283   resource.callback = base::Bind( | 226   resource.callback = base::Bind(&RequestChecker::OnBlockingPageComplete, | 
| 284       &BaseResourceThrottle::OnBlockingPageComplete, AsWeakPtr()); | 227                                  weak_factory_.GetWeakPtr()); | 
| 285   resource.callback_thread = content::BrowserThread::GetTaskRunnerForThread( | 228   resource.callback_thread = content::BrowserThread::GetTaskRunnerForThread( | 
| 286       content::BrowserThread::IO); | 229       content::BrowserThread::IO); | 
| 287   resource.web_contents_getter = info->GetWebContentsGetterForRequest(); | 230   resource.web_contents_getter = | 
|  | 231       delegate_->GetWebContentsGetterForRequest(request_); | 
| 288   resource.threat_source = database_manager_->GetThreatSource(); | 232   resource.threat_source = database_manager_->GetThreatSource(); | 
| 289 | 233 | 
| 290   state_ = STATE_DISPLAYING_BLOCKING_PAGE; | 234   state_ = STATE_DISPLAYING_BLOCKING_PAGE; | 
| 291 | 235 | 
| 292   StartDisplayingBlockingPageHelper(resource); | 236   delegate_->StartDisplayingBlockingPage(std::move(resource), ui_manager_); | 
| 293 } | 237 } | 
| 294 | 238 | 
| 295 void BaseResourceThrottle::StartDisplayingBlockingPageHelper( | 239 void RequestChecker::OnBlockingPageComplete(bool proceed) { | 
| 296     security_interstitials::UnsafeResource resource) { |  | 
| 297   content::BrowserThread::PostTask( |  | 
| 298       content::BrowserThread::UI, FROM_HERE, |  | 
| 299       base::Bind(&BaseResourceThrottle::StartDisplayingBlockingPage, |  | 
| 300                  AsWeakPtr(), ui_manager_, resource)); |  | 
| 301 } |  | 
| 302 |  | 
| 303 // Static |  | 
| 304 void BaseResourceThrottle::StartDisplayingBlockingPage( |  | 
| 305     const base::WeakPtr<BaseResourceThrottle>& throttle, |  | 
| 306     scoped_refptr<BaseUIManager> ui_manager, |  | 
| 307     const security_interstitials::UnsafeResource& resource) { |  | 
| 308   content::WebContents* web_contents = resource.web_contents_getter.Run(); |  | 
| 309   if (web_contents) { |  | 
| 310     ui_manager->DisplayBlockingPage(resource); |  | 
| 311     return; |  | 
| 312   } |  | 
| 313 |  | 
| 314   // Tab is gone or it's being prerendered. |  | 
| 315   content::BrowserThread::PostTask( |  | 
| 316       content::BrowserThread::IO, FROM_HERE, |  | 
| 317       base::Bind(&BaseResourceThrottle::Cancel, throttle)); |  | 
| 318 } |  | 
| 319 |  | 
| 320 void BaseResourceThrottle::OnBlockingPageComplete(bool proceed) { |  | 
| 321   CHECK_EQ(state_, STATE_DISPLAYING_BLOCKING_PAGE); | 240   CHECK_EQ(state_, STATE_DISPLAYING_BLOCKING_PAGE); | 
| 322   state_ = STATE_NONE; | 241   state_ = STATE_NONE; | 
| 323 | 242 | 
| 324   if (proceed) { | 243   if (proceed) { | 
| 325     threat_type_ = SB_THREAT_TYPE_SAFE; | 244     threat_type_ = SB_THREAT_TYPE_SAFE; | 
| 326     if (defer_state_ != DEFERRED_NONE) { | 245     if (defer_state_ != DEFERRED_NONE) { | 
| 327       ResumeRequest(); | 246       ResumeRequest(); | 
| 328     } | 247     } | 
| 329   } else { | 248   } else { | 
| 330     CancelResourceLoad(); | 249     delegate_->CancelResourceLoad(); | 
|  | 250     // |this| may be deleted here. | 
| 331   } | 251   } | 
| 332 } | 252 } | 
| 333 | 253 | 
| 334 void BaseResourceThrottle::CancelResourceLoad() { | 254 RequestChecker::DeferDecision RequestChecker::CheckUrl(const GURL& url) { | 
| 335   Cancel(); | 255   TRACE_EVENT1("loader", "RequestChecker::CheckUrl", "url", url.spec()); | 
| 336 } |  | 
| 337 |  | 
| 338 scoped_refptr<BaseUIManager> BaseResourceThrottle::ui_manager() { |  | 
| 339   return ui_manager_; |  | 
| 340 } |  | 
| 341 |  | 
| 342 bool BaseResourceThrottle::CheckUrl(const GURL& url) { |  | 
| 343   TRACE_EVENT1("loader", "BaseResourceThrottle::CheckUrl", "url", |  | 
| 344                url.spec()); |  | 
| 345   CHECK_EQ(state_, STATE_NONE); | 256   CHECK_EQ(state_, STATE_NONE); | 
| 346   // To reduce aggregate latency on mobile, check only the most dangerous | 257   // To reduce aggregate latency on mobile, check only the most dangerous | 
| 347   // resource types. | 258   // resource types. | 
| 348   if (!database_manager_->CanCheckResourceType(resource_type_)) { | 259   if (!database_manager_->CanCheckResourceType(resource_type_)) { | 
| 349     // TODO(vakh): Consider changing this metric to SafeBrowsing.V4ResourceType | 260     // TODO(vakh): Consider changing this metric to SafeBrowsing.V4ResourceType | 
| 350     // to be consistent with the other PVer4 metrics. | 261     // to be consistent with the other PVer4 metrics. | 
| 351     UMA_HISTOGRAM_ENUMERATION("SB2.ResourceTypes2.Skipped", resource_type_, | 262     UMA_HISTOGRAM_ENUMERATION("SB2.ResourceTypes2.Skipped", resource_type_, | 
| 352                               content::RESOURCE_TYPE_LAST_TYPE); | 263                               content::RESOURCE_TYPE_LAST_TYPE); | 
| 353     return true; | 264     return PROCEED_SKIPPED; | 
| 354   } | 265   } | 
| 355 | 266 | 
| 356   // TODO(vakh): Consider changing this metric to SafeBrowsing.V4ResourceType to | 267   // TODO(vakh): Consider changing this metric to SafeBrowsing.V4ResourceType to | 
| 357   // be consistent with the other PVer4 metrics. | 268   // be consistent with the other PVer4 metrics. | 
| 358   UMA_HISTOGRAM_ENUMERATION("SB2.ResourceTypes2.Checked", resource_type_, | 269   UMA_HISTOGRAM_ENUMERATION("SB2.ResourceTypes2.Checked", resource_type_, | 
| 359                             content::RESOURCE_TYPE_LAST_TYPE); | 270                             content::RESOURCE_TYPE_LAST_TYPE); | 
| 360 | 271 | 
| 361   if (database_manager_->CheckBrowseUrl(url, this)) { | 272   if (database_manager_->CheckBrowseUrl(url, this)) { | 
| 362     threat_type_ = SB_THREAT_TYPE_SAFE; | 273     threat_type_ = SB_THREAT_TYPE_SAFE; | 
| 363     ui_manager_->LogPauseDelay(base::TimeDelta());  // No delay. | 274     ui_manager_->LogPauseDelay(base::TimeDelta());  // No delay. | 
| 364     return true; | 275     return PROCEED_SAFE; | 
| 365   } | 276   } | 
| 366 | 277 | 
| 367   state_ = STATE_CHECKING_URL; | 278   state_ = STATE_CHECKING_URL; | 
| 368   url_being_checked_ = url; | 279   url_being_checked_ = url; | 
| 369   BeginNetLogEvent(NetLogEventType::SAFE_BROWSING_CHECKING_URL, url, nullptr, | 280   BeginNetLogEvent(NetLogEventType::SAFE_BROWSING_CHECKING_URL, url, nullptr, | 
| 370                    nullptr); | 281                    nullptr); | 
| 371 | 282 | 
| 372   // Start a timer to abort the check if it takes too long. | 283   // Start a timer to abort the check if it takes too long. | 
| 373   // TODO(nparker): Set this only when we defer, based on remaining time, | 284   // TODO(nparker): Set this only when we defer, based on remaining time, | 
| 374   // so we don't cancel earlier than necessary. | 285   // so we don't cancel earlier than necessary. | 
| 375   timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kCheckUrlTimeoutMs), | 286   timer_.Start(FROM_HERE, | 
| 376                this, &BaseResourceThrottle::OnCheckUrlTimeout); | 287                base::TimeDelta::FromMilliseconds(g_check_url_timeout_ms), this, | 
|  | 288                &RequestChecker::OnCheckUrlTimeout); | 
| 377 | 289 | 
| 378   return false; | 290   return DEFER; | 
| 379 } | 291 } | 
| 380 | 292 | 
| 381 void BaseResourceThrottle::OnCheckUrlTimeout() { | 293 void RequestChecker::OnCheckUrlTimeout() { | 
| 382   CHECK_EQ(state_, STATE_CHECKING_URL); | 294   CHECK_EQ(state_, STATE_CHECKING_URL); | 
| 383 | 295 | 
| 384   database_manager_->CancelCheck(this); | 296   database_manager_->CancelCheck(this); | 
| 385 | 297 | 
| 386   OnCheckBrowseUrlResult(url_being_checked_, safe_browsing::SB_THREAT_TYPE_SAFE, | 298   OnCheckBrowseUrlResult(url_being_checked_, safe_browsing::SB_THREAT_TYPE_SAFE, | 
| 387                          ThreatMetadata()); | 299                          ThreatMetadata()); | 
| 388 } | 300 } | 
| 389 | 301 | 
| 390 void BaseResourceThrottle::ResumeRequest() { | 302 void RequestChecker::ResumeRequest() { | 
| 391   CHECK_EQ(state_, STATE_NONE); | 303   CHECK_EQ(state_, STATE_NONE); | 
| 392   CHECK_NE(defer_state_, DEFERRED_NONE); | 304   CHECK_NE(defer_state_, DEFERRED_NONE); | 
| 393 | 305 | 
| 394   bool resume = true; | 306   bool resume = true; | 
| 395   if (defer_state_ == DEFERRED_UNCHECKED_REDIRECT) { | 307   if (defer_state_ == DEFERRED_UNCHECKED_REDIRECT) { | 
|  | 308     DCHECK(unchecked_redirect_url_.is_valid()); | 
|  | 309     GURL unchecked_redirect_url; | 
|  | 310     unchecked_redirect_url_.Swap(&unchecked_redirect_url); | 
| 396     // Save the redirect urls for possible malware detail reporting later. | 311     // Save the redirect urls for possible malware detail reporting later. | 
| 397     redirect_urls_.push_back(unchecked_redirect_url_); | 312     redirect_urls_.push_back(unchecked_redirect_url); | 
| 398     if (!CheckUrl(unchecked_redirect_url_)) { | 313     if (!CheckUrl(unchecked_redirect_url)) { | 
| 399       // We're now waiting for the unchecked_redirect_url_. | 314       // We're now waiting for the unchecked_redirect_url. | 
| 400       defer_state_ = DEFERRED_REDIRECT; | 315       defer_state_ = DEFERRED_REDIRECT; | 
| 401       resume = false; | 316       resume = false; | 
| 402       BeginNetLogEvent(NetLogEventType::SAFE_BROWSING_DEFERRED, | 317       BeginNetLogEvent(NetLogEventType::SAFE_BROWSING_DEFERRED, | 
| 403                        unchecked_redirect_url_, "defer_reason", | 318                        unchecked_redirect_url, "defer_reason", | 
| 404                        "resumed_redirect"); | 319                        "resumed_redirect"); | 
| 405     } | 320     } | 
| 406   } | 321   } | 
| 407 | 322 | 
| 408   if (resume) { | 323   if (resume) { | 
| 409     defer_state_ = DEFERRED_NONE; | 324     defer_state_ = DEFERRED_NONE; | 
| 410     Resume(); | 325     delegate_->ResumeResourceRequest(); | 
|  | 326     // |this| may be deleted here. | 
| 411   } | 327   } | 
| 412 } | 328 } | 
| 413 | 329 | 
|  | 330 // Note on net_log calls: SAFE_BROWSING_DEFERRED events must be wholly | 
|  | 331 // nested within SAFE_BROWSING_CHECKING_URL events.  Synchronous checks | 
|  | 332 // are not logged at all. | 
|  | 333 void RequestChecker::BeginNetLogEvent(NetLogEventType type, | 
|  | 334                                       const GURL& url, | 
|  | 335                                       const char* name, | 
|  | 336                                       const char* value) { | 
|  | 337   net_log_with_source_.BeginEvent( | 
|  | 338       type, base::Bind(&NetLogUrlCallback, request_, url, name, value)); | 
|  | 339   request_->net_log().AddEvent( | 
|  | 340       type, net_log_with_source_.source().ToEventParametersCallback()); | 
|  | 341 } | 
|  | 342 | 
|  | 343 void RequestChecker::EndNetLogEvent(NetLogEventType type, | 
|  | 344                                     const char* name, | 
|  | 345                                     const char* value) { | 
|  | 346   net_log_with_source_.EndEvent(type, | 
|  | 347                                 base::Bind(&NetLogStringCallback, name, value)); | 
|  | 348   request_->net_log().AddEvent( | 
|  | 349       type, net_log_with_source_.source().ToEventParametersCallback()); | 
|  | 350 } | 
|  | 351 | 
|  | 352 ScopedTimeoutForTesting::ScopedTimeoutForTesting(int new_timeout_ms) | 
|  | 353     : original_timeout_ms_(g_check_url_timeout_ms) { | 
|  | 354   g_check_url_timeout_ms = new_timeout_ms; | 
|  | 355 } | 
|  | 356 | 
|  | 357 ScopedTimeoutForTesting::ScopedTimeoutForTesting(ScopedTimeoutForTesting&& rhs) | 
|  | 358     : original_timeout_ms_(rhs.original_timeout_ms_) { | 
|  | 359   rhs.original_timeout_ms_ = -1; | 
|  | 360 } | 
|  | 361 | 
|  | 362 ScopedTimeoutForTesting::~ScopedTimeoutForTesting() { | 
|  | 363   if (original_timeout_ms_ != -1) | 
|  | 364     g_check_url_timeout_ms = original_timeout_ms_; | 
|  | 365 } | 
|  | 366 | 
| 414 }  // namespace safe_browsing | 367 }  // namespace safe_browsing | 
| OLD | NEW | 
|---|