Home | History | Annotate | Download | only in safe_browsing
      1 // Copyright (c) 2011 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.
      4 
      5 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
      6 
      7 #include "base/callback.h"
      8 #include "base/command_line.h"
      9 #include "base/lazy_instance.h"
     10 #include "base/path_service.h"
     11 #include "base/stl_util-inl.h"
     12 #include "base/string_util.h"
     13 #include "base/threading/thread_restrictions.h"
     14 #include "chrome/browser/browser_process.h"
     15 #include "chrome/browser/metrics/metrics_service.h"
     16 #include "chrome/browser/prefs/pref_service.h"
     17 #include "chrome/browser/profiles/profile_manager.h"
     18 #include "chrome/browser/safe_browsing/malware_details.h"
     19 #include "chrome/browser/safe_browsing/protocol_manager.h"
     20 #include "chrome/browser/safe_browsing/safe_browsing_blocking_page.h"
     21 #include "chrome/browser/safe_browsing/safe_browsing_database.h"
     22 #include "chrome/browser/tab_contents/tab_util.h"
     23 #include "chrome/common/chrome_constants.h"
     24 #include "chrome/common/chrome_paths.h"
     25 #include "chrome/common/chrome_switches.h"
     26 #include "chrome/common/pref_names.h"
     27 #include "chrome/common/url_constants.h"
     28 #include "content/browser/browser_thread.h"
     29 #include "content/browser/tab_contents/tab_contents.h"
     30 #include "net/base/registry_controlled_domain.h"
     31 #include "net/url_request/url_request_context_getter.h"
     32 
     33 #if defined(OS_WIN)
     34 #include "chrome/installer/util/browser_distribution.h"
     35 #endif
     36 
     37 namespace {
     38 
     39 // The default URL prefix where browser fetches chunk updates, hashes,
     40 // and reports safe browsing hits.
     41 const char* const kSbDefaultInfoURLPrefix =
     42     "http://safebrowsing.clients.google.com/safebrowsing";
     43 
     44 // The default URL prefix where browser fetches MAC client key and reports
     45 // malware details.
     46 const char* const kSbDefaultMacKeyURLPrefix =
     47     "https://sb-ssl.google.com/safebrowsing";
     48 
     49 // When download url check takes this long, client's callback will be called
     50 // without waiting for the result.
     51 const int64 kDownloadUrlCheckTimeoutMs = 10000;
     52 
     53 // Similar to kDownloadUrlCheckTimeoutMs, but for download hash checks.
     54 const int64 kDownloadHashCheckTimeoutMs = 10000;
     55 
     56 // TODO(lzheng): Replace this with Profile* ProfileManager::GetDefaultProfile().
     57 Profile* GetDefaultProfile() {
     58   FilePath user_data_dir;
     59   PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
     60   ProfileManager* profile_manager = g_browser_process->profile_manager();
     61   return profile_manager->GetDefaultProfile(user_data_dir);
     62 }
     63 
     64 // Records disposition information about the check.  |hit| should be
     65 // |true| if there were any prefix hits in |full_hashes|.
     66 void RecordGetHashCheckStatus(
     67     bool hit,
     68     bool is_download,
     69     const std::vector<SBFullHashResult>& full_hashes) {
     70   SafeBrowsingProtocolManager::ResultType result;
     71   if (full_hashes.empty()) {
     72     result = SafeBrowsingProtocolManager::GET_HASH_FULL_HASH_EMPTY;
     73   } else if (hit) {
     74     result = SafeBrowsingProtocolManager::GET_HASH_FULL_HASH_HIT;
     75   } else {
     76     result = SafeBrowsingProtocolManager::GET_HASH_FULL_HASH_MISS;
     77   }
     78   SafeBrowsingProtocolManager::RecordGetHashResult(is_download, result);
     79 }
     80 
     81 }  // namespace
     82 
     83 // static
     84 SafeBrowsingServiceFactory* SafeBrowsingService::factory_ = NULL;
     85 
     86 // The default SafeBrowsingServiceFactory.  Global, made a singleton so we
     87 // don't leak it.
     88 class SafeBrowsingServiceFactoryImpl : public SafeBrowsingServiceFactory {
     89  public:
     90   virtual SafeBrowsingService* CreateSafeBrowsingService() {
     91     return new SafeBrowsingService();
     92   }
     93 
     94  private:
     95   friend struct base::DefaultLazyInstanceTraits<SafeBrowsingServiceFactoryImpl>;
     96 
     97   SafeBrowsingServiceFactoryImpl() { }
     98 
     99   DISALLOW_COPY_AND_ASSIGN(SafeBrowsingServiceFactoryImpl);
    100 };
    101 
    102 static base::LazyInstance<SafeBrowsingServiceFactoryImpl>
    103     g_safe_browsing_service_factory_impl(base::LINKER_INITIALIZED);
    104 
    105 struct SafeBrowsingService::WhiteListedEntry {
    106   int render_process_host_id;
    107   int render_view_id;
    108   std::string domain;
    109   UrlCheckResult result;
    110 };
    111 
    112 SafeBrowsingService::UnsafeResource::UnsafeResource()
    113     : resource_type(ResourceType::MAIN_FRAME),
    114       threat_type(SAFE),
    115       client(NULL),
    116       render_process_host_id(-1),
    117       render_view_id(-1) {
    118 }
    119 
    120 SafeBrowsingService::UnsafeResource::~UnsafeResource() {}
    121 
    122 SafeBrowsingService::SafeBrowsingCheck::SafeBrowsingCheck()
    123     : full_hash(NULL),
    124       client(NULL),
    125       need_get_hash(false),
    126       result(SAFE),
    127       is_download(false),
    128       timeout_task(NULL) {
    129 }
    130 
    131 SafeBrowsingService::SafeBrowsingCheck::~SafeBrowsingCheck() {}
    132 
    133 void SafeBrowsingService::Client::OnSafeBrowsingResult(
    134     const SafeBrowsingCheck& check) {
    135   if (!check.urls.empty()) {
    136 
    137     DCHECK(!check.full_hash.get());
    138     if (!check.is_download) {
    139       DCHECK_EQ(1U, check.urls.size());
    140       OnBrowseUrlCheckResult(check.urls[0], check.result);
    141     } else {
    142       OnDownloadUrlCheckResult(check.urls, check.result);
    143     }
    144   } else if (check.full_hash.get()) {
    145     OnDownloadHashCheckResult(
    146         safe_browsing_util::SBFullHashToString(*check.full_hash),
    147         check.result);
    148   } else {
    149     NOTREACHED();
    150   }
    151 }
    152 
    153 /* static */
    154 SafeBrowsingService* SafeBrowsingService::CreateSafeBrowsingService() {
    155   if (!factory_)
    156     factory_ = g_safe_browsing_service_factory_impl.Pointer();
    157   return factory_->CreateSafeBrowsingService();
    158 }
    159 
    160 SafeBrowsingService::SafeBrowsingService()
    161     : database_(NULL),
    162       protocol_manager_(NULL),
    163       enabled_(false),
    164       enable_download_protection_(false),
    165       enable_csd_whitelist_(false),
    166       update_in_progress_(false),
    167       database_update_in_progress_(false),
    168       closing_database_(false),
    169       download_urlcheck_timeout_ms_(kDownloadUrlCheckTimeoutMs),
    170       download_hashcheck_timeout_ms_(kDownloadHashCheckTimeoutMs) {
    171 }
    172 
    173 void SafeBrowsingService::Initialize() {
    174   // Get the profile's preference for SafeBrowsing.
    175   PrefService* pref_service = GetDefaultProfile()->GetPrefs();
    176   if (pref_service->GetBoolean(prefs::kSafeBrowsingEnabled))
    177     Start();
    178 }
    179 
    180 void SafeBrowsingService::ShutDown() {
    181   BrowserThread::PostTask(
    182       BrowserThread::IO, FROM_HERE,
    183       NewRunnableMethod(this, &SafeBrowsingService::OnIOShutdown));
    184 }
    185 
    186 bool SafeBrowsingService::CanCheckUrl(const GURL& url) const {
    187   return url.SchemeIs(chrome::kFtpScheme) ||
    188          url.SchemeIs(chrome::kHttpScheme) ||
    189          url.SchemeIs(chrome::kHttpsScheme);
    190 }
    191 
    192 // Only report SafeBrowsing related stats when UMA is enabled and
    193 // safe browsing is enabled.
    194 bool SafeBrowsingService::CanReportStats() const {
    195   const MetricsService* metrics = g_browser_process->metrics_service();
    196   const PrefService* pref_service = GetDefaultProfile()->GetPrefs();
    197   return metrics && metrics->reporting_active() &&
    198       pref_service && pref_service->GetBoolean(prefs::kSafeBrowsingEnabled);
    199 }
    200 
    201 // Binhash verification is only enabled for UMA users for now.
    202 bool SafeBrowsingService::DownloadBinHashNeeded() const {
    203   return enable_download_protection_ && CanReportStats();
    204 }
    205 
    206 bool SafeBrowsingService::CheckDownloadUrl(const std::vector<GURL>& url_chain,
    207                                            Client* client) {
    208   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    209   if (!enabled_ || !enable_download_protection_)
    210     return true;
    211 
    212   // We need to check the database for url prefix, and later may fetch the url
    213   // from the safebrowsing backends. These need to be asynchronous.
    214   SafeBrowsingCheck* check = new SafeBrowsingCheck();
    215   check->urls = url_chain;
    216   StartDownloadCheck(
    217       check,
    218       client,
    219       NewRunnableMethod(this,
    220                         &SafeBrowsingService::CheckDownloadUrlOnSBThread,
    221                         check),
    222       download_urlcheck_timeout_ms_);
    223   return false;
    224 }
    225 
    226 bool SafeBrowsingService::CheckDownloadHash(const std::string& full_hash,
    227                                             Client* client) {
    228   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    229   DCHECK(!full_hash.empty());
    230   if (!enabled_ || !enable_download_protection_ || full_hash.empty())
    231     return true;
    232 
    233   // We need to check the database for url prefix, and later may fetch the url
    234   // from the safebrowsing backends. These need to be asynchronous.
    235   SafeBrowsingCheck* check = new SafeBrowsingCheck();
    236   check->full_hash.reset(new SBFullHash);
    237   safe_browsing_util::StringToSBFullHash(full_hash, check->full_hash.get());
    238   StartDownloadCheck(
    239       check,
    240       client,
    241       NewRunnableMethod(this,
    242                         &SafeBrowsingService::CheckDownloadHashOnSBThread,
    243                         check),
    244       download_hashcheck_timeout_ms_);
    245   return false;
    246 }
    247 
    248 bool SafeBrowsingService::MatchCsdWhitelistUrl(const GURL& url) {
    249   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    250   if (!enabled_ || !enable_csd_whitelist_ || !MakeDatabaseAvailable()) {
    251     // There is something funky going on here -- for example, perhaps the user
    252     // has not restarted since enabling metrics reporting, so we haven't
    253     // enabled the csd whitelist yet.  Just to be safe we return true in this
    254     // case.
    255     return true;
    256   }
    257   return database_->ContainsCsdWhitelistedUrl(url);
    258 }
    259 
    260 bool SafeBrowsingService::CheckBrowseUrl(const GURL& url,
    261                                          Client* client) {
    262   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    263   if (!enabled_)
    264     return true;
    265 
    266   if (!CanCheckUrl(url))
    267     return true;
    268 
    269   const base::TimeTicks start = base::TimeTicks::Now();
    270   if (!MakeDatabaseAvailable()) {
    271     QueuedCheck check;
    272     check.client = client;
    273     check.url = url;
    274     check.start = start;
    275     queued_checks_.push_back(check);
    276     return false;
    277   }
    278 
    279   std::string list;
    280   std::vector<SBPrefix> prefix_hits;
    281   std::vector<SBFullHashResult> full_hits;
    282   bool prefix_match =
    283       database_->ContainsBrowseUrl(url, &list, &prefix_hits,
    284                                    &full_hits,
    285                                    protocol_manager_->last_update());
    286 
    287   UMA_HISTOGRAM_TIMES("SB2.FilterCheck", base::TimeTicks::Now() - start);
    288 
    289   if (!prefix_match)
    290     return true;  // URL is okay.
    291 
    292   // Needs to be asynchronous, since we could be in the constructor of a
    293   // ResourceDispatcherHost event handler which can't pause there.
    294   SafeBrowsingCheck* check = new SafeBrowsingCheck();
    295   check->urls.push_back(url);
    296   check->client = client;
    297   check->result = SAFE;
    298   check->is_download = false;
    299   check->need_get_hash = full_hits.empty();
    300   check->prefix_hits.swap(prefix_hits);
    301   check->full_hits.swap(full_hits);
    302   checks_.insert(check);
    303 
    304   BrowserThread::PostTask(
    305       BrowserThread::IO, FROM_HERE,
    306       NewRunnableMethod(this, &SafeBrowsingService::OnCheckDone, check));
    307 
    308   return false;
    309 }
    310 
    311 void SafeBrowsingService::CancelCheck(Client* client) {
    312   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    313   for (CurrentChecks::iterator i = checks_.begin(); i != checks_.end(); ++i) {
    314     // We can't delete matching checks here because the db thread has a copy of
    315     // the pointer.  Instead, we simply NULL out the client, and when the db
    316     // thread calls us back, we'll clean up the check.
    317     if ((*i)->client == client)
    318       (*i)->client = NULL;
    319   }
    320 
    321   // Scan the queued clients store. Clients may be here if they requested a URL
    322   // check before the database has finished loading.
    323   for (std::deque<QueuedCheck>::iterator it(queued_checks_.begin());
    324        it != queued_checks_.end(); ) {
    325     // In this case it's safe to delete matches entirely since nothing has a
    326     // pointer to them.
    327     if (it->client == client)
    328       it = queued_checks_.erase(it);
    329     else
    330       ++it;
    331   }
    332 }
    333 
    334 void SafeBrowsingService::DisplayBlockingPage(
    335     const GURL& url,
    336     const GURL& original_url,
    337     const std::vector<GURL>& redirect_urls,
    338     ResourceType::Type resource_type,
    339     UrlCheckResult result,
    340     Client* client,
    341     int render_process_host_id,
    342     int render_view_id) {
    343   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    344 
    345   // Check if the user has already ignored our warning for this render_view
    346   // and domain.
    347   for (size_t i = 0; i < white_listed_entries_.size(); ++i) {
    348     const WhiteListedEntry& entry = white_listed_entries_[i];
    349     if (entry.render_process_host_id == render_process_host_id &&
    350         entry.render_view_id == render_view_id &&
    351         entry.result == result &&
    352         entry.domain ==
    353         net::RegistryControlledDomainService::GetDomainAndRegistry(url)) {
    354       MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
    355           this, &SafeBrowsingService::NotifyClientBlockingComplete,
    356           client, true));
    357       return;
    358     }
    359   }
    360 
    361   UnsafeResource resource;
    362   resource.url = url;
    363   resource.original_url = original_url;
    364   resource.redirect_urls = redirect_urls;
    365   resource.resource_type = resource_type;
    366   resource.threat_type= result;
    367   resource.client = client;
    368   resource.render_process_host_id = render_process_host_id;
    369   resource.render_view_id = render_view_id;
    370 
    371   // The blocking page must be created from the UI thread.
    372   BrowserThread::PostTask(
    373       BrowserThread::UI, FROM_HERE,
    374       NewRunnableMethod(
    375           this, &SafeBrowsingService::DoDisplayBlockingPage, resource));
    376 }
    377 
    378 void SafeBrowsingService::HandleGetHashResults(
    379     SafeBrowsingCheck* check,
    380     const std::vector<SBFullHashResult>& full_hashes,
    381     bool can_cache) {
    382   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    383 
    384   if (!enabled_)
    385     return;
    386 
    387   // If the service has been shut down, |check| should have been deleted.
    388   DCHECK(checks_.find(check) != checks_.end());
    389 
    390   // |start| is set before calling |GetFullHash()|, which should be
    391   // the only path which gets to here.
    392   DCHECK(!check->start.is_null());
    393   UMA_HISTOGRAM_LONG_TIMES("SB2.Network",
    394                            base::TimeTicks::Now() - check->start);
    395 
    396   std::vector<SBPrefix> prefixes = check->prefix_hits;
    397   OnHandleGetHashResults(check, full_hashes);  // 'check' is deleted here.
    398 
    399   if (can_cache && MakeDatabaseAvailable()) {
    400     // Cache the GetHash results in memory:
    401     database_->CacheHashResults(prefixes, full_hashes);
    402   }
    403 }
    404 
    405 void SafeBrowsingService::HandleChunk(const std::string& list,
    406                                       SBChunkList* chunks) {
    407   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    408   DCHECK(enabled_);
    409   safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
    410       this, &SafeBrowsingService::HandleChunkForDatabase, list, chunks));
    411 }
    412 
    413 void SafeBrowsingService::HandleChunkDelete(
    414     std::vector<SBChunkDelete>* chunk_deletes) {
    415   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    416   DCHECK(enabled_);
    417   safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
    418       this, &SafeBrowsingService::DeleteChunks, chunk_deletes));
    419 }
    420 
    421 void SafeBrowsingService::UpdateStarted() {
    422   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    423   DCHECK(enabled_);
    424   DCHECK(!update_in_progress_);
    425   update_in_progress_ = true;
    426   safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
    427       this, &SafeBrowsingService::GetAllChunksFromDatabase));
    428 }
    429 
    430 void SafeBrowsingService::UpdateFinished(bool update_succeeded) {
    431   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    432   DCHECK(enabled_);
    433   if (update_in_progress_) {
    434     update_in_progress_ = false;
    435     safe_browsing_thread_->message_loop()->PostTask(FROM_HERE,
    436         NewRunnableMethod(this,
    437                           &SafeBrowsingService::DatabaseUpdateFinished,
    438                           update_succeeded));
    439   }
    440 }
    441 
    442 bool SafeBrowsingService::IsUpdateInProgress() const {
    443   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    444   return update_in_progress_;
    445 }
    446 
    447 void SafeBrowsingService::OnBlockingPageDone(
    448     const std::vector<UnsafeResource>& resources,
    449     bool proceed) {
    450   for (std::vector<UnsafeResource>::const_iterator iter = resources.begin();
    451        iter != resources.end(); ++iter) {
    452     const UnsafeResource& resource = *iter;
    453     NotifyClientBlockingComplete(resource.client, proceed);
    454 
    455     if (proceed) {
    456       // Whitelist this domain and warning type for the given tab.
    457       WhiteListedEntry entry;
    458       entry.render_process_host_id = resource.render_process_host_id;
    459       entry.render_view_id = resource.render_view_id;
    460       entry.domain = net::RegistryControlledDomainService::GetDomainAndRegistry(
    461             resource.url);
    462       entry.result = resource.threat_type;
    463       white_listed_entries_.push_back(entry);
    464     }
    465   }
    466 }
    467 
    468 void SafeBrowsingService::OnNewMacKeys(const std::string& client_key,
    469                                        const std::string& wrapped_key) {
    470   PrefService* prefs = g_browser_process->local_state();
    471   if (prefs) {
    472     prefs->SetString(prefs::kSafeBrowsingClientKey, client_key);
    473     prefs->SetString(prefs::kSafeBrowsingWrappedKey, wrapped_key);
    474   }
    475 }
    476 
    477 void SafeBrowsingService::OnEnable(bool enabled) {
    478   if (enabled)
    479     Start();
    480   else
    481     ShutDown();
    482 }
    483 
    484 // static
    485 void SafeBrowsingService::RegisterPrefs(PrefService* prefs) {
    486   prefs->RegisterStringPref(prefs::kSafeBrowsingClientKey, "");
    487   prefs->RegisterStringPref(prefs::kSafeBrowsingWrappedKey, "");
    488 }
    489 
    490 void SafeBrowsingService::CloseDatabase() {
    491   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    492 
    493   // Cases to avoid:
    494   //  * If |closing_database_| is true, continuing will queue up a second
    495   //    request, |closing_database_| will be reset after handling the first
    496   //    request, and if any functions on the db thread recreate the database, we
    497   //    could start using it on the IO thread and then have the second request
    498   //    handler delete it out from under us.
    499   //  * If |database_| is NULL, then either no creation request is in flight, in
    500   //    which case we don't need to do anything, or one is in flight, in which
    501   //    case the database will be recreated before our deletion request is
    502   //    handled, and could be used on the IO thread in that time period, leading
    503   //    to the same problem as above.
    504   //  * If |queued_checks_| is non-empty and |database_| is non-NULL, we're
    505   //    about to be called back (in DatabaseLoadComplete()).  This will call
    506   //    CheckUrl(), which will want the database.  Closing the database here
    507   //    would lead to an infinite loop in DatabaseLoadComplete(), and even if it
    508   //    didn't, it would be pointless since we'd just want to recreate.
    509   //
    510   // The first two cases above are handled by checking DatabaseAvailable().
    511   if (!DatabaseAvailable() || !queued_checks_.empty())
    512     return;
    513 
    514   closing_database_ = true;
    515   if (safe_browsing_thread_.get()) {
    516     safe_browsing_thread_->message_loop()->PostTask(FROM_HERE,
    517         NewRunnableMethod(this, &SafeBrowsingService::OnCloseDatabase));
    518   }
    519 }
    520 
    521 void SafeBrowsingService::ResetDatabase() {
    522   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    523   DCHECK(enabled_);
    524   safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
    525       this, &SafeBrowsingService::OnResetDatabase));
    526 }
    527 
    528 void SafeBrowsingService::LogPauseDelay(base::TimeDelta time) {
    529   UMA_HISTOGRAM_LONG_TIMES("SB2.Delay", time);
    530 }
    531 
    532 SafeBrowsingService::~SafeBrowsingService() {
    533   // We should have already been shut down.  If we're still enabled, then the
    534   // database isn't going to be closed properly, which could lead to corruption.
    535   DCHECK(!enabled_);
    536 }
    537 
    538 void SafeBrowsingService::OnIOInitialize(
    539     const std::string& client_key,
    540     const std::string& wrapped_key,
    541     net::URLRequestContextGetter* request_context_getter) {
    542   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    543   enabled_ = true;
    544 
    545   MakeDatabaseAvailable();
    546 
    547   // On Windows, get the safe browsing client name from the browser
    548   // distribution classes in installer util. These classes don't yet have
    549   // an analog on non-Windows builds so just keep the name specified here.
    550 #if defined(OS_WIN)
    551   BrowserDistribution* dist = BrowserDistribution::GetDistribution();
    552   std::string client_name(dist->GetSafeBrowsingName());
    553 #else
    554 #if defined(GOOGLE_CHROME_BUILD)
    555   std::string client_name("googlechrome");
    556 #else
    557   std::string client_name("chromium");
    558 #endif
    559 #endif
    560   CommandLine* cmdline = CommandLine::ForCurrentProcess();
    561   bool disable_auto_update =
    562       cmdline->HasSwitch(switches::kSbDisableAutoUpdate) ||
    563       cmdline->HasSwitch(switches::kDisableBackgroundNetworking);
    564   std::string info_url_prefix =
    565       cmdline->HasSwitch(switches::kSbInfoURLPrefix) ?
    566       cmdline->GetSwitchValueASCII(switches::kSbInfoURLPrefix) :
    567       kSbDefaultInfoURLPrefix;
    568   std::string mackey_url_prefix =
    569       cmdline->HasSwitch(switches::kSbMacKeyURLPrefix) ?
    570       cmdline->GetSwitchValueASCII(switches::kSbMacKeyURLPrefix) :
    571       kSbDefaultMacKeyURLPrefix;
    572 
    573   DCHECK(!protocol_manager_);
    574   protocol_manager_ =
    575       SafeBrowsingProtocolManager::Create(this,
    576                                           client_name,
    577                                           client_key,
    578                                           wrapped_key,
    579                                           request_context_getter,
    580                                           info_url_prefix,
    581                                           mackey_url_prefix,
    582                                           disable_auto_update);
    583 
    584   protocol_manager_->Initialize();
    585 }
    586 
    587 void SafeBrowsingService::OnIOShutdown() {
    588   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    589   if (!enabled_)
    590     return;
    591 
    592   enabled_ = false;
    593 
    594   // This cancels all in-flight GetHash requests.
    595   delete protocol_manager_;
    596   protocol_manager_ = NULL;
    597 
    598   // Delete queued checks, calling back any clients with 'SAFE'.
    599   // If we don't do this here we may fail to close the database below.
    600   while (!queued_checks_.empty()) {
    601     QueuedCheck queued = queued_checks_.front();
    602     if (queued.client) {
    603       SafeBrowsingCheck sb_check;
    604       sb_check.urls.push_back(queued.url);
    605       sb_check.client = queued.client;
    606       sb_check.result = SAFE;
    607       queued.client->OnSafeBrowsingResult(sb_check);
    608     }
    609     queued_checks_.pop_front();
    610   }
    611 
    612   // Close the database.  We don't simply DeleteSoon() because if a close is
    613   // already pending, we'll double-free, and we don't set |database_| to NULL
    614   // because if there is still anything running on the db thread, it could
    615   // create a new database object (via GetDatabase()) that would then leak.
    616   CloseDatabase();
    617 
    618   // Flush the database thread. Any in-progress database check results will be
    619   // ignored and cleaned up below.
    620   //
    621   // Note that to avoid leaking the database, we rely on the fact that no new
    622   // tasks will be added to the db thread between the call above and this one.
    623   // See comments on the declaration of |safe_browsing_thread_|.
    624   {
    625     // A ScopedAllowIO object is required to join the thread when calling Stop.
    626     // See http://crbug.com/72696.
    627     base::ThreadRestrictions::ScopedAllowIO allow_io_for_thread_join;
    628     safe_browsing_thread_.reset();
    629   }
    630 
    631   // Delete pending checks, calling back any clients with 'SAFE'.  We have
    632   // to do this after the db thread returns because methods on it can have
    633   // copies of these pointers, so deleting them might lead to accessing garbage.
    634   for (CurrentChecks::iterator it = checks_.begin();
    635        it != checks_.end(); ++it) {
    636     SafeBrowsingCheck* check = *it;
    637     if (check->client) {
    638       check->result = SAFE;
    639       check->client->OnSafeBrowsingResult(*check);
    640     }
    641     if (check->timeout_task)
    642       check->timeout_task->Cancel();
    643   }
    644   STLDeleteElements(&checks_);
    645 
    646   gethash_requests_.clear();
    647 }
    648 
    649 bool SafeBrowsingService::DatabaseAvailable() const {
    650   base::AutoLock lock(database_lock_);
    651   return !closing_database_ && (database_ != NULL);
    652 }
    653 
    654 bool SafeBrowsingService::MakeDatabaseAvailable() {
    655   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    656   DCHECK(enabled_);
    657   if (DatabaseAvailable())
    658     return true;
    659   safe_browsing_thread_->message_loop()->PostTask(FROM_HERE,
    660       NewRunnableMethod(this, &SafeBrowsingService::GetDatabase));
    661   return false;
    662 }
    663 
    664 SafeBrowsingDatabase* SafeBrowsingService::GetDatabase() {
    665   DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
    666   if (database_)
    667     return database_;
    668 
    669   FilePath path;
    670   bool result = PathService::Get(chrome::DIR_USER_DATA, &path);
    671   DCHECK(result);
    672   path = path.Append(chrome::kSafeBrowsingBaseFilename);
    673 
    674   const base::TimeTicks before = base::TimeTicks::Now();
    675 
    676   SafeBrowsingDatabase* database =
    677       SafeBrowsingDatabase::Create(enable_download_protection_,
    678                                    enable_csd_whitelist_);
    679 
    680   database->Init(path);
    681   {
    682     // Acquiring the lock here guarantees correct ordering between the writes to
    683     // the new database object above, and the setting of |databse_| below.
    684     base::AutoLock lock(database_lock_);
    685     database_ = database;
    686   }
    687 
    688   BrowserThread::PostTask(
    689       BrowserThread::IO, FROM_HERE,
    690       NewRunnableMethod(this, &SafeBrowsingService::DatabaseLoadComplete));
    691 
    692   UMA_HISTOGRAM_TIMES("SB2.DatabaseOpen", base::TimeTicks::Now() - before);
    693   return database_;
    694 }
    695 
    696 void SafeBrowsingService::OnCheckDone(SafeBrowsingCheck* check) {
    697   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    698 
    699   if (!enabled_)
    700     return;
    701 
    702   // If the service has been shut down, |check| should have been deleted.
    703   DCHECK(checks_.find(check) != checks_.end());
    704 
    705   if (check->client && check->need_get_hash) {
    706     // We have a partial match so we need to query Google for the full hash.
    707     // Clean up will happen in HandleGetHashResults.
    708 
    709     // See if we have a GetHash request already in progress for this particular
    710     // prefix. If so, we just append ourselves to the list of interested parties
    711     // when the results arrive. We only do this for checks involving one prefix,
    712     // since that is the common case (multiple prefixes will issue the request
    713     // as normal).
    714     if (check->prefix_hits.size() == 1) {
    715       SBPrefix prefix = check->prefix_hits[0];
    716       GetHashRequests::iterator it = gethash_requests_.find(prefix);
    717       if (it != gethash_requests_.end()) {
    718         // There's already a request in progress.
    719         it->second.push_back(check);
    720         return;
    721       }
    722 
    723       // No request in progress, so we're the first for this prefix.
    724       GetHashRequestors requestors;
    725       requestors.push_back(check);
    726       gethash_requests_[prefix] = requestors;
    727     }
    728 
    729     // Reset the start time so that we can measure the network time without the
    730     // database time.
    731     check->start = base::TimeTicks::Now();
    732     protocol_manager_->GetFullHash(check, check->prefix_hits);
    733   } else {
    734     // We may have cached results for previous GetHash queries.  Since
    735     // this data comes from cache, don't histogram hits.
    736     HandleOneCheck(check, check->full_hits);
    737   }
    738 }
    739 
    740 void SafeBrowsingService::GetAllChunksFromDatabase() {
    741   DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
    742 
    743   bool database_error = true;
    744   std::vector<SBListChunkRanges> lists;
    745   DCHECK(!database_update_in_progress_);
    746   database_update_in_progress_ = true;
    747   GetDatabase();  // This guarantees that |database_| is non-NULL.
    748   if (database_->UpdateStarted(&lists)) {
    749     database_error = false;
    750   } else {
    751     database_->UpdateFinished(false);
    752   }
    753 
    754   BrowserThread::PostTask(
    755       BrowserThread::IO, FROM_HERE,
    756       NewRunnableMethod(
    757           this, &SafeBrowsingService::OnGetAllChunksFromDatabase, lists,
    758           database_error));
    759 }
    760 
    761 void SafeBrowsingService::OnGetAllChunksFromDatabase(
    762     const std::vector<SBListChunkRanges>& lists, bool database_error) {
    763   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    764   if (enabled_)
    765     protocol_manager_->OnGetChunksComplete(lists, database_error);
    766 }
    767 
    768 void SafeBrowsingService::OnChunkInserted() {
    769   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    770   if (enabled_)
    771     protocol_manager_->OnChunkInserted();
    772 }
    773 
    774 void SafeBrowsingService::DatabaseLoadComplete() {
    775   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    776   if (!enabled_)
    777     return;
    778 
    779   HISTOGRAM_COUNTS("SB.QueueDepth", queued_checks_.size());
    780   if (queued_checks_.empty())
    781     return;
    782 
    783   // If the database isn't already available, calling CheckUrl() in the loop
    784   // below will add the check back to the queue, and we'll infinite-loop.
    785   DCHECK(DatabaseAvailable());
    786   while (!queued_checks_.empty()) {
    787     QueuedCheck check = queued_checks_.front();
    788     DCHECK(!check.start.is_null());
    789     HISTOGRAM_TIMES("SB.QueueDelay", base::TimeTicks::Now() - check.start);
    790     // If CheckUrl() determines the URL is safe immediately, it doesn't call the
    791     // client's handler function (because normally it's being directly called by
    792     // the client).  Since we're not the client, we have to convey this result.
    793     if (check.client && CheckBrowseUrl(check.url, check.client)) {
    794       SafeBrowsingCheck sb_check;
    795       sb_check.urls.push_back(check.url);
    796       sb_check.client = check.client;
    797       sb_check.result = SAFE;
    798       check.client->OnSafeBrowsingResult(sb_check);
    799     }
    800     queued_checks_.pop_front();
    801   }
    802 }
    803 
    804 void SafeBrowsingService::HandleChunkForDatabase(
    805     const std::string& list_name, SBChunkList* chunks) {
    806   DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
    807   if (chunks) {
    808     GetDatabase()->InsertChunks(list_name, *chunks);
    809     delete chunks;
    810   }
    811   BrowserThread::PostTask(
    812       BrowserThread::IO, FROM_HERE,
    813       NewRunnableMethod(this, &SafeBrowsingService::OnChunkInserted));
    814 }
    815 
    816 void SafeBrowsingService::DeleteChunks(
    817     std::vector<SBChunkDelete>* chunk_deletes) {
    818   DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
    819   if (chunk_deletes) {
    820     GetDatabase()->DeleteChunks(*chunk_deletes);
    821     delete chunk_deletes;
    822   }
    823 }
    824 
    825 SafeBrowsingService::UrlCheckResult SafeBrowsingService::GetResultFromListname(
    826     const std::string& list_name) {
    827   if (safe_browsing_util::IsPhishingList(list_name)) {
    828     return URL_PHISHING;
    829   }
    830 
    831   if (safe_browsing_util::IsMalwareList(list_name)) {
    832     return URL_MALWARE;
    833   }
    834 
    835   if (safe_browsing_util::IsBadbinurlList(list_name)) {
    836     return BINARY_MALWARE_URL;
    837   }
    838 
    839   if (safe_browsing_util::IsBadbinhashList(list_name)) {
    840     return BINARY_MALWARE_HASH;
    841   }
    842 
    843   DVLOG(1) << "Unknown safe browsing list " << list_name;
    844   return SAFE;
    845 }
    846 
    847 void SafeBrowsingService::NotifyClientBlockingComplete(Client* client,
    848                                                        bool proceed) {
    849   client->OnBlockingPageComplete(proceed);
    850 }
    851 
    852 void SafeBrowsingService::DatabaseUpdateFinished(bool update_succeeded) {
    853   DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
    854   GetDatabase()->UpdateFinished(update_succeeded);
    855   DCHECK(database_update_in_progress_);
    856   database_update_in_progress_ = false;
    857 }
    858 
    859 void SafeBrowsingService::Start() {
    860   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    861   DCHECK(!safe_browsing_thread_.get());
    862   safe_browsing_thread_.reset(new base::Thread("Chrome_SafeBrowsingThread"));
    863   if (!safe_browsing_thread_->Start())
    864     return;
    865 
    866   // Retrieve client MAC keys.
    867   PrefService* local_state = g_browser_process->local_state();
    868   DCHECK(local_state);
    869   std::string client_key, wrapped_key;
    870   if (local_state) {
    871     client_key =
    872       local_state->GetString(prefs::kSafeBrowsingClientKey);
    873     wrapped_key =
    874       local_state->GetString(prefs::kSafeBrowsingWrappedKey);
    875   }
    876 
    877   // We will issue network fetches using the default profile's request context.
    878   scoped_refptr<net::URLRequestContextGetter> request_context_getter(
    879       GetDefaultProfile()->GetRequestContext());
    880 
    881   CommandLine* cmdline = CommandLine::ForCurrentProcess();
    882   enable_download_protection_ =
    883       !cmdline->HasSwitch(switches::kSbDisableDownloadProtection);
    884 
    885   // We only download the csd-whitelist if client-side phishing detection is
    886   // enabled and if the user has opted in with stats collection.  Note: we
    887   // cannot check whether the metrics_service() object is created because it
    888   // may be initialized after this method is called.
    889 #ifdef OS_CHROMEOS
    890   // Client-side detection is disabled on ChromeOS for now, so don't bother
    891   // downloading the whitelist.
    892   enable_csd_whitelist_ = false;
    893 #else
    894   enable_csd_whitelist_ =
    895       (!cmdline->HasSwitch(switches::kDisableClientSidePhishingDetection) &&
    896        local_state && local_state->GetBoolean(prefs::kMetricsReportingEnabled));
    897 #endif
    898 
    899   BrowserThread::PostTask(
    900       BrowserThread::IO, FROM_HERE,
    901       NewRunnableMethod(
    902           this, &SafeBrowsingService::OnIOInitialize, client_key, wrapped_key,
    903           request_context_getter));
    904 }
    905 
    906 void SafeBrowsingService::OnCloseDatabase() {
    907   DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
    908   DCHECK(closing_database_);
    909 
    910   // Because |closing_database_| is true, nothing on the IO thread will be
    911   // accessing the database, so it's safe to delete and then NULL the pointer.
    912   delete database_;
    913   database_ = NULL;
    914 
    915   // Acquiring the lock here guarantees correct ordering between the resetting
    916   // of |database_| above and of |closing_database_| below, which ensures there
    917   // won't be a window during which the IO thread falsely believes the database
    918   // is available.
    919   base::AutoLock lock(database_lock_);
    920   closing_database_ = false;
    921 }
    922 
    923 void SafeBrowsingService::OnResetDatabase() {
    924   DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
    925   GetDatabase()->ResetDatabase();
    926 }
    927 
    928 void SafeBrowsingService::CacheHashResults(
    929   const std::vector<SBPrefix>& prefixes,
    930   const std::vector<SBFullHashResult>& full_hashes) {
    931   DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
    932   GetDatabase()->CacheHashResults(prefixes, full_hashes);
    933 }
    934 
    935 void SafeBrowsingService::OnHandleGetHashResults(
    936     SafeBrowsingCheck* check,
    937     const std::vector<SBFullHashResult>& full_hashes) {
    938   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    939   bool is_download = check->is_download;
    940   SBPrefix prefix = check->prefix_hits[0];
    941   GetHashRequests::iterator it = gethash_requests_.find(prefix);
    942   if (check->prefix_hits.size() > 1 || it == gethash_requests_.end()) {
    943     const bool hit = HandleOneCheck(check, full_hashes);
    944     RecordGetHashCheckStatus(hit, is_download, full_hashes);
    945     return;
    946   }
    947 
    948   // Call back all interested parties, noting if any has a hit.
    949   GetHashRequestors& requestors = it->second;
    950   bool hit = false;
    951   for (GetHashRequestors::iterator r = requestors.begin();
    952        r != requestors.end(); ++r) {
    953     if (HandleOneCheck(*r, full_hashes))
    954       hit = true;
    955   }
    956   RecordGetHashCheckStatus(hit, is_download, full_hashes);
    957 
    958   gethash_requests_.erase(it);
    959 }
    960 
    961 bool SafeBrowsingService::HandleOneCheck(
    962     SafeBrowsingCheck* check,
    963     const std::vector<SBFullHashResult>& full_hashes) {
    964   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    965   DCHECK(check);
    966 
    967   // Always calculate the index, for recording hits.
    968   int index = -1;
    969   if (!check->urls.empty()) {
    970     for (size_t i = 0; i < check->urls.size(); ++i) {
    971       index = safe_browsing_util::GetUrlHashIndex(check->urls[i], full_hashes);
    972       if (index != -1)
    973         break;
    974     }
    975   } else {
    976     index = safe_browsing_util::GetHashIndex(*(check->full_hash), full_hashes);
    977   }
    978 
    979   // |client| is NULL if the request was cancelled.
    980   if (check->client) {
    981     check->result = SAFE;
    982     if (index != -1)
    983       check->result = GetResultFromListname(full_hashes[index].list_name);
    984   }
    985   SafeBrowsingCheckDone(check);
    986   return (index != -1);
    987 }
    988 
    989 void SafeBrowsingService::DoDisplayBlockingPage(
    990     const UnsafeResource& resource) {
    991   // The tab might have been closed.
    992   TabContents* wc =
    993       tab_util::GetTabContentsByID(resource.render_process_host_id,
    994                                    resource.render_view_id);
    995 
    996   if (!wc) {
    997     // The tab is gone and we did not have a chance at showing the interstitial.
    998     // Just act as "Don't Proceed" was chosen.
    999     std::vector<UnsafeResource> resources;
   1000     resources.push_back(resource);
   1001     BrowserThread::PostTask(
   1002       BrowserThread::IO, FROM_HERE,
   1003       NewRunnableMethod(
   1004           this, &SafeBrowsingService::OnBlockingPageDone, resources, false));
   1005     return;
   1006   }
   1007 
   1008   if (resource.threat_type != SafeBrowsingService::SAFE && CanReportStats()) {
   1009     GURL page_url = wc->GetURL();
   1010     GURL referrer_url;
   1011     NavigationEntry* entry = wc->controller().GetActiveEntry();
   1012     if (entry)
   1013       referrer_url = entry->referrer();
   1014     bool is_subresource = resource.resource_type != ResourceType::MAIN_FRAME;
   1015 
   1016     // When the malicious url is on the main frame, and resource.original_url
   1017     // is not the same as the resource.url, that means we have a redirect from
   1018     // resource.original_url to resource.url.
   1019     // Also, at this point, page_url points to the _previous_ page that we
   1020     // were on. We replace page_url with resource.original_url and referrer
   1021     // with page_url.
   1022     if (!is_subresource &&
   1023         !resource.original_url.is_empty() &&
   1024         resource.original_url != resource.url) {
   1025       referrer_url = page_url;
   1026       page_url = resource.original_url;
   1027     }
   1028     ReportSafeBrowsingHit(resource.url, page_url, referrer_url, is_subresource,
   1029                           resource.threat_type, std::string() /* post_data */);
   1030   }
   1031 
   1032   SafeBrowsingBlockingPage::ShowBlockingPage(this, resource);
   1033 }
   1034 
   1035 // A safebrowsing hit is sent after a blocking page for malware/phishing
   1036 // or after the warning dialog for download urls, only for UMA users.
   1037 void SafeBrowsingService::ReportSafeBrowsingHit(
   1038     const GURL& malicious_url,
   1039     const GURL& page_url,
   1040     const GURL& referrer_url,
   1041     bool is_subresource,
   1042     SafeBrowsingService::UrlCheckResult threat_type,
   1043     const std::string& post_data) {
   1044   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   1045   if (!CanReportStats())
   1046     return;
   1047 
   1048   BrowserThread::PostTask(
   1049       BrowserThread::IO, FROM_HERE,
   1050       NewRunnableMethod(
   1051           this,
   1052           &SafeBrowsingService::ReportSafeBrowsingHitOnIOThread,
   1053           malicious_url,
   1054           page_url,
   1055           referrer_url,
   1056           is_subresource,
   1057           threat_type,
   1058           post_data));
   1059 }
   1060 
   1061 void SafeBrowsingService::ReportSafeBrowsingHitOnIOThread(
   1062     const GURL& malicious_url,
   1063     const GURL& page_url,
   1064     const GURL& referrer_url,
   1065     bool is_subresource,
   1066     SafeBrowsingService::UrlCheckResult threat_type,
   1067     const std::string& post_data) {
   1068   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   1069   if (!enabled_)
   1070     return;
   1071 
   1072   DVLOG(1) << "ReportSafeBrowsingHit: " << malicious_url << " " << page_url
   1073            << " " << referrer_url << " " << is_subresource << " "
   1074            << threat_type;
   1075   protocol_manager_->ReportSafeBrowsingHit(malicious_url, page_url,
   1076                                            referrer_url, is_subresource,
   1077                                            threat_type, post_data);
   1078 }
   1079 
   1080 // If the user had opted-in to send MalwareDetails, this gets called
   1081 // when the report is ready.
   1082 void SafeBrowsingService::SendSerializedMalwareDetails(
   1083     const std::string& serialized) {
   1084   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   1085   if (!serialized.empty()) {
   1086     DVLOG(1) << "Sending serialized malware details.";
   1087     protocol_manager_->ReportMalwareDetails(serialized);
   1088   }
   1089 }
   1090 
   1091 void SafeBrowsingService::CheckDownloadHashOnSBThread(
   1092     SafeBrowsingCheck* check) {
   1093   DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
   1094   DCHECK(enable_download_protection_);
   1095 
   1096   if (!database_->ContainsDownloadHashPrefix(check->full_hash->prefix)) {
   1097     // Good, we don't have hash for this url prefix.
   1098     check->result = SAFE;
   1099     BrowserThread::PostTask(
   1100         BrowserThread::IO, FROM_HERE,
   1101         NewRunnableMethod(this,
   1102                           &SafeBrowsingService::CheckDownloadHashDone,
   1103                           check));
   1104     return;
   1105   }
   1106 
   1107   check->need_get_hash = true;
   1108   check->prefix_hits.push_back(check->full_hash->prefix);
   1109   BrowserThread::PostTask(
   1110       BrowserThread::IO, FROM_HERE,
   1111       NewRunnableMethod(this, &SafeBrowsingService::OnCheckDone, check));
   1112 }
   1113 
   1114 void SafeBrowsingService::CheckDownloadUrlOnSBThread(SafeBrowsingCheck* check) {
   1115   DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
   1116   DCHECK(enable_download_protection_);
   1117 
   1118   std::vector<SBPrefix> prefix_hits;
   1119 
   1120   if (!database_->ContainsDownloadUrl(check->urls, &prefix_hits)) {
   1121     // Good, we don't have hash for this url prefix.
   1122     check->result = SAFE;
   1123     BrowserThread::PostTask(
   1124         BrowserThread::IO, FROM_HERE,
   1125         NewRunnableMethod(this,
   1126                           &SafeBrowsingService::CheckDownloadUrlDone,
   1127                           check));
   1128     return;
   1129   }
   1130 
   1131   check->need_get_hash = true;
   1132   check->prefix_hits.clear();
   1133   check->prefix_hits = prefix_hits;
   1134   BrowserThread::PostTask(
   1135       BrowserThread::IO, FROM_HERE,
   1136       NewRunnableMethod(this, &SafeBrowsingService::OnCheckDone, check));
   1137 }
   1138 
   1139 void SafeBrowsingService::TimeoutCallback(SafeBrowsingCheck* check) {
   1140   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   1141   DCHECK(check);
   1142 
   1143   if (!enabled_)
   1144     return;
   1145 
   1146   DCHECK(checks_.find(check) != checks_.end());
   1147   DCHECK_EQ(check->result, SAFE);
   1148   if (check->client) {
   1149     check->client->OnSafeBrowsingResult(*check);
   1150     check->client = NULL;
   1151   }
   1152   check->timeout_task = NULL;
   1153 }
   1154 
   1155 void SafeBrowsingService::CheckDownloadUrlDone(SafeBrowsingCheck* check) {
   1156   DCHECK(enable_download_protection_);
   1157   SafeBrowsingCheckDone(check);
   1158 }
   1159 
   1160 void SafeBrowsingService::CheckDownloadHashDone(SafeBrowsingCheck* check) {
   1161   DCHECK(enable_download_protection_);
   1162   SafeBrowsingCheckDone(check);
   1163 }
   1164 
   1165 void SafeBrowsingService::SafeBrowsingCheckDone(SafeBrowsingCheck* check) {
   1166   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   1167   DCHECK(check);
   1168 
   1169   if (!enabled_)
   1170     return;
   1171 
   1172   VLOG(1) << "SafeBrowsingCheckDone: " << check->result;
   1173   DCHECK(checks_.find(check) != checks_.end());
   1174   if (check->client)
   1175     check->client->OnSafeBrowsingResult(*check);
   1176   if (check->timeout_task)
   1177     check->timeout_task->Cancel();
   1178   checks_.erase(check);
   1179   delete check;
   1180 }
   1181 
   1182 void SafeBrowsingService::StartDownloadCheck(SafeBrowsingCheck* check,
   1183                                              Client* client,
   1184                                              CancelableTask* task,
   1185                                              int64 timeout_ms) {
   1186   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   1187   check->client = client;
   1188   check->result = SAFE;
   1189   check->is_download = true;
   1190   check->timeout_task =
   1191       NewRunnableMethod(this, &SafeBrowsingService::TimeoutCallback, check);
   1192   checks_.insert(check);
   1193 
   1194   safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, task);
   1195 
   1196   MessageLoop::current()->PostDelayedTask(
   1197       FROM_HERE, check->timeout_task, timeout_ms);
   1198 }
   1199