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