Home | History | Annotate | Download | only in indexed_db
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "content/browser/indexed_db/indexed_db_context_impl.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/bind.h"
     10 #include "base/command_line.h"
     11 #include "base/file_util.h"
     12 #include "base/files/file_enumerator.h"
     13 #include "base/logging.h"
     14 #include "base/sequenced_task_runner.h"
     15 #include "base/strings/string_util.h"
     16 #include "base/strings/utf_string_conversions.h"
     17 #include "base/threading/thread_restrictions.h"
     18 #include "base/time/time.h"
     19 #include "base/values.h"
     20 #include "content/browser/browser_main_loop.h"
     21 #include "content/browser/indexed_db/indexed_db_connection.h"
     22 #include "content/browser/indexed_db/indexed_db_database.h"
     23 #include "content/browser/indexed_db/indexed_db_dispatcher_host.h"
     24 #include "content/browser/indexed_db/indexed_db_factory.h"
     25 #include "content/browser/indexed_db/indexed_db_quota_client.h"
     26 #include "content/browser/indexed_db/indexed_db_tracing.h"
     27 #include "content/browser/indexed_db/indexed_db_transaction.h"
     28 #include "content/public/browser/browser_thread.h"
     29 #include "content/public/browser/indexed_db_info.h"
     30 #include "content/public/common/content_switches.h"
     31 #include "ui/base/text/bytes_formatting.h"
     32 #include "webkit/browser/database/database_util.h"
     33 #include "webkit/browser/quota/quota_manager.h"
     34 #include "webkit/browser/quota/special_storage_policy.h"
     35 #include "webkit/common/database/database_identifier.h"
     36 
     37 using base::DictionaryValue;
     38 using base::ListValue;
     39 using webkit_database::DatabaseUtil;
     40 
     41 namespace content {
     42 const base::FilePath::CharType IndexedDBContextImpl::kIndexedDBDirectory[] =
     43     FILE_PATH_LITERAL("IndexedDB");
     44 
     45 static const base::FilePath::CharType kIndexedDBExtension[] =
     46     FILE_PATH_LITERAL(".indexeddb");
     47 
     48 static const base::FilePath::CharType kLevelDBExtension[] =
     49     FILE_PATH_LITERAL(".leveldb");
     50 
     51 namespace {
     52 
     53 // This may be called after the IndexedDBContext is destroyed.
     54 void GetAllOriginsAndPaths(const base::FilePath& indexeddb_path,
     55                            std::vector<GURL>* origins,
     56                            std::vector<base::FilePath>* file_paths) {
     57   // TODO(jsbell): DCHECK that this is running on an IndexedDB thread,
     58   // if a global handle to it is ever available.
     59   if (indexeddb_path.empty())
     60     return;
     61   base::FileEnumerator file_enumerator(
     62       indexeddb_path, false, base::FileEnumerator::DIRECTORIES);
     63   for (base::FilePath file_path = file_enumerator.Next(); !file_path.empty();
     64        file_path = file_enumerator.Next()) {
     65     if (file_path.Extension() == kLevelDBExtension &&
     66         file_path.RemoveExtension().Extension() == kIndexedDBExtension) {
     67       std::string origin_id = file_path.BaseName().RemoveExtension()
     68           .RemoveExtension().MaybeAsASCII();
     69       origins->push_back(webkit_database::GetOriginFromIdentifier(origin_id));
     70       if (file_paths)
     71         file_paths->push_back(file_path);
     72     }
     73   }
     74 }
     75 
     76 // This will be called after the IndexedDBContext is destroyed.
     77 void ClearSessionOnlyOrigins(
     78     const base::FilePath& indexeddb_path,
     79     scoped_refptr<quota::SpecialStoragePolicy> special_storage_policy) {
     80   // TODO(jsbell): DCHECK that this is running on an IndexedDB thread,
     81   // if a global handle to it is ever available.
     82   std::vector<GURL> origins;
     83   std::vector<base::FilePath> file_paths;
     84   GetAllOriginsAndPaths(indexeddb_path, &origins, &file_paths);
     85   DCHECK_EQ(origins.size(), file_paths.size());
     86   std::vector<base::FilePath>::const_iterator file_path_iter =
     87       file_paths.begin();
     88   for (std::vector<GURL>::const_iterator iter = origins.begin();
     89        iter != origins.end();
     90        ++iter, ++file_path_iter) {
     91     if (!special_storage_policy->IsStorageSessionOnly(*iter))
     92       continue;
     93     if (special_storage_policy->IsStorageProtected(*iter))
     94       continue;
     95     base::DeleteFile(*file_path_iter, true);
     96   }
     97 }
     98 
     99 }  // namespace
    100 
    101 IndexedDBContextImpl::IndexedDBContextImpl(
    102     const base::FilePath& data_path,
    103     quota::SpecialStoragePolicy* special_storage_policy,
    104     quota::QuotaManagerProxy* quota_manager_proxy,
    105     base::SequencedTaskRunner* task_runner)
    106     : force_keep_session_state_(false),
    107       special_storage_policy_(special_storage_policy),
    108       quota_manager_proxy_(quota_manager_proxy),
    109       task_runner_(task_runner) {
    110   IDB_TRACE("init");
    111   if (!data_path.empty())
    112     data_path_ = data_path.Append(kIndexedDBDirectory);
    113   if (quota_manager_proxy) {
    114     quota_manager_proxy->RegisterClient(new IndexedDBQuotaClient(this));
    115   }
    116 }
    117 
    118 IndexedDBFactory* IndexedDBContextImpl::GetIDBFactory() {
    119   DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
    120   if (!factory_) {
    121     // Prime our cache of origins with existing databases so we can
    122     // detect when dbs are newly created.
    123     GetOriginSet();
    124     factory_ = new IndexedDBFactory(this);
    125   }
    126   return factory_;
    127 }
    128 
    129 std::vector<GURL> IndexedDBContextImpl::GetAllOrigins() {
    130   DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
    131   std::vector<GURL> origins;
    132   std::set<GURL>* origins_set = GetOriginSet();
    133   for (std::set<GURL>::const_iterator iter = origins_set->begin();
    134        iter != origins_set->end();
    135        ++iter) {
    136     origins.push_back(*iter);
    137   }
    138   return origins;
    139 }
    140 
    141 std::vector<IndexedDBInfo> IndexedDBContextImpl::GetAllOriginsInfo() {
    142   DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
    143   std::vector<GURL> origins = GetAllOrigins();
    144   std::vector<IndexedDBInfo> result;
    145   for (std::vector<GURL>::const_iterator iter = origins.begin();
    146        iter != origins.end();
    147        ++iter) {
    148     const GURL& origin_url = *iter;
    149 
    150     base::FilePath idb_directory = GetFilePath(origin_url);
    151     size_t connection_count = GetConnectionCount(origin_url);
    152     result.push_back(IndexedDBInfo(origin_url,
    153                                    GetOriginDiskUsage(origin_url),
    154                                    GetOriginLastModified(origin_url),
    155                                    idb_directory,
    156                                    connection_count));
    157   }
    158   return result;
    159 }
    160 
    161 static bool HostNameComparator(const GURL& i, const GURL& j) {
    162   return i.host() < j.host();
    163 }
    164 
    165 ListValue* IndexedDBContextImpl::GetAllOriginsDetails() {
    166   DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
    167   std::vector<GURL> origins = GetAllOrigins();
    168 
    169   std::sort(origins.begin(), origins.end(), HostNameComparator);
    170 
    171   scoped_ptr<ListValue> list(new ListValue());
    172   for (std::vector<GURL>::const_iterator iter = origins.begin();
    173        iter != origins.end();
    174        ++iter) {
    175     const GURL& origin_url = *iter;
    176 
    177     scoped_ptr<DictionaryValue> info(new DictionaryValue());
    178     info->SetString("url", origin_url.spec());
    179     info->SetString("size", ui::FormatBytes(GetOriginDiskUsage(origin_url)));
    180     info->SetDouble("last_modified",
    181                     GetOriginLastModified(origin_url).ToJsTime());
    182     info->SetString("path", GetFilePath(origin_url).value());
    183     info->SetDouble("connection_count", GetConnectionCount(origin_url));
    184 
    185     // This ends up being O(n^2) since we iterate over all open databases
    186     // to extract just those in the origin, and we're iterating over all
    187     // origins in the outer loop.
    188 
    189     if (factory_) {
    190       std::vector<IndexedDBDatabase*> databases =
    191           factory_->GetOpenDatabasesForOrigin(origin_url);
    192       // TODO(jsbell): Sort by name?
    193       scoped_ptr<ListValue> database_list(new ListValue());
    194 
    195       for (std::vector<IndexedDBDatabase*>::iterator it = databases.begin();
    196            it != databases.end();
    197            ++it) {
    198 
    199         const IndexedDBDatabase* db = *it;
    200         scoped_ptr<DictionaryValue> db_info(new DictionaryValue());
    201 
    202         db_info->SetString("name", db->name());
    203         db_info->SetDouble("pending_opens", db->PendingOpenCount());
    204         db_info->SetDouble("pending_upgrades", db->PendingUpgradeCount());
    205         db_info->SetDouble("running_upgrades", db->RunningUpgradeCount());
    206         db_info->SetDouble("pending_deletes", db->PendingDeleteCount());
    207         db_info->SetDouble("connection_count",
    208                            db->ConnectionCount() - db->PendingUpgradeCount() -
    209                                db->RunningUpgradeCount());
    210 
    211         scoped_ptr<ListValue> transaction_list(new ListValue());
    212         std::vector<const IndexedDBTransaction*> transactions =
    213             db->transaction_coordinator().GetTransactions();
    214         for (std::vector<const IndexedDBTransaction*>::iterator trans_it =
    215                  transactions.begin();
    216              trans_it != transactions.end();
    217              ++trans_it) {
    218 
    219           const IndexedDBTransaction* transaction = *trans_it;
    220           scoped_ptr<DictionaryValue> transaction_info(new DictionaryValue());
    221 
    222           const char* kModes[] = { "readonly", "readwrite", "versionchange" };
    223           transaction_info->SetString("mode", kModes[transaction->mode()]);
    224           switch (transaction->state()) {
    225             case IndexedDBTransaction::CREATED:
    226               transaction_info->SetString("status", "blocked");
    227               break;
    228             case IndexedDBTransaction::STARTED:
    229               if (transaction->diagnostics().tasks_scheduled > 0)
    230                 transaction_info->SetString("status", "running");
    231               else
    232                 transaction_info->SetString("status", "started");
    233               break;
    234             case IndexedDBTransaction::FINISHED:
    235               transaction_info->SetString("status", "finished");
    236               break;
    237           }
    238 
    239           transaction_info->SetDouble(
    240               "pid",
    241               IndexedDBDispatcherHost::TransactionIdToProcessId(
    242                   transaction->id()));
    243           transaction_info->SetDouble(
    244               "tid",
    245               IndexedDBDispatcherHost::TransactionIdToRendererTransactionId(
    246                   transaction->id()));
    247           transaction_info->SetDouble(
    248               "age",
    249               (base::Time::Now() - transaction->diagnostics().creation_time)
    250                   .InMillisecondsF());
    251           transaction_info->SetDouble(
    252               "runtime",
    253               (base::Time::Now() - transaction->diagnostics().start_time)
    254                   .InMillisecondsF());
    255           transaction_info->SetDouble(
    256               "tasks_scheduled", transaction->diagnostics().tasks_scheduled);
    257           transaction_info->SetDouble(
    258               "tasks_completed", transaction->diagnostics().tasks_completed);
    259 
    260           scoped_ptr<ListValue> scope(new ListValue());
    261           for (std::set<int64>::const_iterator scope_it =
    262                    transaction->scope().begin();
    263                scope_it != transaction->scope().end();
    264                ++scope_it) {
    265             IndexedDBDatabaseMetadata::ObjectStoreMap::const_iterator it =
    266                 db->metadata().object_stores.find(*scope_it);
    267             if (it != db->metadata().object_stores.end())
    268               scope->AppendString(it->second.name);
    269           }
    270 
    271           transaction_info->Set("scope", scope.release());
    272           transaction_list->Append(transaction_info.release());
    273         }
    274         db_info->Set("transactions", transaction_list.release());
    275 
    276         database_list->Append(db_info.release());
    277       }
    278       info->Set("databases", database_list.release());
    279     }
    280 
    281     list->Append(info.release());
    282   }
    283   return list.release();
    284 }
    285 
    286 int64 IndexedDBContextImpl::GetOriginDiskUsage(const GURL& origin_url) {
    287   DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
    288   if (data_path_.empty() || !IsInOriginSet(origin_url))
    289     return 0;
    290   EnsureDiskUsageCacheInitialized(origin_url);
    291   return origin_size_map_[origin_url];
    292 }
    293 
    294 base::Time IndexedDBContextImpl::GetOriginLastModified(const GURL& origin_url) {
    295   DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
    296   if (data_path_.empty() || !IsInOriginSet(origin_url))
    297     return base::Time();
    298   base::FilePath idb_directory = GetFilePath(origin_url);
    299   base::PlatformFileInfo file_info;
    300   if (!base::GetFileInfo(idb_directory, &file_info))
    301     return base::Time();
    302   return file_info.last_modified;
    303 }
    304 
    305 void IndexedDBContextImpl::DeleteForOrigin(const GURL& origin_url) {
    306   DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
    307   ForceClose(origin_url);
    308   if (data_path_.empty() || !IsInOriginSet(origin_url))
    309     return;
    310 
    311   base::FilePath idb_directory = GetFilePath(origin_url);
    312   EnsureDiskUsageCacheInitialized(origin_url);
    313   bool deleted = LevelDBDatabase::Destroy(idb_directory);
    314   if (!deleted) {
    315     LOG(WARNING) << "Failed to delete LevelDB database: "
    316                  << idb_directory.AsUTF8Unsafe();
    317   } else {
    318     // LevelDB does not delete empty directories; work around this.
    319     // TODO(jsbell): Remove when upstream bug is fixed.
    320     // https://code.google.com/p/leveldb/issues/detail?id=209
    321     const bool kNonRecursive = false;
    322     base::DeleteFile(idb_directory, kNonRecursive);
    323   }
    324 
    325   QueryDiskAndUpdateQuotaUsage(origin_url);
    326   if (deleted) {
    327     RemoveFromOriginSet(origin_url);
    328     origin_size_map_.erase(origin_url);
    329     space_available_map_.erase(origin_url);
    330   }
    331 }
    332 
    333 void IndexedDBContextImpl::ForceClose(const GURL origin_url) {
    334   DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
    335   if (data_path_.empty() || !IsInOriginSet(origin_url))
    336     return;
    337 
    338   if (connections_.find(origin_url) != connections_.end()) {
    339     ConnectionSet& connections = connections_[origin_url];
    340     ConnectionSet::iterator it = connections.begin();
    341     while (it != connections.end()) {
    342       // Remove before closing so callbacks don't double-erase
    343       IndexedDBConnection* connection = *it;
    344       DCHECK(connection->IsConnected());
    345       connections.erase(it++);
    346       connection->ForceClose();
    347     }
    348     DCHECK_EQ(connections_[origin_url].size(), 0UL);
    349     connections_.erase(origin_url);
    350   }
    351   if (factory_)
    352     factory_->ForceClose(origin_url);
    353 }
    354 
    355 size_t IndexedDBContextImpl::GetConnectionCount(const GURL& origin_url) {
    356   DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
    357   if (data_path_.empty() || !IsInOriginSet(origin_url))
    358     return 0;
    359 
    360   if (connections_.find(origin_url) == connections_.end())
    361     return 0;
    362 
    363   return connections_[origin_url].size();
    364 }
    365 
    366 base::FilePath IndexedDBContextImpl::GetFilePath(const GURL& origin_url) const {
    367   std::string origin_id = webkit_database::GetIdentifierFromOrigin(origin_url);
    368   return GetIndexedDBFilePath(origin_id);
    369 }
    370 
    371 base::FilePath IndexedDBContextImpl::GetFilePathForTesting(
    372     const std::string& origin_id) const {
    373   return GetIndexedDBFilePath(origin_id);
    374 }
    375 
    376 void IndexedDBContextImpl::SetTaskRunnerForTesting(
    377     base::SequencedTaskRunner* task_runner) {
    378   DCHECK(!task_runner_);
    379   task_runner_ = task_runner;
    380 }
    381 
    382 void IndexedDBContextImpl::ConnectionOpened(const GURL& origin_url,
    383                                             IndexedDBConnection* connection) {
    384   DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
    385   DCHECK_EQ(connections_[origin_url].count(connection), 0UL);
    386   if (quota_manager_proxy()) {
    387     quota_manager_proxy()->NotifyStorageAccessed(
    388         quota::QuotaClient::kIndexedDatabase,
    389         origin_url,
    390         quota::kStorageTypeTemporary);
    391   }
    392   connections_[origin_url].insert(connection);
    393   if (AddToOriginSet(origin_url)) {
    394     // A newly created db, notify the quota system.
    395     QueryDiskAndUpdateQuotaUsage(origin_url);
    396   } else {
    397     EnsureDiskUsageCacheInitialized(origin_url);
    398   }
    399   QueryAvailableQuota(origin_url);
    400 }
    401 
    402 void IndexedDBContextImpl::ConnectionClosed(const GURL& origin_url,
    403                                             IndexedDBConnection* connection) {
    404   DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
    405   // May not be in the map if connection was forced to close
    406   if (connections_.find(origin_url) == connections_.end() ||
    407       connections_[origin_url].count(connection) != 1)
    408     return;
    409   if (quota_manager_proxy()) {
    410     quota_manager_proxy()->NotifyStorageAccessed(
    411         quota::QuotaClient::kIndexedDatabase,
    412         origin_url,
    413         quota::kStorageTypeTemporary);
    414   }
    415   connections_[origin_url].erase(connection);
    416   if (connections_[origin_url].size() == 0) {
    417     QueryDiskAndUpdateQuotaUsage(origin_url);
    418     connections_.erase(origin_url);
    419   }
    420 }
    421 
    422 void IndexedDBContextImpl::TransactionComplete(const GURL& origin_url) {
    423   DCHECK(connections_.find(origin_url) != connections_.end() &&
    424          connections_[origin_url].size() > 0);
    425   QueryDiskAndUpdateQuotaUsage(origin_url);
    426   QueryAvailableQuota(origin_url);
    427 }
    428 
    429 bool IndexedDBContextImpl::WouldBeOverQuota(const GURL& origin_url,
    430                                             int64 additional_bytes) {
    431   if (space_available_map_.find(origin_url) == space_available_map_.end()) {
    432     // We haven't heard back from the QuotaManager yet, just let it through.
    433     return false;
    434   }
    435   bool over_quota = additional_bytes > space_available_map_[origin_url];
    436   return over_quota;
    437 }
    438 
    439 bool IndexedDBContextImpl::IsOverQuota(const GURL& origin_url) {
    440   const int kOneAdditionalByte = 1;
    441   return WouldBeOverQuota(origin_url, kOneAdditionalByte);
    442 }
    443 
    444 quota::QuotaManagerProxy* IndexedDBContextImpl::quota_manager_proxy() {
    445   return quota_manager_proxy_;
    446 }
    447 
    448 IndexedDBContextImpl::~IndexedDBContextImpl() {
    449   if (factory_) {
    450     TaskRunner()->PostTask(
    451         FROM_HERE, base::Bind(&IndexedDBFactory::ContextDestroyed, factory_));
    452     factory_ = NULL;
    453   }
    454 
    455   if (data_path_.empty())
    456     return;
    457 
    458   if (force_keep_session_state_)
    459     return;
    460 
    461   bool has_session_only_databases =
    462       special_storage_policy_ &&
    463       special_storage_policy_->HasSessionOnlyOrigins();
    464 
    465   // Clearning only session-only databases, and there are none.
    466   if (!has_session_only_databases)
    467     return;
    468 
    469   TaskRunner()->PostTask(
    470       FROM_HERE,
    471       base::Bind(
    472           &ClearSessionOnlyOrigins, data_path_, special_storage_policy_));
    473 }
    474 
    475 base::FilePath IndexedDBContextImpl::GetIndexedDBFilePath(
    476     const std::string& origin_id) const {
    477   DCHECK(!data_path_.empty());
    478   return data_path_.AppendASCII(origin_id).AddExtension(kIndexedDBExtension)
    479       .AddExtension(kLevelDBExtension);
    480 }
    481 
    482 int64 IndexedDBContextImpl::ReadUsageFromDisk(const GURL& origin_url) const {
    483   if (data_path_.empty())
    484     return 0;
    485   base::FilePath file_path = GetFilePath(origin_url);
    486   return base::ComputeDirectorySize(file_path);
    487 }
    488 
    489 void IndexedDBContextImpl::EnsureDiskUsageCacheInitialized(
    490     const GURL& origin_url) {
    491   if (origin_size_map_.find(origin_url) == origin_size_map_.end())
    492     origin_size_map_[origin_url] = ReadUsageFromDisk(origin_url);
    493 }
    494 
    495 void IndexedDBContextImpl::QueryDiskAndUpdateQuotaUsage(
    496     const GURL& origin_url) {
    497   int64 former_disk_usage = origin_size_map_[origin_url];
    498   int64 current_disk_usage = ReadUsageFromDisk(origin_url);
    499   int64 difference = current_disk_usage - former_disk_usage;
    500   if (difference) {
    501     origin_size_map_[origin_url] = current_disk_usage;
    502     // quota_manager_proxy() is NULL in unit tests.
    503     if (quota_manager_proxy()) {
    504       quota_manager_proxy()->NotifyStorageModified(
    505           quota::QuotaClient::kIndexedDatabase,
    506           origin_url,
    507           quota::kStorageTypeTemporary,
    508           difference);
    509     }
    510   }
    511 }
    512 
    513 void IndexedDBContextImpl::GotUsageAndQuota(const GURL& origin_url,
    514                                             quota::QuotaStatusCode status,
    515                                             int64 usage,
    516                                             int64 quota) {
    517   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    518   DCHECK(status == quota::kQuotaStatusOk || status == quota::kQuotaErrorAbort)
    519       << "status was " << status;
    520   if (status == quota::kQuotaErrorAbort) {
    521     // We seem to no longer care to wait around for the answer.
    522     return;
    523   }
    524   TaskRunner()->PostTask(FROM_HERE,
    525                          base::Bind(&IndexedDBContextImpl::GotUpdatedQuota,
    526                                     this,
    527                                     origin_url,
    528                                     usage,
    529                                     quota));
    530 }
    531 
    532 void IndexedDBContextImpl::GotUpdatedQuota(const GURL& origin_url,
    533                                            int64 usage,
    534                                            int64 quota) {
    535   DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
    536   space_available_map_[origin_url] = quota - usage;
    537 }
    538 
    539 void IndexedDBContextImpl::QueryAvailableQuota(const GURL& origin_url) {
    540   if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
    541     DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
    542     if (quota_manager_proxy()) {
    543       BrowserThread::PostTask(
    544           BrowserThread::IO,
    545           FROM_HERE,
    546           base::Bind(
    547               &IndexedDBContextImpl::QueryAvailableQuota, this, origin_url));
    548     }
    549     return;
    550   }
    551   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    552   if (!quota_manager_proxy() || !quota_manager_proxy()->quota_manager())
    553     return;
    554   quota_manager_proxy()->quota_manager()->GetUsageAndQuota(
    555       origin_url,
    556       quota::kStorageTypeTemporary,
    557       base::Bind(&IndexedDBContextImpl::GotUsageAndQuota, this, origin_url));
    558 }
    559 
    560 std::set<GURL>* IndexedDBContextImpl::GetOriginSet() {
    561   if (!origin_set_) {
    562     origin_set_.reset(new std::set<GURL>);
    563     std::vector<GURL> origins;
    564     GetAllOriginsAndPaths(data_path_, &origins, NULL);
    565     for (std::vector<GURL>::const_iterator iter = origins.begin();
    566          iter != origins.end();
    567          ++iter) {
    568       origin_set_->insert(*iter);
    569     }
    570   }
    571   return origin_set_.get();
    572 }
    573 
    574 void IndexedDBContextImpl::ResetCaches() {
    575   origin_set_.reset();
    576   origin_size_map_.clear();
    577   space_available_map_.clear();
    578 }
    579 
    580 base::TaskRunner* IndexedDBContextImpl::TaskRunner() const {
    581   return task_runner_;
    582 }
    583 
    584 }  // namespace content
    585