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/files/file_enumerator.h"
     13 #include "base/files/file_util.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_impl.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 "storage/browser/database/database_util.h"
     34 #include "storage/browser/quota/quota_manager_proxy.h"
     35 #include "storage/browser/quota/special_storage_policy.h"
     36 #include "storage/common/database/database_identifier.h"
     37 #include "ui/base/text/bytes_formatting.h"
     38 
     39 using base::DictionaryValue;
     40 using base::ListValue;
     41 using storage::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(storage::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<storage::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     storage::SpecialStoragePolicy* special_storage_policy,
    106     storage::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_.get()) {
    123     // Prime our cache of origins with existing databases so we can
    124     // detect when dbs are newly created.
    125     GetOriginSet();
    126     factory_ = new IndexedDBFactoryImpl(this);
    127   }
    128   return factory_.get();
    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     if (!is_incognito())
    185       info->SetString("path", GetFilePath(origin_url).value());
    186     info->SetDouble("connection_count", GetConnectionCount(origin_url));
    187 
    188     // This ends up being O(n^2) since we iterate over all open databases
    189     // to extract just those in the origin, and we're iterating over all
    190     // origins in the outer loop.
    191 
    192     if (factory_.get()) {
    193       std::pair<IndexedDBFactory::OriginDBMapIterator,
    194                 IndexedDBFactory::OriginDBMapIterator> range =
    195           factory_->GetOpenDatabasesForOrigin(origin_url);
    196       // TODO(jsbell): Sort by name?
    197       scoped_ptr<base::ListValue> database_list(new base::ListValue());
    198 
    199       for (IndexedDBFactory::OriginDBMapIterator it = range.first;
    200            it != range.second;
    201            ++it) {
    202         const IndexedDBDatabase* db = it->second;
    203         scoped_ptr<base::DictionaryValue> db_info(new base::DictionaryValue());
    204 
    205         db_info->SetString("name", db->name());
    206         db_info->SetDouble("pending_opens", db->PendingOpenCount());
    207         db_info->SetDouble("pending_upgrades", db->PendingUpgradeCount());
    208         db_info->SetDouble("running_upgrades", db->RunningUpgradeCount());
    209         db_info->SetDouble("pending_deletes", db->PendingDeleteCount());
    210         db_info->SetDouble("connection_count",
    211                            db->ConnectionCount() - db->PendingUpgradeCount() -
    212                                db->RunningUpgradeCount());
    213 
    214         scoped_ptr<base::ListValue> transaction_list(new base::ListValue());
    215         std::vector<const IndexedDBTransaction*> transactions =
    216             db->transaction_coordinator().GetTransactions();
    217         for (std::vector<const IndexedDBTransaction*>::iterator trans_it =
    218                  transactions.begin();
    219              trans_it != transactions.end();
    220              ++trans_it) {
    221           const IndexedDBTransaction* transaction = *trans_it;
    222           scoped_ptr<base::DictionaryValue> transaction_info(
    223               new base::DictionaryValue());
    224 
    225           const char* kModes[] = { "readonly", "readwrite", "versionchange" };
    226           transaction_info->SetString("mode", kModes[transaction->mode()]);
    227           switch (transaction->state()) {
    228             case IndexedDBTransaction::CREATED:
    229               transaction_info->SetString("status", "blocked");
    230               break;
    231             case IndexedDBTransaction::STARTED:
    232               if (transaction->diagnostics().tasks_scheduled > 0)
    233                 transaction_info->SetString("status", "running");
    234               else
    235                 transaction_info->SetString("status", "started");
    236               break;
    237             case IndexedDBTransaction::COMMITTING:
    238               transaction_info->SetString("status", "committing");
    239               break;
    240             case IndexedDBTransaction::FINISHED:
    241               transaction_info->SetString("status", "finished");
    242               break;
    243           }
    244 
    245           transaction_info->SetDouble(
    246               "pid",
    247               IndexedDBDispatcherHost::TransactionIdToProcessId(
    248                   transaction->id()));
    249           transaction_info->SetDouble(
    250               "tid",
    251               IndexedDBDispatcherHost::TransactionIdToRendererTransactionId(
    252                   transaction->id()));
    253           transaction_info->SetDouble(
    254               "age",
    255               (base::Time::Now() - transaction->diagnostics().creation_time)
    256                   .InMillisecondsF());
    257           transaction_info->SetDouble(
    258               "runtime",
    259               (base::Time::Now() - transaction->diagnostics().start_time)
    260                   .InMillisecondsF());
    261           transaction_info->SetDouble(
    262               "tasks_scheduled", transaction->diagnostics().tasks_scheduled);
    263           transaction_info->SetDouble(
    264               "tasks_completed", transaction->diagnostics().tasks_completed);
    265 
    266           scoped_ptr<base::ListValue> scope(new base::ListValue());
    267           for (std::set<int64>::const_iterator scope_it =
    268                    transaction->scope().begin();
    269                scope_it != transaction->scope().end();
    270                ++scope_it) {
    271             IndexedDBDatabaseMetadata::ObjectStoreMap::const_iterator it =
    272                 db->metadata().object_stores.find(*scope_it);
    273             if (it != db->metadata().object_stores.end())
    274               scope->AppendString(it->second.name);
    275           }
    276 
    277           transaction_info->Set("scope", scope.release());
    278           transaction_list->Append(transaction_info.release());
    279         }
    280         db_info->Set("transactions", transaction_list.release());
    281 
    282         database_list->Append(db_info.release());
    283       }
    284       info->Set("databases", database_list.release());
    285     }
    286 
    287     list->Append(info.release());
    288   }
    289   return list.release();
    290 }
    291 
    292 int64 IndexedDBContextImpl::GetOriginDiskUsage(const GURL& origin_url) {
    293   DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
    294   if (data_path_.empty() || !IsInOriginSet(origin_url))
    295     return 0;
    296   EnsureDiskUsageCacheInitialized(origin_url);
    297   return origin_size_map_[origin_url];
    298 }
    299 
    300 base::Time IndexedDBContextImpl::GetOriginLastModified(const GURL& origin_url) {
    301   DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
    302   if (data_path_.empty() || !IsInOriginSet(origin_url))
    303     return base::Time();
    304   base::FilePath idb_directory = GetFilePath(origin_url);
    305   base::File::Info file_info;
    306   if (!base::GetFileInfo(idb_directory, &file_info))
    307     return base::Time();
    308   return file_info.last_modified;
    309 }
    310 
    311 void IndexedDBContextImpl::DeleteForOrigin(const GURL& origin_url) {
    312   DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
    313   ForceClose(origin_url, FORCE_CLOSE_DELETE_ORIGIN);
    314   if (data_path_.empty() || !IsInOriginSet(origin_url))
    315     return;
    316 
    317   base::FilePath idb_directory = GetFilePath(origin_url);
    318   EnsureDiskUsageCacheInitialized(origin_url);
    319   leveldb::Status s = LevelDBDatabase::Destroy(idb_directory);
    320   if (!s.ok()) {
    321     LOG(WARNING) << "Failed to delete LevelDB database: "
    322                  << idb_directory.AsUTF8Unsafe();
    323   } else {
    324     // LevelDB does not delete empty directories; work around this.
    325     // TODO(jsbell): Remove when upstream bug is fixed.
    326     // https://code.google.com/p/leveldb/issues/detail?id=209
    327     const bool kNonRecursive = false;
    328     base::DeleteFile(idb_directory, kNonRecursive);
    329   }
    330 
    331   QueryDiskAndUpdateQuotaUsage(origin_url);
    332   if (s.ok()) {
    333     RemoveFromOriginSet(origin_url);
    334     origin_size_map_.erase(origin_url);
    335     space_available_map_.erase(origin_url);
    336   }
    337 }
    338 
    339 void IndexedDBContextImpl::ForceClose(const GURL origin_url,
    340                                       ForceCloseReason reason) {
    341   DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
    342   UMA_HISTOGRAM_ENUMERATION("WebCore.IndexedDB.Context.ForceCloseReason",
    343                             reason,
    344                             FORCE_CLOSE_REASON_MAX);
    345 
    346   if (data_path_.empty() || !IsInOriginSet(origin_url))
    347     return;
    348 
    349   if (factory_.get())
    350     factory_->ForceClose(origin_url);
    351   DCHECK_EQ(0UL, GetConnectionCount(origin_url));
    352 }
    353 
    354 size_t IndexedDBContextImpl::GetConnectionCount(const GURL& origin_url) {
    355   DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
    356   if (data_path_.empty() || !IsInOriginSet(origin_url))
    357     return 0;
    358 
    359   if (!factory_.get())
    360     return 0;
    361 
    362   return factory_->GetConnectionCount(origin_url);
    363 }
    364 
    365 base::FilePath IndexedDBContextImpl::GetFilePath(const GURL& origin_url) const {
    366   std::string origin_id = storage::GetIdentifierFromOrigin(origin_url);
    367   return GetIndexedDBFilePath(origin_id);
    368 }
    369 
    370 base::FilePath IndexedDBContextImpl::GetFilePathForTesting(
    371     const std::string& origin_id) const {
    372   return GetIndexedDBFilePath(origin_id);
    373 }
    374 
    375 void IndexedDBContextImpl::SetTaskRunnerForTesting(
    376     base::SequencedTaskRunner* task_runner) {
    377   DCHECK(!task_runner_.get());
    378   task_runner_ = task_runner;
    379 }
    380 
    381 void IndexedDBContextImpl::ConnectionOpened(const GURL& origin_url,
    382                                             IndexedDBConnection* connection) {
    383   DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
    384   if (quota_manager_proxy()) {
    385     quota_manager_proxy()->NotifyStorageAccessed(
    386         storage::QuotaClient::kIndexedDatabase,
    387         origin_url,
    388         storage::kStorageTypeTemporary);
    389   }
    390   if (AddToOriginSet(origin_url)) {
    391     // A newly created db, notify the quota system.
    392     QueryDiskAndUpdateQuotaUsage(origin_url);
    393   } else {
    394     EnsureDiskUsageCacheInitialized(origin_url);
    395   }
    396   QueryAvailableQuota(origin_url);
    397 }
    398 
    399 void IndexedDBContextImpl::ConnectionClosed(const GURL& origin_url,
    400                                             IndexedDBConnection* connection) {
    401   DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
    402   if (quota_manager_proxy()) {
    403     quota_manager_proxy()->NotifyStorageAccessed(
    404         storage::QuotaClient::kIndexedDatabase,
    405         origin_url,
    406         storage::kStorageTypeTemporary);
    407   }
    408   if (factory_.get() && factory_->GetConnectionCount(origin_url) == 0)
    409     QueryDiskAndUpdateQuotaUsage(origin_url);
    410 }
    411 
    412 void IndexedDBContextImpl::TransactionComplete(const GURL& origin_url) {
    413   DCHECK(!factory_.get() || factory_->GetConnectionCount(origin_url) > 0);
    414   QueryDiskAndUpdateQuotaUsage(origin_url);
    415   QueryAvailableQuota(origin_url);
    416 }
    417 
    418 void IndexedDBContextImpl::DatabaseDeleted(const GURL& origin_url) {
    419   AddToOriginSet(origin_url);
    420   QueryDiskAndUpdateQuotaUsage(origin_url);
    421   QueryAvailableQuota(origin_url);
    422 }
    423 
    424 bool IndexedDBContextImpl::WouldBeOverQuota(const GURL& origin_url,
    425                                             int64 additional_bytes) {
    426   if (space_available_map_.find(origin_url) == space_available_map_.end()) {
    427     // We haven't heard back from the QuotaManager yet, just let it through.
    428     return false;
    429   }
    430   bool over_quota = additional_bytes > space_available_map_[origin_url];
    431   return over_quota;
    432 }
    433 
    434 bool IndexedDBContextImpl::IsOverQuota(const GURL& origin_url) {
    435   const int kOneAdditionalByte = 1;
    436   return WouldBeOverQuota(origin_url, kOneAdditionalByte);
    437 }
    438 
    439 storage::QuotaManagerProxy* IndexedDBContextImpl::quota_manager_proxy() {
    440   return quota_manager_proxy_.get();
    441 }
    442 
    443 IndexedDBContextImpl::~IndexedDBContextImpl() {
    444   if (factory_.get()) {
    445     TaskRunner()->PostTask(
    446         FROM_HERE, base::Bind(&IndexedDBFactory::ContextDestroyed, factory_));
    447     factory_ = NULL;
    448   }
    449 
    450   if (data_path_.empty())
    451     return;
    452 
    453   if (force_keep_session_state_)
    454     return;
    455 
    456   bool has_session_only_databases =
    457       special_storage_policy_.get() &&
    458       special_storage_policy_->HasSessionOnlyOrigins();
    459 
    460   // Clearing only session-only databases, and there are none.
    461   if (!has_session_only_databases)
    462     return;
    463 
    464   TaskRunner()->PostTask(
    465       FROM_HERE,
    466       base::Bind(
    467           &ClearSessionOnlyOrigins, data_path_, special_storage_policy_));
    468 }
    469 
    470 base::FilePath IndexedDBContextImpl::GetIndexedDBFilePath(
    471     const std::string& origin_id) const {
    472   DCHECK(!data_path_.empty());
    473   return data_path_.AppendASCII(origin_id).AddExtension(kIndexedDBExtension)
    474       .AddExtension(kLevelDBExtension);
    475 }
    476 
    477 int64 IndexedDBContextImpl::ReadUsageFromDisk(const GURL& origin_url) const {
    478   if (data_path_.empty())
    479     return 0;
    480   base::FilePath file_path = GetFilePath(origin_url);
    481   return base::ComputeDirectorySize(file_path);
    482 }
    483 
    484 void IndexedDBContextImpl::EnsureDiskUsageCacheInitialized(
    485     const GURL& origin_url) {
    486   if (origin_size_map_.find(origin_url) == origin_size_map_.end())
    487     origin_size_map_[origin_url] = ReadUsageFromDisk(origin_url);
    488 }
    489 
    490 void IndexedDBContextImpl::QueryDiskAndUpdateQuotaUsage(
    491     const GURL& origin_url) {
    492   int64 former_disk_usage = origin_size_map_[origin_url];
    493   int64 current_disk_usage = ReadUsageFromDisk(origin_url);
    494   int64 difference = current_disk_usage - former_disk_usage;
    495   if (difference) {
    496     origin_size_map_[origin_url] = current_disk_usage;
    497     // quota_manager_proxy() is NULL in unit tests.
    498     if (quota_manager_proxy()) {
    499       quota_manager_proxy()->NotifyStorageModified(
    500           storage::QuotaClient::kIndexedDatabase,
    501           origin_url,
    502           storage::kStorageTypeTemporary,
    503           difference);
    504     }
    505   }
    506 }
    507 
    508 void IndexedDBContextImpl::GotUsageAndQuota(const GURL& origin_url,
    509                                             storage::QuotaStatusCode status,
    510                                             int64 usage,
    511                                             int64 quota) {
    512   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    513   DCHECK(status == storage::kQuotaStatusOk ||
    514          status == storage::kQuotaErrorAbort)
    515       << "status was " << status;
    516   if (status == storage::kQuotaErrorAbort) {
    517     // We seem to no longer care to wait around for the answer.
    518     return;
    519   }
    520   TaskRunner()->PostTask(FROM_HERE,
    521                          base::Bind(&IndexedDBContextImpl::GotUpdatedQuota,
    522                                     this,
    523                                     origin_url,
    524                                     usage,
    525                                     quota));
    526 }
    527 
    528 void IndexedDBContextImpl::GotUpdatedQuota(const GURL& origin_url,
    529                                            int64 usage,
    530                                            int64 quota) {
    531   DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
    532   space_available_map_[origin_url] = quota - usage;
    533 }
    534 
    535 void IndexedDBContextImpl::QueryAvailableQuota(const GURL& origin_url) {
    536   if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
    537     DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
    538     if (quota_manager_proxy()) {
    539       BrowserThread::PostTask(
    540           BrowserThread::IO,
    541           FROM_HERE,
    542           base::Bind(
    543               &IndexedDBContextImpl::QueryAvailableQuota, this, origin_url));
    544     }
    545     return;
    546   }
    547   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    548   if (!quota_manager_proxy() || !quota_manager_proxy()->quota_manager())
    549     return;
    550   quota_manager_proxy()->quota_manager()->GetUsageAndQuota(
    551       origin_url,
    552       storage::kStorageTypeTemporary,
    553       base::Bind(&IndexedDBContextImpl::GotUsageAndQuota, this, origin_url));
    554 }
    555 
    556 std::set<GURL>* IndexedDBContextImpl::GetOriginSet() {
    557   if (!origin_set_) {
    558     origin_set_.reset(new std::set<GURL>);
    559     std::vector<GURL> origins;
    560     GetAllOriginsAndPaths(data_path_, &origins, NULL);
    561     for (std::vector<GURL>::const_iterator iter = origins.begin();
    562          iter != origins.end();
    563          ++iter) {
    564       origin_set_->insert(*iter);
    565     }
    566   }
    567   return origin_set_.get();
    568 }
    569 
    570 void IndexedDBContextImpl::ResetCaches() {
    571   origin_set_.reset();
    572   origin_size_map_.clear();
    573   space_available_map_.clear();
    574 }
    575 
    576 base::SequencedTaskRunner* IndexedDBContextImpl::TaskRunner() const {
    577   return task_runner_.get();
    578 }
    579 
    580 }  // namespace content
    581