Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(37)

Side by Side Diff: chrome/browser/safe_browsing/download_protection_service.cc

Issue 565053002: [Downloads] Gracefully handle SafeBrowsing check failures. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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/download_protection_service.h" 5 #include "chrome/browser/safe_browsing/download_protection_service.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/compiler_specific.h" 8 #include "base/compiler_specific.h"
9 #include "base/format_macros.h" 9 #include "base/format_macros.h"
10 #include "base/memory/scoped_ptr.h" 10 #include "base/memory/scoped_ptr.h"
(...skipping 293 matching lines...) Expand 10 before | Expand all | Expand 10 after
304 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 304 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
305 // TODO(noelutz): implement some cache to make sure we don't issue the same 305 // TODO(noelutz): implement some cache to make sure we don't issue the same
306 // request over and over again if a user downloads the same binary multiple 306 // request over and over again if a user downloads the same binary multiple
307 // times. 307 // times.
308 DownloadCheckResultReason reason = REASON_MAX; 308 DownloadCheckResultReason reason = REASON_MAX;
309 if (!IsSupportedDownload( 309 if (!IsSupportedDownload(
310 *item_, item_->GetTargetFilePath(), &reason, &type_)) { 310 *item_, item_->GetTargetFilePath(), &reason, &type_)) {
311 switch (reason) { 311 switch (reason) {
312 case REASON_EMPTY_URL_CHAIN: 312 case REASON_EMPTY_URL_CHAIN:
313 case REASON_INVALID_URL: 313 case REASON_INVALID_URL:
314 PostFinishTask(SAFE, reason); 314 PostFinishTask(UNKNOWN, reason);
315 return; 315 return;
316 316
317 case REASON_NOT_BINARY_FILE: 317 case REASON_NOT_BINARY_FILE:
318 RecordFileExtensionType(item_->GetTargetFilePath()); 318 RecordFileExtensionType(item_->GetTargetFilePath());
319 PostFinishTask(SAFE, reason); 319 PostFinishTask(UNKNOWN, reason);
320 return; 320 return;
321 321
322 default: 322 default:
323 // We only expect the reasons explicitly handled above. 323 // We only expect the reasons explicitly handled above.
324 NOTREACHED(); 324 NOTREACHED();
325 } 325 }
326 } 326 }
327 RecordFileExtensionType(item_->GetTargetFilePath()); 327 RecordFileExtensionType(item_->GetTargetFilePath());
328 328
329 // Compute features from the file contents. Note that we record histograms 329 // Compute features from the file contents. Note that we record histograms
(...skipping 20 matching lines...) Expand all
350 timeout_start_time_ = base::TimeTicks::Now(); 350 timeout_start_time_ = base::TimeTicks::Now();
351 BrowserThread::PostDelayedTask( 351 BrowserThread::PostDelayedTask(
352 BrowserThread::UI, 352 BrowserThread::UI,
353 FROM_HERE, 353 FROM_HERE,
354 base::Bind(&CheckClientDownloadRequest::Cancel, 354 base::Bind(&CheckClientDownloadRequest::Cancel,
355 weakptr_factory_.GetWeakPtr()), 355 weakptr_factory_.GetWeakPtr()),
356 base::TimeDelta::FromMilliseconds( 356 base::TimeDelta::FromMilliseconds(
357 service_->download_request_timeout_ms())); 357 service_->download_request_timeout_ms()));
358 } 358 }
359 359
360 // Canceling a request will cause us to always report the result as SAFE 360 // Canceling a request will cause us to always report the result as UNKNOWN
361 // unless a pending request is about to call FinishRequest. 361 // unless a pending request is about to call FinishRequest.
362 void Cancel() { 362 void Cancel() {
363 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 363 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
364 if (fetcher_.get()) { 364 if (fetcher_.get()) {
365 // The DownloadProtectionService is going to release its reference, so we 365 // The DownloadProtectionService is going to release its reference, so we
366 // might be destroyed before the URLFetcher completes. Cancel the 366 // might be destroyed before the URLFetcher completes. Cancel the
367 // fetcher so it does not try to invoke OnURLFetchComplete. 367 // fetcher so it does not try to invoke OnURLFetchComplete.
368 fetcher_.reset(); 368 fetcher_.reset();
369 } 369 }
370 // Note: If there is no fetcher, then some callback is still holding a 370 // Note: If there is no fetcher, then some callback is still holding a
371 // reference to this object. We'll eventually wind up in some method on 371 // reference to this object. We'll eventually wind up in some method on
372 // the UI thread that will call FinishRequest() again. If FinishRequest() 372 // the UI thread that will call FinishRequest() again. If FinishRequest()
373 // is called a second time, it will be a no-op. 373 // is called a second time, it will be a no-op.
374 FinishRequest(SAFE, REASON_REQUEST_CANCELED); 374 FinishRequest(UNKNOWN, REASON_REQUEST_CANCELED);
375 // Calling FinishRequest might delete this object, we may be deleted by 375 // Calling FinishRequest might delete this object, we may be deleted by
376 // this point. 376 // this point.
377 } 377 }
378 378
379 // content::DownloadItem::Observer implementation. 379 // content::DownloadItem::Observer implementation.
380 virtual void OnDownloadDestroyed(content::DownloadItem* download) OVERRIDE { 380 virtual void OnDownloadDestroyed(content::DownloadItem* download) OVERRIDE {
381 Cancel(); 381 Cancel();
382 DCHECK(item_ == NULL); 382 DCHECK(item_ == NULL);
383 } 383 }
384 384
385 // From the net::URLFetcherDelegate interface. 385 // From the net::URLFetcherDelegate interface.
386 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE { 386 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE {
387 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 387 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
388 DCHECK_EQ(source, fetcher_.get()); 388 DCHECK_EQ(source, fetcher_.get());
389 VLOG(2) << "Received a response for URL: " 389 VLOG(2) << "Received a response for URL: "
390 << item_->GetUrlChain().back() << ": success=" 390 << item_->GetUrlChain().back() << ": success="
391 << source->GetStatus().is_success() << " response_code=" 391 << source->GetStatus().is_success() << " response_code="
392 << source->GetResponseCode(); 392 << source->GetResponseCode();
393 if (source->GetStatus().is_success()) { 393 if (source->GetStatus().is_success()) {
394 UMA_HISTOGRAM_SPARSE_SLOWLY( 394 UMA_HISTOGRAM_SPARSE_SLOWLY(
395 "SBClientDownload.DownloadRequestResponseCode", 395 "SBClientDownload.DownloadRequestResponseCode",
396 source->GetResponseCode()); 396 source->GetResponseCode());
397 } 397 }
398 UMA_HISTOGRAM_SPARSE_SLOWLY( 398 UMA_HISTOGRAM_SPARSE_SLOWLY(
399 "SBClientDownload.DownloadRequestNetError", 399 "SBClientDownload.DownloadRequestNetError",
400 -source->GetStatus().error()); 400 -source->GetStatus().error());
401 DownloadCheckResultReason reason = REASON_SERVER_PING_FAILED; 401 DownloadCheckResultReason reason = REASON_SERVER_PING_FAILED;
402 DownloadCheckResult result = SAFE; 402 DownloadCheckResult result = UNKNOWN;
403 if (source->GetStatus().is_success() && 403 if (source->GetStatus().is_success() &&
404 net::HTTP_OK == source->GetResponseCode()) { 404 net::HTTP_OK == source->GetResponseCode()) {
405 ClientDownloadResponse response; 405 ClientDownloadResponse response;
406 std::string data; 406 std::string data;
407 bool got_data = source->GetResponseAsString(&data); 407 bool got_data = source->GetResponseAsString(&data);
408 DCHECK(got_data); 408 DCHECK(got_data);
409 if (!response.ParseFromString(data)) { 409 if (!response.ParseFromString(data)) {
410 reason = REASON_INVALID_RESPONSE_PROTO; 410 reason = REASON_INVALID_RESPONSE_PROTO;
411 result = UNKNOWN;
411 } else if (response.verdict() == ClientDownloadResponse::SAFE) { 412 } else if (response.verdict() == ClientDownloadResponse::SAFE) {
412 reason = REASON_DOWNLOAD_SAFE; 413 reason = REASON_DOWNLOAD_SAFE;
414 result = SAFE;
413 } else if (service_ && !service_->IsSupportedDownload( 415 } else if (service_ && !service_->IsSupportedDownload(
414 *item_, item_->GetTargetFilePath())) { 416 *item_, item_->GetTargetFilePath())) {
415 // The client of the download protection service assumes that we don't 417 // The client of the download protection service assumes that we don't
416 // support this download so we cannot return any other verdict than 418 // support this download so we cannot return any other verdict than
417 // SAFE even if the server says it's dangerous to download this file. 419 // UNKNOWN even if the server says it's dangerous to download this file.
418 // Note: if service_ is NULL we already cancelled the request and 420 // Note: if service_ is NULL we already cancelled the request and
419 // returned SAFE. 421 // returned UNKNOWN.
420 reason = REASON_DOWNLOAD_NOT_SUPPORTED; 422 reason = REASON_DOWNLOAD_NOT_SUPPORTED;
423 result = UNKNOWN;
421 } else if (response.verdict() == ClientDownloadResponse::DANGEROUS) { 424 } else if (response.verdict() == ClientDownloadResponse::DANGEROUS) {
422 reason = REASON_DOWNLOAD_DANGEROUS; 425 reason = REASON_DOWNLOAD_DANGEROUS;
423 result = DANGEROUS; 426 result = DANGEROUS;
424 } else if (response.verdict() == ClientDownloadResponse::UNCOMMON) { 427 } else if (response.verdict() == ClientDownloadResponse::UNCOMMON) {
425 reason = REASON_DOWNLOAD_UNCOMMON; 428 reason = REASON_DOWNLOAD_UNCOMMON;
426 result = UNCOMMON; 429 result = UNCOMMON;
427 } else if (response.verdict() == ClientDownloadResponse::DANGEROUS_HOST) { 430 } else if (response.verdict() == ClientDownloadResponse::DANGEROUS_HOST) {
428 reason = REASON_DOWNLOAD_DANGEROUS_HOST; 431 reason = REASON_DOWNLOAD_DANGEROUS_HOST;
429 result = DANGEROUS_HOST; 432 result = DANGEROUS_HOST;
430 } else if ( 433 } else if (
431 response.verdict() == ClientDownloadResponse::POTENTIALLY_UNWANTED) { 434 response.verdict() == ClientDownloadResponse::POTENTIALLY_UNWANTED) {
432 reason = REASON_DOWNLOAD_POTENTIALLY_UNWANTED; 435 reason = REASON_DOWNLOAD_POTENTIALLY_UNWANTED;
433 result = POTENTIALLY_UNWANTED; 436 result = POTENTIALLY_UNWANTED;
434 } else { 437 } else {
435 LOG(DFATAL) << "Unknown download response verdict: " 438 LOG(DFATAL) << "Unknown download response verdict: "
436 << response.verdict(); 439 << response.verdict();
437 reason = REASON_INVALID_RESPONSE_VERDICT; 440 reason = REASON_INVALID_RESPONSE_VERDICT;
441 result = UNKNOWN;
438 } 442 }
439 DownloadFeedbackService::MaybeStorePingsForDownload( 443 DownloadFeedbackService::MaybeStorePingsForDownload(
440 result, item_, client_download_request_data_, data); 444 result, item_, client_download_request_data_, data);
441 } 445 }
442 // We don't need the fetcher anymore. 446 // We don't need the fetcher anymore.
443 fetcher_.reset(); 447 fetcher_.reset();
444 UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestDuration", 448 UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestDuration",
445 base::TimeTicks::Now() - start_time_); 449 base::TimeTicks::Now() - start_time_);
446 UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestNetworkDuration", 450 UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestNetworkDuration",
447 base::TimeTicks::Now() - request_start_time_); 451 base::TimeTicks::Now() - request_start_time_);
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after
559 VLOG(1) << "Zip analysis failed for " << item_->GetFullPath().value(); 563 VLOG(1) << "Zip analysis failed for " << item_->GetFullPath().value();
560 } 564 }
561 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.ZipFileHasExecutable", 565 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.ZipFileHasExecutable",
562 zipped_executable_); 566 zipped_executable_);
563 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.ZipFileHasArchiveButNoExecutable", 567 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.ZipFileHasArchiveButNoExecutable",
564 results.has_archive && !zipped_executable_); 568 results.has_archive && !zipped_executable_);
565 UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractZipFeaturesTime", 569 UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractZipFeaturesTime",
566 base::TimeTicks::Now() - zip_analysis_start_time_); 570 base::TimeTicks::Now() - zip_analysis_start_time_);
567 571
568 if (!zipped_executable_) { 572 if (!zipped_executable_) {
569 PostFinishTask(SAFE, REASON_ARCHIVE_WITHOUT_BINARIES); 573 PostFinishTask(UNKNOWN, REASON_ARCHIVE_WITHOUT_BINARIES);
570 return; 574 return;
571 } 575 }
572 OnFileFeatureExtractionDone(); 576 OnFileFeatureExtractionDone();
573 } 577 }
574 578
579 static void RecordCountOfSignedOrWhitelistedDownload() {
580 UMA_HISTOGRAM_COUNTS("SBClientDownload.SignedOrWhitelistedDownload", 1);
581 }
582
575 void CheckWhitelists() { 583 void CheckWhitelists() {
576 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 584 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
577 DownloadCheckResultReason reason = REASON_MAX; 585
578 if (!database_manager_.get()) { 586 if (!database_manager_.get()) {
579 reason = REASON_SB_DISABLED; 587 PostFinishTask(UNKNOWN, REASON_SB_DISABLED);
580 } else { 588 return;
581 const GURL& url = url_chain_.back();
582 if (url.is_valid() && database_manager_->MatchDownloadWhitelistUrl(url)) {
583 VLOG(2) << url << " is on the download whitelist.";
584 reason = REASON_WHITELISTED_URL;
585 }
586 if (reason != REASON_MAX || signature_info_.trusted()) {
587 UMA_HISTOGRAM_COUNTS("SBClientDownload.SignedOrWhitelistedDownload", 1);
588 }
589 } 589 }
590 if (reason == REASON_MAX && signature_info_.trusted()) { 590
591 const GURL& url = url_chain_.back();
592 if (url.is_valid() && database_manager_->MatchDownloadWhitelistUrl(url)) {
593 VLOG(2) << url << " is on the download whitelist.";
594 RecordCountOfSignedOrWhitelistedDownload();
595 PostFinishTask(SAFE, REASON_WHITELISTED_URL);
596 return;
597 }
598
599 if (signature_info_.trusted()) {
600 RecordCountOfSignedOrWhitelistedDownload();
591 for (int i = 0; i < signature_info_.certificate_chain_size(); ++i) { 601 for (int i = 0; i < signature_info_.certificate_chain_size(); ++i) {
592 if (CertificateChainIsWhitelisted( 602 if (CertificateChainIsWhitelisted(
593 signature_info_.certificate_chain(i))) { 603 signature_info_.certificate_chain(i))) {
594 reason = REASON_TRUSTED_EXECUTABLE; 604 PostFinishTask(SAFE, REASON_TRUSTED_EXECUTABLE);
595 break; 605 return;
596 } 606 }
597 } 607 }
598 } 608 }
599 if (reason != REASON_MAX) { 609
600 PostFinishTask(SAFE, reason); 610 if (!pingback_enabled_) {
601 } else if (!pingback_enabled_) { 611 PostFinishTask(UNKNOWN, REASON_PING_DISABLED);
602 PostFinishTask(SAFE, REASON_PING_DISABLED); 612 return;
603 } else { 613 }
604 // Currently, the UI only works on Windows so we don't even bother 614
605 // with pinging the server if we're not on Windows. TODO(noelutz): 615 // Currently, the UI only works on Windows so we don't even bother with
606 // change this code once the UI is done for Linux and Mac. 616 // pinging the server if we're not on Windows.
617 // TODO(noelutz): change this code once the UI is done for Linux and Mac.
607 #if defined(OS_WIN) 618 #if defined(OS_WIN)
608 // The URLFetcher is owned by the UI thread, so post a message to 619 // The URLFetcher is owned by the UI thread, so post a message to
609 // start the pingback. 620 // start the pingback.
610 BrowserThread::PostTask( 621 BrowserThread::PostTask(
611 BrowserThread::UI, 622 BrowserThread::UI,
612 FROM_HERE, 623 FROM_HERE,
613 base::Bind(&CheckClientDownloadRequest::GetTabRedirects, this)); 624 base::Bind(&CheckClientDownloadRequest::GetTabRedirects, this));
614 #else 625 #else
615 PostFinishTask(SAFE, REASON_OS_NOT_SUPPORTED); 626 PostFinishTask(UNKNOWN, REASON_OS_NOT_SUPPORTED);
616 #endif 627 #endif
617 }
618 } 628 }
619 629
620 void GetTabRedirects() { 630 void GetTabRedirects() {
621 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 631 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
622 if (!tab_url_.is_valid()) { 632 if (!tab_url_.is_valid()) {
623 SendRequest(); 633 SendRequest();
624 return; 634 return;
625 } 635 }
626 636
627 Profile* profile = Profile::FromBrowserContext(item_->GetBrowserContext()); 637 Profile* profile = Profile::FromBrowserContext(item_->GetBrowserContext());
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
702 } 712 }
703 } 713 }
704 714
705 request.set_user_initiated(item_->HasUserGesture()); 715 request.set_user_initiated(item_->HasUserGesture());
706 request.set_file_basename( 716 request.set_file_basename(
707 item_->GetTargetFilePath().BaseName().AsUTF8Unsafe()); 717 item_->GetTargetFilePath().BaseName().AsUTF8Unsafe());
708 request.set_download_type(type_); 718 request.set_download_type(type_);
709 request.mutable_signature()->CopyFrom(signature_info_); 719 request.mutable_signature()->CopyFrom(signature_info_);
710 request.mutable_image_headers()->CopyFrom(image_headers_); 720 request.mutable_image_headers()->CopyFrom(image_headers_);
711 if (!request.SerializeToString(&client_download_request_data_)) { 721 if (!request.SerializeToString(&client_download_request_data_)) {
712 FinishRequest(SAFE, REASON_INVALID_REQUEST_PROTO); 722 FinishRequest(UNKNOWN, REASON_INVALID_REQUEST_PROTO);
713 return; 723 return;
714 } 724 }
715 725
716 VLOG(2) << "Sending a request for URL: " 726 VLOG(2) << "Sending a request for URL: "
717 << item_->GetUrlChain().back(); 727 << item_->GetUrlChain().back();
718 fetcher_.reset(net::URLFetcher::Create(0 /* ID used for testing */, 728 fetcher_.reset(net::URLFetcher::Create(0 /* ID used for testing */,
719 GetDownloadRequestUrl(), 729 GetDownloadRequestUrl(),
720 net::URLFetcher::POST, 730 net::URLFetcher::POST,
721 this)); 731 this));
722 fetcher_->SetLoadFlags(net::LOAD_DISABLE_CACHE); 732 fetcher_->SetLoadFlags(net::LOAD_DISABLE_CACHE);
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
758 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.DownloadRequestTimeoutStats", 768 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.DownloadRequestTimeoutStats",
759 reason, 769 reason,
760 REASON_MAX); 770 REASON_MAX);
761 if (reason != REASON_REQUEST_CANCELED) { 771 if (reason != REASON_REQUEST_CANCELED) {
762 UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestTimeoutDuration", 772 UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestTimeoutDuration",
763 base::TimeTicks::Now() - timeout_start_time_); 773 base::TimeTicks::Now() - timeout_start_time_);
764 } 774 }
765 } 775 }
766 if (service_) { 776 if (service_) {
767 VLOG(2) << "SafeBrowsing download verdict for: " 777 VLOG(2) << "SafeBrowsing download verdict for: "
768 << item_->DebugString(true) << " verdict:" << reason; 778 << item_->DebugString(true) << " verdict:" << reason
779 << " result:" << result;
769 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.CheckDownloadStats", 780 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.CheckDownloadStats",
770 reason, 781 reason,
771 REASON_MAX); 782 REASON_MAX);
772 callback_.Run(result); 783 callback_.Run(result);
773 item_->RemoveObserver(this); 784 item_->RemoveObserver(this);
774 item_ = NULL; 785 item_ = NULL;
775 DownloadProtectionService* service = service_; 786 DownloadProtectionService* service = service_;
776 service_ = NULL; 787 service_ = NULL;
777 service->RequestFinished(this); 788 service->RequestFinished(this);
778 // DownloadProtectionService::RequestFinished will decrement our refcount, 789 // DownloadProtectionService::RequestFinished will decrement our refcount,
779 // so we may be deleted now. 790 // so we may be deleted now.
780 } else { 791 } else {
781 callback_.Run(SAFE); 792 callback_.Run(UNKNOWN);
782 } 793 }
783 } 794 }
784 795
785 bool CertificateChainIsWhitelisted( 796 bool CertificateChainIsWhitelisted(
786 const ClientDownloadRequest_CertificateChain& chain) { 797 const ClientDownloadRequest_CertificateChain& chain) {
787 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 798 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
788 if (chain.element_size() < 2) { 799 if (chain.element_size() < 2) {
789 // We need to have both a signing certificate and its issuer certificate 800 // We need to have both a signing certificate and its issuer certificate
790 // present to construct a whitelist entry. 801 // present to construct a whitelist entry.
791 return false; 802 return false;
(...skipping 258 matching lines...) Expand 10 before | Expand all | Expand 10 after
1050 GURL DownloadProtectionService::GetDownloadRequestUrl() { 1061 GURL DownloadProtectionService::GetDownloadRequestUrl() {
1051 GURL url(kDownloadRequestUrl); 1062 GURL url(kDownloadRequestUrl);
1052 std::string api_key = google_apis::GetAPIKey(); 1063 std::string api_key = google_apis::GetAPIKey();
1053 if (!api_key.empty()) 1064 if (!api_key.empty())
1054 url = url.Resolve("?key=" + net::EscapeQueryParamValue(api_key, true)); 1065 url = url.Resolve("?key=" + net::EscapeQueryParamValue(api_key, true));
1055 1066
1056 return url; 1067 return url;
1057 } 1068 }
1058 1069
1059 } // namespace safe_browsing 1070 } // namespace safe_browsing
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698