15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "content/browser/indexed_db/indexed_db_context_impl.h"
6c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
7ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include <algorithm>
8f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include <utility>
9ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/command_line.h"
12868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/files/file_enumerator.h"
131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/files/file_util.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
15a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "base/metrics/histogram.h"
16eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/sequenced_task_runner.h"
17868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string_util.h"
18868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
19eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/threading/thread_restrictions.h"
20d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "base/time/time.h"
21ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include "base/values.h"
22eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "content/browser/browser_main_loop.h"
23eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "content/browser/indexed_db/indexed_db_connection.h"
24ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include "content/browser/indexed_db/indexed_db_database.h"
25d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "content/browser/indexed_db/indexed_db_dispatcher_host.h"
265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "content/browser/indexed_db/indexed_db_factory_impl.h"
27c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "content/browser/indexed_db/indexed_db_quota_client.h"
28a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "content/browser/indexed_db/indexed_db_tracing.h"
29ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include "content/browser/indexed_db/indexed_db_transaction.h"
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/browser_thread.h"
31c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "content/public/browser/indexed_db_info.h"
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/common/content_switches.h"
331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "storage/browser/database/database_util.h"
341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "storage/browser/quota/quota_manager_proxy.h"
351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "storage/browser/quota/special_storage_policy.h"
361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "storage/common/database/database_identifier.h"
37ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include "ui/base/text/bytes_formatting.h"
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
39ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochusing base::DictionaryValue;
40ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochusing base::ListValue;
4103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)using storage::DatabaseUtil;
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace content {
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const base::FilePath::CharType IndexedDBContextImpl::kIndexedDBDirectory[] =
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    FILE_PATH_LITERAL("IndexedDB");
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
477d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)static const base::FilePath::CharType kIndexedDBExtension[] =
487d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    FILE_PATH_LITERAL(".indexeddb");
497d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
507d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)static const base::FilePath::CharType kLevelDBExtension[] =
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    FILE_PATH_LITERAL(".leveldb");
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// This may be called after the IndexedDBContext is destroyed.
56868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)void GetAllOriginsAndPaths(const base::FilePath& indexeddb_path,
57868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                           std::vector<GURL>* origins,
58868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                           std::vector<base::FilePath>* file_paths) {
59eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // TODO(jsbell): DCHECK that this is running on an IndexedDB thread,
60eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // if a global handle to it is ever available.
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (indexeddb_path.empty())
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
63868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  base::FileEnumerator file_enumerator(
64868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      indexeddb_path, false, base::FileEnumerator::DIRECTORIES);
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (base::FilePath file_path = file_enumerator.Next(); !file_path.empty();
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       file_path = file_enumerator.Next()) {
677d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    if (file_path.Extension() == kLevelDBExtension &&
687d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)        file_path.RemoveExtension().Extension() == kIndexedDBExtension) {
697d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      std::string origin_id = file_path.BaseName().RemoveExtension()
707d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)          .RemoveExtension().MaybeAsASCII();
7103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      origins->push_back(storage::GetOriginFromIdentifier(origin_id));
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (file_paths)
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        file_paths->push_back(file_path);
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
78eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// This will be called after the IndexedDBContext is destroyed.
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ClearSessionOnlyOrigins(
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const base::FilePath& indexeddb_path,
8103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy) {
82eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // TODO(jsbell): DCHECK that this is running on an IndexedDB thread,
83eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // if a global handle to it is ever available.
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<GURL> origins;
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<base::FilePath> file_paths;
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GetAllOriginsAndPaths(indexeddb_path, &origins, &file_paths);
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(origins.size(), file_paths.size());
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<base::FilePath>::const_iterator file_path_iter =
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      file_paths.begin();
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (std::vector<GURL>::const_iterator iter = origins.begin();
91868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)       iter != origins.end();
92868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)       ++iter, ++file_path_iter) {
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!special_storage_policy->IsStorageSessionOnly(*iter))
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (special_storage_policy->IsStorageProtected(*iter))
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
977dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    base::DeleteFile(*file_path_iter, true);
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)IndexedDBContextImpl::IndexedDBContextImpl(
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const base::FilePath& data_path,
10503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    storage::SpecialStoragePolicy* special_storage_policy,
10603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    storage::QuotaManagerProxy* quota_manager_proxy,
107eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    base::SequencedTaskRunner* task_runner)
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : force_keep_session_state_(false),
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      special_storage_policy_(special_storage_policy),
110eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      quota_manager_proxy_(quota_manager_proxy),
111eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      task_runner_(task_runner) {
112a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  IDB_TRACE("init");
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!data_path.empty())
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    data_path_ = data_path.Append(kIndexedDBDirectory);
1157dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  if (quota_manager_proxy) {
116eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    quota_manager_proxy->RegisterClient(new IndexedDBQuotaClient(this));
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
120eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen MurdochIndexedDBFactory* IndexedDBContextImpl::GetIDBFactory() {
121eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
1221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (!factory_.get()) {
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Prime our cache of origins with existing databases so we can
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // detect when dbs are newly created.
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GetOriginSet();
1265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    factory_ = new IndexedDBFactoryImpl(this);
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  return factory_.get();
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::vector<GURL> IndexedDBContextImpl::GetAllOrigins() {
132eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<GURL> origins;
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::set<GURL>* origins_set = GetOriginSet();
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (std::set<GURL>::const_iterator iter = origins_set->begin();
136868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)       iter != origins_set->end();
137868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)       ++iter) {
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    origins.push_back(*iter);
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return origins;
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
143c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)std::vector<IndexedDBInfo> IndexedDBContextImpl::GetAllOriginsInfo() {
144eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
145c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::vector<GURL> origins = GetAllOrigins();
146c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::vector<IndexedDBInfo> result;
147c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  for (std::vector<GURL>::const_iterator iter = origins.begin();
148868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)       iter != origins.end();
149868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)       ++iter) {
150c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const GURL& origin_url = *iter;
151c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
152c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    base::FilePath idb_directory = GetFilePath(origin_url);
153ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    size_t connection_count = GetConnectionCount(origin_url);
154c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    result.push_back(IndexedDBInfo(origin_url,
155c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                   GetOriginDiskUsage(origin_url),
156c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                   GetOriginLastModified(origin_url),
157ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                                   idb_directory,
158ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                                   connection_count));
159c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
160c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return result;
161c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
162c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
163ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochstatic bool HostNameComparator(const GURL& i, const GURL& j) {
164ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  return i.host() < j.host();
165ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch}
166ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
1675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)base::ListValue* IndexedDBContextImpl::GetAllOriginsDetails() {
168ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
169ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  std::vector<GURL> origins = GetAllOrigins();
170ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
171ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  std::sort(origins.begin(), origins.end(), HostNameComparator);
172ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
1735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  scoped_ptr<base::ListValue> list(new base::ListValue());
174ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  for (std::vector<GURL>::const_iterator iter = origins.begin();
175ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch       iter != origins.end();
176ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch       ++iter) {
177ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    const GURL& origin_url = *iter;
178ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
1795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    scoped_ptr<base::DictionaryValue> info(new base::DictionaryValue());
180ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    info->SetString("url", origin_url.spec());
181ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    info->SetString("size", ui::FormatBytes(GetOriginDiskUsage(origin_url)));
182ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    info->SetDouble("last_modified",
183ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                    GetOriginLastModified(origin_url).ToJsTime());
184116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if (!is_incognito())
185116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      info->SetString("path", GetFilePath(origin_url).value());
186ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    info->SetDouble("connection_count", GetConnectionCount(origin_url));
187ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
188ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    // This ends up being O(n^2) since we iterate over all open databases
189ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    // to extract just those in the origin, and we're iterating over all
190ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    // origins in the outer loop.
191ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
1921320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (factory_.get()) {
1935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      std::pair<IndexedDBFactory::OriginDBMapIterator,
1945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                IndexedDBFactory::OriginDBMapIterator> range =
1951e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)          factory_->GetOpenDatabasesForOrigin(origin_url);
196ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      // TODO(jsbell): Sort by name?
1975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      scoped_ptr<base::ListValue> database_list(new base::ListValue());
198ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
1995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      for (IndexedDBFactory::OriginDBMapIterator it = range.first;
2005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)           it != range.second;
201ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch           ++it) {
2025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        const IndexedDBDatabase* db = it->second;
2035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        scoped_ptr<base::DictionaryValue> db_info(new base::DictionaryValue());
204ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
205ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        db_info->SetString("name", db->name());
206ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        db_info->SetDouble("pending_opens", db->PendingOpenCount());
207ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        db_info->SetDouble("pending_upgrades", db->PendingUpgradeCount());
208ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        db_info->SetDouble("running_upgrades", db->RunningUpgradeCount());
209ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        db_info->SetDouble("pending_deletes", db->PendingDeleteCount());
210ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        db_info->SetDouble("connection_count",
211ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                           db->ConnectionCount() - db->PendingUpgradeCount() -
212ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                               db->RunningUpgradeCount());
213ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
2145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        scoped_ptr<base::ListValue> transaction_list(new base::ListValue());
215ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        std::vector<const IndexedDBTransaction*> transactions =
216ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch            db->transaction_coordinator().GetTransactions();
217ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        for (std::vector<const IndexedDBTransaction*>::iterator trans_it =
218ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                 transactions.begin();
219ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch             trans_it != transactions.end();
220ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch             ++trans_it) {
221ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch          const IndexedDBTransaction* transaction = *trans_it;
2225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          scoped_ptr<base::DictionaryValue> transaction_info(
2235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)              new base::DictionaryValue());
224ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
225ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch          const char* kModes[] = { "readonly", "readwrite", "versionchange" };
226ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch          transaction_info->SetString("mode", kModes[transaction->mode()]);
227f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)          switch (transaction->state()) {
228d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)            case IndexedDBTransaction::CREATED:
229d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)              transaction_info->SetString("status", "blocked");
230d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)              break;
231f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)            case IndexedDBTransaction::STARTED:
232f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)              if (transaction->diagnostics().tasks_scheduled > 0)
233d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                transaction_info->SetString("status", "running");
234d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)              else
235d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                transaction_info->SetString("status", "started");
236d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)              break;
23746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)            case IndexedDBTransaction::COMMITTING:
23846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)              transaction_info->SetString("status", "committing");
23946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)              break;
240f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)            case IndexedDBTransaction::FINISHED:
241f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)              transaction_info->SetString("status", "finished");
242f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)              break;
243d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          }
244d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
245d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          transaction_info->SetDouble(
246d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)              "pid",
247d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)              IndexedDBDispatcherHost::TransactionIdToProcessId(
248d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                  transaction->id()));
249d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          transaction_info->SetDouble(
250d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)              "tid",
251d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)              IndexedDBDispatcherHost::TransactionIdToRendererTransactionId(
252d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                  transaction->id()));
253d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          transaction_info->SetDouble(
254d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)              "age",
255f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)              (base::Time::Now() - transaction->diagnostics().creation_time)
256d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                  .InMillisecondsF());
257d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          transaction_info->SetDouble(
258d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)              "runtime",
259f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)              (base::Time::Now() - transaction->diagnostics().start_time)
260d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                  .InMillisecondsF());
261f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)          transaction_info->SetDouble(
262f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)              "tasks_scheduled", transaction->diagnostics().tasks_scheduled);
263f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)          transaction_info->SetDouble(
264f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)              "tasks_completed", transaction->diagnostics().tasks_completed);
265ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
2665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          scoped_ptr<base::ListValue> scope(new base::ListValue());
267ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch          for (std::set<int64>::const_iterator scope_it =
268ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                   transaction->scope().begin();
269ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch               scope_it != transaction->scope().end();
270ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch               ++scope_it) {
271ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch            IndexedDBDatabaseMetadata::ObjectStoreMap::const_iterator it =
272ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                db->metadata().object_stores.find(*scope_it);
273ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch            if (it != db->metadata().object_stores.end())
274ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch              scope->AppendString(it->second.name);
275ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch          }
276ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
277ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch          transaction_info->Set("scope", scope.release());
278ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch          transaction_list->Append(transaction_info.release());
279ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        }
280ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        db_info->Set("transactions", transaction_list.release());
281ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
282ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        database_list->Append(db_info.release());
283ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      }
284ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      info->Set("databases", database_list.release());
285ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    }
286ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
287ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    list->Append(info.release());
288ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  }
289ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  return list.release();
290ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch}
291ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int64 IndexedDBContextImpl::GetOriginDiskUsage(const GURL& origin_url) {
293eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (data_path_.empty() || !IsInOriginSet(origin_url))
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0;
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EnsureDiskUsageCacheInitialized(origin_url);
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return origin_size_map_[origin_url];
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)base::Time IndexedDBContextImpl::GetOriginLastModified(const GURL& origin_url) {
301eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (data_path_.empty() || !IsInOriginSet(origin_url))
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return base::Time();
304c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  base::FilePath idb_directory = GetFilePath(origin_url);
3055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::File::Info file_info;
306a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (!base::GetFileInfo(idb_directory, &file_info))
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return base::Time();
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return file_info.last_modified;
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void IndexedDBContextImpl::DeleteForOrigin(const GURL& origin_url) {
312eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
313a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  ForceClose(origin_url, FORCE_CLOSE_DELETE_ORIGIN);
314c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (data_path_.empty() || !IsInOriginSet(origin_url))
315c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
316c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
317c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  base::FilePath idb_directory = GetFilePath(origin_url);
318c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EnsureDiskUsageCacheInitialized(origin_url);
319a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  leveldb::Status s = LevelDBDatabase::Destroy(idb_directory);
320a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!s.ok()) {
3214e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    LOG(WARNING) << "Failed to delete LevelDB database: "
3224e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                 << idb_directory.AsUTF8Unsafe();
3234e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  } else {
3244e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // LevelDB does not delete empty directories; work around this.
3254e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // TODO(jsbell): Remove when upstream bug is fixed.
3264e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // https://code.google.com/p/leveldb/issues/detail?id=209
3274e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    const bool kNonRecursive = false;
3284e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    base::DeleteFile(idb_directory, kNonRecursive);
3294e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  }
330c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
331c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  QueryDiskAndUpdateQuotaUsage(origin_url);
332a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (s.ok()) {
333c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    RemoveFromOriginSet(origin_url);
334c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    origin_size_map_.erase(origin_url);
335c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    space_available_map_.erase(origin_url);
336c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
337c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
338c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
339a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void IndexedDBContextImpl::ForceClose(const GURL origin_url,
340a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                      ForceCloseReason reason) {
341eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
342a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  UMA_HISTOGRAM_ENUMERATION("WebCore.IndexedDB.Context.ForceCloseReason",
343a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                            reason,
344a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                            FORCE_CLOSE_REASON_MAX);
345a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (data_path_.empty() || !IsInOriginSet(origin_url))
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (factory_.get())
350a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    factory_->ForceClose(origin_url);
3515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK_EQ(0UL, GetConnectionCount(origin_url));
352c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
354ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochsize_t IndexedDBContextImpl::GetConnectionCount(const GURL& origin_url) {
355ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
356ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  if (data_path_.empty() || !IsInOriginSet(origin_url))
357ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    return 0;
358ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
3591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (!factory_.get())
360ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    return 0;
361ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
3625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return factory_->GetConnectionCount(origin_url);
363ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch}
364ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
3651e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)base::FilePath IndexedDBContextImpl::GetFilePath(const GURL& origin_url) const {
36603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  std::string origin_id = storage::GetIdentifierFromOrigin(origin_url);
367c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return GetIndexedDBFilePath(origin_id);
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)base::FilePath IndexedDBContextImpl::GetFilePathForTesting(
3717d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    const std::string& origin_id) const {
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return GetIndexedDBFilePath(origin_id);
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
375eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid IndexedDBContextImpl::SetTaskRunnerForTesting(
376eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    base::SequencedTaskRunner* task_runner) {
3771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  DCHECK(!task_runner_.get());
378eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  task_runner_ = task_runner;
379eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
380eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void IndexedDBContextImpl::ConnectionOpened(const GURL& origin_url,
382eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                            IndexedDBConnection* connection) {
383eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (quota_manager_proxy()) {
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    quota_manager_proxy()->NotifyStorageAccessed(
38603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        storage::QuotaClient::kIndexedDatabase,
387868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        origin_url,
38803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        storage::kStorageTypeTemporary);
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (AddToOriginSet(origin_url)) {
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // A newly created db, notify the quota system.
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    QueryDiskAndUpdateQuotaUsage(origin_url);
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EnsureDiskUsageCacheInitialized(origin_url);
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  QueryAvailableQuota(origin_url);
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void IndexedDBContextImpl::ConnectionClosed(const GURL& origin_url,
400eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                            IndexedDBConnection* connection) {
401eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (quota_manager_proxy()) {
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    quota_manager_proxy()->NotifyStorageAccessed(
40403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        storage::QuotaClient::kIndexedDatabase,
405868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        origin_url,
40603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        storage::kStorageTypeTemporary);
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4081320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (factory_.get() && factory_->GetConnectionCount(origin_url) == 0)
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    QueryDiskAndUpdateQuotaUsage(origin_url);
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void IndexedDBContextImpl::TransactionComplete(const GURL& origin_url) {
4131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  DCHECK(!factory_.get() || factory_->GetConnectionCount(origin_url) > 0);
4145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  QueryDiskAndUpdateQuotaUsage(origin_url);
4155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  QueryAvailableQuota(origin_url);
4165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
4175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void IndexedDBContextImpl::DatabaseDeleted(const GURL& origin_url) {
4195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  AddToOriginSet(origin_url);
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  QueryDiskAndUpdateQuotaUsage(origin_url);
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  QueryAvailableQuota(origin_url);
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool IndexedDBContextImpl::WouldBeOverQuota(const GURL& origin_url,
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                            int64 additional_bytes) {
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (space_available_map_.find(origin_url) == space_available_map_.end()) {
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We haven't heard back from the QuotaManager yet, just let it through.
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool over_quota = additional_bytes > space_available_map_[origin_url];
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return over_quota;
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool IndexedDBContextImpl::IsOverQuota(const GURL& origin_url) {
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int kOneAdditionalByte = 1;
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return WouldBeOverQuota(origin_url, kOneAdditionalByte);
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
43903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)storage::QuotaManagerProxy* IndexedDBContextImpl::quota_manager_proxy() {
4401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  return quota_manager_proxy_.get();
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)IndexedDBContextImpl::~IndexedDBContextImpl() {
4441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (factory_.get()) {
4451e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    TaskRunner()->PostTask(
4461e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        FROM_HERE, base::Bind(&IndexedDBFactory::ContextDestroyed, factory_));
447ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    factory_ = NULL;
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (data_path_.empty())
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (force_keep_session_state_)
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool has_session_only_databases =
4571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      special_storage_policy_.get() &&
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      special_storage_policy_->HasSessionOnlyOrigins();
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
46046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  // Clearing only session-only databases, and there are none.
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!has_session_only_databases)
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
464eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  TaskRunner()->PostTask(
465868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      FROM_HERE,
466868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      base::Bind(
467868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)          &ClearSessionOnlyOrigins, data_path_, special_storage_policy_));
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)base::FilePath IndexedDBContextImpl::GetIndexedDBFilePath(
4717d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    const std::string& origin_id) const {
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!data_path_.empty());
473eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return data_path_.AppendASCII(origin_id).AddExtension(kIndexedDBExtension)
474eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      .AddExtension(kLevelDBExtension);
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int64 IndexedDBContextImpl::ReadUsageFromDisk(const GURL& origin_url) const {
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (data_path_.empty())
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0;
4801e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  base::FilePath file_path = GetFilePath(origin_url);
481eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return base::ComputeDirectorySize(file_path);
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void IndexedDBContextImpl::EnsureDiskUsageCacheInitialized(
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const GURL& origin_url) {
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (origin_size_map_.find(origin_url) == origin_size_map_.end())
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    origin_size_map_[origin_url] = ReadUsageFromDisk(origin_url);
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void IndexedDBContextImpl::QueryDiskAndUpdateQuotaUsage(
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const GURL& origin_url) {
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int64 former_disk_usage = origin_size_map_[origin_url];
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int64 current_disk_usage = ReadUsageFromDisk(origin_url);
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int64 difference = current_disk_usage - former_disk_usage;
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (difference) {
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    origin_size_map_[origin_url] = current_disk_usage;
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // quota_manager_proxy() is NULL in unit tests.
498868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    if (quota_manager_proxy()) {
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      quota_manager_proxy()->NotifyStorageModified(
50003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)          storage::QuotaClient::kIndexedDatabase,
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          origin_url,
50203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)          storage::kStorageTypeTemporary,
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          difference);
504868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    }
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void IndexedDBContextImpl::GotUsageAndQuota(const GURL& origin_url,
50903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                                            storage::QuotaStatusCode status,
510868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                                            int64 usage,
511868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                                            int64 quota) {
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
51303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  DCHECK(status == storage::kQuotaStatusOk ||
51403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)         status == storage::kQuotaErrorAbort)
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      << "status was " << status;
51603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  if (status == storage::kQuotaErrorAbort) {
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We seem to no longer care to wait around for the answer.
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
520eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  TaskRunner()->PostTask(FROM_HERE,
521eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                         base::Bind(&IndexedDBContextImpl::GotUpdatedQuota,
522eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                    this,
523eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                    origin_url,
524eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                    usage,
525eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                    quota));
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
528868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)void IndexedDBContextImpl::GotUpdatedQuota(const GURL& origin_url,
529868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                                           int64 usage,
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                           int64 quota) {
531eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  space_available_map_[origin_url] = quota - usage;
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void IndexedDBContextImpl::QueryAvailableQuota(const GURL& origin_url) {
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
537eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    DCHECK(TaskRunner()->RunsTasksOnCurrentThread());
538868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    if (quota_manager_proxy()) {
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      BrowserThread::PostTask(
540868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)          BrowserThread::IO,
541868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)          FROM_HERE,
542868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)          base::Bind(
543868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)              &IndexedDBContextImpl::QueryAvailableQuota, this, origin_url));
544868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    }
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!quota_manager_proxy() || !quota_manager_proxy()->quota_manager())
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  quota_manager_proxy()->quota_manager()->GetUsageAndQuota(
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      origin_url,
55203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      storage::kStorageTypeTemporary,
5535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::Bind(&IndexedDBContextImpl::GotUsageAndQuota, this, origin_url));
5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::set<GURL>* IndexedDBContextImpl::GetOriginSet() {
557c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!origin_set_) {
5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    origin_set_.reset(new std::set<GURL>);
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::vector<GURL> origins;
5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GetAllOriginsAndPaths(data_path_, &origins, NULL);
5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (std::vector<GURL>::const_iterator iter = origins.begin();
562868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)         iter != origins.end();
563868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)         ++iter) {
5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      origin_set_->insert(*iter);
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return origin_set_.get();
5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void IndexedDBContextImpl::ResetCaches() {
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  origin_set_.reset();
5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  origin_size_map_.clear();
5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  space_available_map_.clear();
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)base::SequencedTaskRunner* IndexedDBContextImpl::TaskRunner() const {
5771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  return task_runner_.get();
578eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
579eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace content
581