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.
GetAllOriginsAndPaths(const base::FilePath & indexeddb_path,std::vector<GURL> * origins,std::vector<base::FilePath> * file_paths)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.
ClearSessionOnlyOrigins(const base::FilePath & indexeddb_path,scoped_refptr<quota::SpecialStoragePolicy> special_storage_policy)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
IndexedDBContextImpl(const base::FilePath & data_path,quota::SpecialStoragePolicy * special_storage_policy,quota::QuotaManagerProxy * quota_manager_proxy,base::SequencedTaskRunner * task_runner)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
GetIDBFactory()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
GetAllOrigins()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
GetAllOriginsInfo()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
HostNameComparator(const GURL & i,const GURL & j)163 static bool HostNameComparator(const GURL& i, const GURL& j) {
164 return i.host() < j.host();
165 }
166
GetAllOriginsDetails()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
GetOriginDiskUsage(const GURL & origin_url)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
GetOriginLastModified(const GURL & origin_url)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
DeleteForOrigin(const GURL & origin_url)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
ForceClose(const GURL origin_url,ForceCloseReason reason)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
GetConnectionCount(const GURL & origin_url)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
GetFilePath(const GURL & origin_url) const364 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
GetFilePathForTesting(const std::string & origin_id) const369 base::FilePath IndexedDBContextImpl::GetFilePathForTesting(
370 const std::string& origin_id) const {
371 return GetIndexedDBFilePath(origin_id);
372 }
373
SetTaskRunnerForTesting(base::SequencedTaskRunner * task_runner)374 void IndexedDBContextImpl::SetTaskRunnerForTesting(
375 base::SequencedTaskRunner* task_runner) {
376 DCHECK(!task_runner_);
377 task_runner_ = task_runner;
378 }
379
ConnectionOpened(const GURL & origin_url,IndexedDBConnection * connection)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
ConnectionClosed(const GURL & origin_url,IndexedDBConnection * connection)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
TransactionComplete(const GURL & origin_url)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
DatabaseDeleted(const GURL & origin_url)417 void IndexedDBContextImpl::DatabaseDeleted(const GURL& origin_url) {
418 AddToOriginSet(origin_url);
419 QueryDiskAndUpdateQuotaUsage(origin_url);
420 QueryAvailableQuota(origin_url);
421 }
422
WouldBeOverQuota(const GURL & origin_url,int64 additional_bytes)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
IsOverQuota(const GURL & origin_url)433 bool IndexedDBContextImpl::IsOverQuota(const GURL& origin_url) {
434 const int kOneAdditionalByte = 1;
435 return WouldBeOverQuota(origin_url, kOneAdditionalByte);
436 }
437
quota_manager_proxy()438 quota::QuotaManagerProxy* IndexedDBContextImpl::quota_manager_proxy() {
439 return quota_manager_proxy_;
440 }
441
~IndexedDBContextImpl()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
GetIndexedDBFilePath(const std::string & origin_id) const469 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
ReadUsageFromDisk(const GURL & origin_url) const476 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
EnsureDiskUsageCacheInitialized(const GURL & origin_url)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
QueryDiskAndUpdateQuotaUsage(const GURL & origin_url)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
GotUsageAndQuota(const GURL & origin_url,quota::QuotaStatusCode status,int64 usage,int64 quota)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
GotUpdatedQuota(const GURL & origin_url,int64 usage,int64 quota)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
QueryAvailableQuota(const GURL & origin_url)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
GetOriginSet()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
ResetCaches()568 void IndexedDBContextImpl::ResetCaches() {
569 origin_set_.reset();
570 origin_size_map_.clear();
571 space_available_map_.clear();
572 }
573
TaskRunner() const574 base::TaskRunner* IndexedDBContextImpl::TaskRunner() const {
575 return task_runner_;
576 }
577
578 } // namespace content
579