Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 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 "chrome/browser/safe_browsing/browser_feature_extractor.h" | 5 #include "chrome/browser/safe_browsing/browser_feature_extractor.h" |
| 6 | 6 |
| 7 #include <map> | 7 #include <map> |
| 8 #include <utility> | 8 #include <utility> |
| 9 | 9 |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| 11 #include "base/bind_helpers.h" | 11 #include "base/bind_helpers.h" |
| 12 #include "base/format_macros.h" | 12 #include "base/format_macros.h" |
| 13 #include "base/stl_util.h" | 13 #include "base/stl_util.h" |
| 14 #include "base/strings/stringprintf.h" | 14 #include "base/strings/stringprintf.h" |
| 15 #include "base/time/time.h" | 15 #include "base/time/time.h" |
| 16 #include "chrome/browser/common/cancelable_request.h" | |
| 17 #include "chrome/browser/history/history_service.h" | 16 #include "chrome/browser/history/history_service.h" |
| 18 #include "chrome/browser/history/history_service_factory.h" | 17 #include "chrome/browser/history/history_service_factory.h" |
| 19 #include "chrome/browser/history/history_types.h" | 18 #include "chrome/browser/history/history_types.h" |
| 20 #include "chrome/browser/profiles/profile.h" | 19 #include "chrome/browser/profiles/profile.h" |
| 21 #include "chrome/browser/safe_browsing/browser_features.h" | 20 #include "chrome/browser/safe_browsing/browser_features.h" |
| 22 #include "chrome/browser/safe_browsing/client_side_detection_host.h" | 21 #include "chrome/browser/safe_browsing/client_side_detection_host.h" |
| 23 #include "chrome/browser/safe_browsing/database_manager.h" | 22 #include "chrome/browser/safe_browsing/database_manager.h" |
| 24 #include "chrome/common/safe_browsing/csd.pb.h" | 23 #include "chrome/common/safe_browsing/csd.pb.h" |
| 25 #include "content/public/browser/browser_thread.h" | 24 #include "content/public/browser/browser_thread.h" |
| 26 #include "content/public/browser/navigation_controller.h" | 25 #include "content/public/browser/navigation_controller.h" |
| (...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 167 WebContents* tab, | 166 WebContents* tab, |
| 168 ClientSideDetectionHost* host) | 167 ClientSideDetectionHost* host) |
| 169 : tab_(tab), | 168 : tab_(tab), |
| 170 host_(host), | 169 host_(host), |
| 171 weak_factory_(this) { | 170 weak_factory_(this) { |
| 172 DCHECK(tab); | 171 DCHECK(tab); |
| 173 } | 172 } |
| 174 | 173 |
| 175 BrowserFeatureExtractor::~BrowserFeatureExtractor() { | 174 BrowserFeatureExtractor::~BrowserFeatureExtractor() { |
| 176 weak_factory_.InvalidateWeakPtrs(); | 175 weak_factory_.InvalidateWeakPtrs(); |
| 177 // Delete all the pending extractions (delete callback and request objects). | |
| 178 STLDeleteContainerPairFirstPointers(pending_extractions_.begin(), | |
| 179 pending_extractions_.end()); | |
| 180 | |
| 181 // Also cancel all the pending history service queries. | |
| 182 HistoryService* history; | |
| 183 bool success = GetHistoryService(&history); | |
| 184 DCHECK(success || pending_queries_.size() == 0); | |
| 185 // Cancel all the pending history lookups and cleanup the memory. | |
| 186 for (PendingQueriesMap::iterator it = pending_queries_.begin(); | |
| 187 it != pending_queries_.end(); ++it) { | |
| 188 if (history) { | |
| 189 history->CancelRequest(it->first); | |
| 190 } | |
| 191 ExtractionData& extraction = it->second; | |
| 192 delete extraction.first; // delete request | |
|
blundell
2014/06/24 14:08:58
now when the tracker is destroyed it will destroy
sdefresne
2014/06/25 12:20:37
It will invalidate all requests in flight, and sin
| |
| 193 } | |
| 194 pending_queries_.clear(); | |
| 195 } | 176 } |
| 196 | 177 |
| 197 void BrowserFeatureExtractor::ExtractFeatures(const BrowseInfo* info, | 178 void BrowserFeatureExtractor::ExtractFeatures(const BrowseInfo* info, |
| 198 ClientPhishingRequest* request, | 179 ClientPhishingRequest* request, |
| 199 const DoneCallback& callback) { | 180 const DoneCallback& callback) { |
| 200 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 181 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 201 DCHECK(request); | 182 DCHECK(request); |
| 202 DCHECK(info); | 183 DCHECK(info); |
| 203 DCHECK_EQ(0U, request->url().find("http:")); | 184 DCHECK_EQ(0U, request->url().find("http:")); |
| 204 DCHECK(!callback.is_null()); | 185 DCHECK(!callback.is_null()); |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 242 std::string(), controller, url_index, info->url_redirects, request); | 223 std::string(), controller, url_index, info->url_redirects, request); |
| 243 } | 224 } |
| 244 if (first_host_index != -1) { | 225 if (first_host_index != -1) { |
| 245 AddNavigationFeatures(features::kHostPrefix, | 226 AddNavigationFeatures(features::kHostPrefix, |
| 246 controller, | 227 controller, |
| 247 first_host_index, | 228 first_host_index, |
| 248 info->host_redirects, | 229 info->host_redirects, |
| 249 request); | 230 request); |
| 250 } | 231 } |
| 251 | 232 |
| 233 // The API doesn't take a scoped_ptr because the API gets mocked and we | |
| 234 // cannot mock an API that takes scoped_ptr as arguments. | |
| 235 scoped_ptr<ClientPhishingRequest> req(request); | |
| 236 | |
| 252 ExtractBrowseInfoFeatures(*info, request); | 237 ExtractBrowseInfoFeatures(*info, request); |
| 253 pending_extractions_[request] = callback; | |
| 254 base::MessageLoop::current()->PostTask( | 238 base::MessageLoop::current()->PostTask( |
| 255 FROM_HERE, | 239 FROM_HERE, |
| 256 base::Bind(&BrowserFeatureExtractor::StartExtractFeatures, | 240 base::Bind(&BrowserFeatureExtractor::StartExtractFeatures, |
| 257 weak_factory_.GetWeakPtr(), request, callback)); | 241 weak_factory_.GetWeakPtr(), |
| 242 base::Passed(&req), | |
| 243 callback)); | |
| 258 } | 244 } |
| 259 | 245 |
| 260 void BrowserFeatureExtractor::ExtractMalwareFeatures( | 246 void BrowserFeatureExtractor::ExtractMalwareFeatures( |
| 261 BrowseInfo* info, | 247 BrowseInfo* info, |
| 262 ClientMalwareRequest* request, | 248 ClientMalwareRequest* request, |
| 263 const MalwareDoneCallback& callback) { | 249 const MalwareDoneCallback& callback) { |
| 264 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 250 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 265 DCHECK(!callback.is_null()); | 251 DCHECK(!callback.is_null()); |
| 266 | 252 |
| 267 // Grab the IPs because they might go away before we're done | 253 // Grab the IPs because they might go away before we're done |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 306 AddFeature(features::kSafeBrowsingThreatType, | 292 AddFeature(features::kSafeBrowsingThreatType, |
| 307 static_cast<double>(info.unsafe_resource->threat_type), | 293 static_cast<double>(info.unsafe_resource->threat_type), |
| 308 request); | 294 request); |
| 309 } | 295 } |
| 310 if (info.http_status_code != 0) { | 296 if (info.http_status_code != 0) { |
| 311 AddFeature(features::kHttpStatusCode, info.http_status_code, request); | 297 AddFeature(features::kHttpStatusCode, info.http_status_code, request); |
| 312 } | 298 } |
| 313 } | 299 } |
| 314 | 300 |
| 315 void BrowserFeatureExtractor::StartExtractFeatures( | 301 void BrowserFeatureExtractor::StartExtractFeatures( |
| 316 ClientPhishingRequest* request, | 302 scoped_ptr<ClientPhishingRequest> request, |
| 317 const DoneCallback& callback) { | 303 const DoneCallback& callback) { |
| 318 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 304 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 319 size_t removed = pending_extractions_.erase(request); | |
| 320 DCHECK_EQ(1U, removed); | |
| 321 HistoryService* history; | 305 HistoryService* history; |
| 322 if (!request || !request->IsInitialized() || !GetHistoryService(&history)) { | 306 if (!request || !request->IsInitialized() || !GetHistoryService(&history)) { |
| 323 callback.Run(false, request); | 307 callback.Run(false, request.Pass()); |
| 324 return; | 308 return; |
| 325 } | 309 } |
| 326 // HistoryService::QueryURL migrated from CancelableRequestComsumer to | |
| 327 // CancelableRequestTracker and there is no Handle to associate to the | |
| 328 // request. Instead manage the request object lifetime by using a scoped_ptr | |
| 329 // and using base::Passed(). So if the asynchronous call is canceled, the | |
| 330 // request is deleted, otherwise the callback becomes the owner. | |
| 331 scoped_ptr<ClientPhishingRequest> owned_request(request); | |
| 332 history->QueryURL(GURL(request->url()), | 310 history->QueryURL(GURL(request->url()), |
| 333 true /* wants_visits */, | 311 true /* wants_visits */, |
| 334 base::Bind(&BrowserFeatureExtractor::QueryUrlHistoryDone, | 312 base::Bind(&BrowserFeatureExtractor::QueryUrlHistoryDone, |
| 335 base::Unretained(this), | 313 base::Unretained(this), |
| 336 base::Passed(&owned_request), | 314 base::Passed(&request), |
| 337 callback), | 315 callback), |
| 338 &cancelable_task_tracker_); | 316 &cancelable_task_tracker_); |
| 339 } | 317 } |
| 340 | 318 |
| 341 void BrowserFeatureExtractor::QueryUrlHistoryDone( | 319 void BrowserFeatureExtractor::QueryUrlHistoryDone( |
| 342 scoped_ptr<ClientPhishingRequest> owned_request, | 320 scoped_ptr<ClientPhishingRequest> request, |
| 343 const DoneCallback& callback, | 321 const DoneCallback& callback, |
| 344 bool success, | 322 bool success, |
| 345 const history::URLRow& row, | 323 const history::URLRow& row, |
| 346 const history::VisitVector& visits) { | 324 const history::VisitVector& visits) { |
| 347 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 325 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 348 DCHECK(owned_request); | 326 DCHECK(request); |
| 349 DCHECK(!callback.is_null()); | 327 DCHECK(!callback.is_null()); |
| 350 ClientPhishingRequest* request = owned_request.release(); | |
| 351 if (!success) { | 328 if (!success) { |
| 352 // URL is not found in the history. In practice this should not | 329 // URL is not found in the history. In practice this should not |
| 353 // happen (unless there is a real error) because we just visited | 330 // happen (unless there is a real error) because we just visited |
| 354 // that URL. | 331 // that URL. |
| 355 callback.Run(false, request); | 332 callback.Run(false, request.Pass()); |
| 356 return; | 333 return; |
| 357 } | 334 } |
| 358 AddFeature(features::kUrlHistoryVisitCount, | 335 AddFeature(features::kUrlHistoryVisitCount, |
| 359 static_cast<double>(row.visit_count()), | 336 static_cast<double>(row.visit_count()), |
| 360 request); | 337 request.get()); |
| 361 | 338 |
| 362 base::Time threshold = base::Time::Now() - base::TimeDelta::FromDays(1); | 339 base::Time threshold = base::Time::Now() - base::TimeDelta::FromDays(1); |
| 363 int num_visits_24h_ago = 0; | 340 int num_visits_24h_ago = 0; |
| 364 int num_visits_typed = 0; | 341 int num_visits_typed = 0; |
| 365 int num_visits_link = 0; | 342 int num_visits_link = 0; |
| 366 for (history::VisitVector::const_iterator it = visits.begin(); | 343 for (history::VisitVector::const_iterator it = visits.begin(); |
| 367 it != visits.end(); | 344 it != visits.end(); |
| 368 ++it) { | 345 ++it) { |
| 369 if (!content::PageTransitionIsMainFrame(it->transition)) { | 346 if (!content::PageTransitionIsMainFrame(it->transition)) { |
| 370 continue; | 347 continue; |
| 371 } | 348 } |
| 372 if (it->visit_time < threshold) { | 349 if (it->visit_time < threshold) { |
| 373 ++num_visits_24h_ago; | 350 ++num_visits_24h_ago; |
| 374 } | 351 } |
| 375 content::PageTransition transition = content::PageTransitionStripQualifier( | 352 content::PageTransition transition = content::PageTransitionStripQualifier( |
| 376 it->transition); | 353 it->transition); |
| 377 if (transition == content::PAGE_TRANSITION_TYPED) { | 354 if (transition == content::PAGE_TRANSITION_TYPED) { |
| 378 ++num_visits_typed; | 355 ++num_visits_typed; |
| 379 } else if (transition == content::PAGE_TRANSITION_LINK) { | 356 } else if (transition == content::PAGE_TRANSITION_LINK) { |
| 380 ++num_visits_link; | 357 ++num_visits_link; |
| 381 } | 358 } |
| 382 } | 359 } |
| 383 AddFeature(features::kUrlHistoryVisitCountMoreThan24hAgo, | 360 AddFeature(features::kUrlHistoryVisitCountMoreThan24hAgo, |
| 384 static_cast<double>(num_visits_24h_ago), | 361 static_cast<double>(num_visits_24h_ago), |
| 385 request); | 362 request.get()); |
| 386 AddFeature(features::kUrlHistoryTypedCount, | 363 AddFeature(features::kUrlHistoryTypedCount, |
| 387 static_cast<double>(num_visits_typed), | 364 static_cast<double>(num_visits_typed), |
| 388 request); | 365 request.get()); |
| 389 AddFeature(features::kUrlHistoryLinkCount, | 366 AddFeature(features::kUrlHistoryLinkCount, |
| 390 static_cast<double>(num_visits_link), | 367 static_cast<double>(num_visits_link), |
| 391 request); | 368 request.get()); |
| 392 | 369 |
| 393 // Issue next history lookup for host visits. | 370 // Issue next history lookup for host visits. |
| 394 HistoryService* history; | 371 HistoryService* history; |
| 395 if (!GetHistoryService(&history)) { | 372 if (!GetHistoryService(&history)) { |
| 396 callback.Run(false, request); | 373 callback.Run(false, request.Pass()); |
| 397 return; | 374 return; |
| 398 } | 375 } |
| 399 CancelableRequestProvider::Handle next_handle = | 376 history->GetVisibleVisitCountToHost( |
| 400 history->GetVisibleVisitCountToHost( | 377 GURL(request->url()), |
| 401 GURL(request->url()), | 378 base::Bind(&BrowserFeatureExtractor::QueryHttpHostVisitsDone, |
| 402 &request_consumer_, | 379 base::Unretained(this), |
| 403 base::Bind(&BrowserFeatureExtractor::QueryHttpHostVisitsDone, | 380 base::Passed(&request), |
| 404 base::Unretained(this))); | 381 callback), |
| 405 StorePendingQuery(next_handle, request, callback); | 382 &cancelable_task_tracker_); |
| 406 } | 383 } |
| 407 | 384 |
| 408 void BrowserFeatureExtractor::QueryHttpHostVisitsDone( | 385 void BrowserFeatureExtractor::QueryHttpHostVisitsDone( |
| 409 CancelableRequestProvider::Handle handle, | 386 scoped_ptr<ClientPhishingRequest> request, |
| 387 const DoneCallback& callback, | |
| 410 bool success, | 388 bool success, |
| 411 int num_visits, | 389 int num_visits, |
| 412 base::Time first_visit) { | 390 base::Time first_visit) { |
| 413 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 391 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 414 ClientPhishingRequest* request; | |
| 415 DoneCallback callback; | |
| 416 if (!GetPendingQuery(handle, &request, &callback)) { | |
| 417 DLOG(FATAL) << "No pending history query found"; | |
| 418 return; | |
| 419 } | |
| 420 DCHECK(request); | 392 DCHECK(request); |
| 421 DCHECK(!callback.is_null()); | 393 DCHECK(!callback.is_null()); |
| 422 if (!success) { | 394 if (!success) { |
| 423 callback.Run(false, request); | 395 callback.Run(false, request.Pass()); |
| 424 return; | 396 return; |
| 425 } | 397 } |
| 426 SetHostVisitsFeatures(num_visits, first_visit, true, request); | 398 SetHostVisitsFeatures(num_visits, first_visit, true, request.get()); |
| 427 | 399 |
| 428 // Same lookup but for the HTTPS URL. | 400 // Same lookup but for the HTTPS URL. |
| 429 HistoryService* history; | 401 HistoryService* history; |
| 430 if (!GetHistoryService(&history)) { | 402 if (!GetHistoryService(&history)) { |
| 431 callback.Run(false, request); | 403 callback.Run(false, request.Pass()); |
| 432 return; | 404 return; |
| 433 } | 405 } |
| 434 std::string https_url = request->url(); | 406 std::string https_url = request->url(); |
| 435 CancelableRequestProvider::Handle next_handle = | 407 history->GetVisibleVisitCountToHost( |
| 436 history->GetVisibleVisitCountToHost( | 408 GURL(https_url.replace(0, 5, "https:")), |
| 437 GURL(https_url.replace(0, 5, "https:")), | 409 base::Bind(&BrowserFeatureExtractor::QueryHttpsHostVisitsDone, |
| 438 &request_consumer_, | 410 base::Unretained(this), |
| 439 base::Bind(&BrowserFeatureExtractor::QueryHttpsHostVisitsDone, | 411 base::Passed(&request), |
| 440 base::Unretained(this))); | 412 callback), |
| 441 StorePendingQuery(next_handle, request, callback); | 413 &cancelable_task_tracker_); |
| 442 } | 414 } |
| 443 | 415 |
| 444 void BrowserFeatureExtractor::QueryHttpsHostVisitsDone( | 416 void BrowserFeatureExtractor::QueryHttpsHostVisitsDone( |
| 445 CancelableRequestProvider::Handle handle, | 417 scoped_ptr<ClientPhishingRequest> request, |
| 418 const DoneCallback& callback, | |
| 446 bool success, | 419 bool success, |
| 447 int num_visits, | 420 int num_visits, |
| 448 base::Time first_visit) { | 421 base::Time first_visit) { |
| 449 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 422 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 450 ClientPhishingRequest* request; | |
| 451 DoneCallback callback; | |
| 452 if (!GetPendingQuery(handle, &request, &callback)) { | |
| 453 DLOG(FATAL) << "No pending history query found"; | |
| 454 return; | |
| 455 } | |
| 456 DCHECK(request); | 423 DCHECK(request); |
| 457 DCHECK(!callback.is_null()); | 424 DCHECK(!callback.is_null()); |
| 458 if (!success) { | 425 if (!success) { |
| 459 callback.Run(false, request); | 426 callback.Run(false, request.Pass()); |
| 460 return; | 427 return; |
| 461 } | 428 } |
| 462 SetHostVisitsFeatures(num_visits, first_visit, false, request); | 429 SetHostVisitsFeatures(num_visits, first_visit, false, request.get()); |
| 463 callback.Run(true, request); // We're done with all the history lookups. | 430 callback.Run(true, request.Pass()); |
| 464 } | 431 } |
| 465 | 432 |
| 466 void BrowserFeatureExtractor::SetHostVisitsFeatures( | 433 void BrowserFeatureExtractor::SetHostVisitsFeatures( |
| 467 int num_visits, | 434 int num_visits, |
| 468 base::Time first_visit, | 435 base::Time first_visit, |
| 469 bool is_http_query, | 436 bool is_http_query, |
| 470 ClientPhishingRequest* request) { | 437 ClientPhishingRequest* request) { |
| 471 DCHECK(request); | 438 DCHECK(request); |
| 472 AddFeature(is_http_query ? | 439 AddFeature(is_http_query ? |
| 473 features::kHttpHostVisitCount : features::kHttpsHostVisitCount, | 440 features::kHttpHostVisitCount : features::kHttpsHostVisitCount, |
| 474 static_cast<double>(num_visits), | 441 static_cast<double>(num_visits), |
| 475 request); | 442 request); |
| 476 if (num_visits > 0) { | 443 if (num_visits > 0) { |
| 477 AddFeature( | 444 AddFeature( |
| 478 is_http_query ? | 445 is_http_query ? |
| 479 features::kFirstHttpHostVisitMoreThan24hAgo : | 446 features::kFirstHttpHostVisitMoreThan24hAgo : |
| 480 features::kFirstHttpsHostVisitMoreThan24hAgo, | 447 features::kFirstHttpsHostVisitMoreThan24hAgo, |
| 481 (first_visit < (base::Time::Now() - base::TimeDelta::FromDays(1))) ? | 448 (first_visit < (base::Time::Now() - base::TimeDelta::FromDays(1))) ? |
| 482 1.0 : 0.0, | 449 1.0 : 0.0, |
| 483 request); | 450 request); |
| 484 } | 451 } |
| 485 } | 452 } |
| 486 | 453 |
| 487 void BrowserFeatureExtractor::StorePendingQuery( | |
| 488 CancelableRequestProvider::Handle handle, | |
| 489 ClientPhishingRequest* request, | |
| 490 const DoneCallback& callback) { | |
| 491 DCHECK_EQ(0U, pending_queries_.count(handle)); | |
| 492 pending_queries_[handle] = std::make_pair(request, callback); | |
| 493 } | |
| 494 | |
| 495 bool BrowserFeatureExtractor::GetPendingQuery( | |
| 496 CancelableRequestProvider::Handle handle, | |
| 497 ClientPhishingRequest** request, | |
| 498 DoneCallback* callback) { | |
| 499 PendingQueriesMap::iterator it = pending_queries_.find(handle); | |
| 500 DCHECK(it != pending_queries_.end()); | |
| 501 if (it != pending_queries_.end()) { | |
| 502 *request = it->second.first; | |
| 503 *callback = it->second.second; | |
| 504 pending_queries_.erase(it); | |
| 505 return true; | |
| 506 } | |
| 507 return false; | |
| 508 } | |
| 509 | |
| 510 bool BrowserFeatureExtractor::GetHistoryService(HistoryService** history) { | 454 bool BrowserFeatureExtractor::GetHistoryService(HistoryService** history) { |
| 511 *history = NULL; | 455 *history = NULL; |
| 512 if (tab_ && tab_->GetBrowserContext()) { | 456 if (tab_ && tab_->GetBrowserContext()) { |
| 513 Profile* profile = Profile::FromBrowserContext(tab_->GetBrowserContext()); | 457 Profile* profile = Profile::FromBrowserContext(tab_->GetBrowserContext()); |
| 514 *history = HistoryServiceFactory::GetForProfile(profile, | 458 *history = HistoryServiceFactory::GetForProfile(profile, |
| 515 Profile::EXPLICIT_ACCESS); | 459 Profile::EXPLICIT_ACCESS); |
| 516 if (*history) { | 460 if (*history) { |
| 517 return true; | 461 return true; |
| 518 } | 462 } |
| 519 } | 463 } |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 534 // Limit the number of matched bad IPs in one request to control | 478 // Limit the number of matched bad IPs in one request to control |
| 535 // the request's size | 479 // the request's size |
| 536 if (matched_bad_ips >= kMaxMalwareIPPerRequest) { | 480 if (matched_bad_ips >= kMaxMalwareIPPerRequest) { |
| 537 break; | 481 break; |
| 538 } | 482 } |
| 539 } | 483 } |
| 540 callback.Run(true, request.Pass()); | 484 callback.Run(true, request.Pass()); |
| 541 } | 485 } |
| 542 | 486 |
| 543 } // namespace safe_browsing | 487 } // namespace safe_browsing |
| OLD | NEW |