1// Copyright 2014 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 "base/bind.h"
6#include "base/location.h"
7#include "base/stl_util.h"
8#include "base/task_runner.h"
9#include "content/browser/indexed_db/indexed_db_active_blob_registry.h"
10#include "content/browser/indexed_db/indexed_db_backing_store.h"
11#include "content/browser/indexed_db/indexed_db_factory.h"
12#include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
13
14namespace content {
15
16IndexedDBActiveBlobRegistry::IndexedDBActiveBlobRegistry(
17    IndexedDBBackingStore* backing_store)
18    : backing_store_(backing_store), weak_factory_(this) {}
19
20IndexedDBActiveBlobRegistry::~IndexedDBActiveBlobRegistry() {
21}
22
23void IndexedDBActiveBlobRegistry::AddBlobRef(int64 database_id,
24                                             int64 blob_key) {
25  DCHECK(backing_store_);
26  DCHECK(backing_store_->task_runner()->RunsTasksOnCurrentThread());
27  DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
28  DCHECK(DatabaseMetaDataKey::IsValidBlobKey(blob_key));
29  DCHECK(!ContainsKey(deleted_dbs_, database_id));
30  bool need_ref = use_tracker_.empty();
31  SingleDBMap& single_db_map = use_tracker_[database_id];
32  SingleDBMap::iterator iter = single_db_map.find(blob_key);
33  if (iter == single_db_map.end()) {
34    single_db_map[blob_key] = false;
35    if (need_ref) {
36      backing_store_->factory()->ReportOutstandingBlobs(
37          backing_store_->origin_url(), true);
38    }
39  } else {
40    DCHECK(!need_ref);
41    DCHECK(!iter->second);  // You can't add a reference once it's been deleted.
42  }
43}
44
45void IndexedDBActiveBlobRegistry::ReleaseBlobRef(int64 database_id,
46                                                 int64 blob_key) {
47  DCHECK(backing_store_);
48  DCHECK(backing_store_->task_runner()->RunsTasksOnCurrentThread());
49  DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
50  DCHECK(DatabaseMetaDataKey::IsValidBlobKey(blob_key));
51  AllDBsMap::iterator db_pair = use_tracker_.find(database_id);
52  if (db_pair == use_tracker_.end()) {
53    NOTREACHED();
54    return;
55  }
56  SingleDBMap& single_db = db_pair->second;
57  SingleDBMap::iterator blob_pair = single_db.find(blob_key);
58  if (blob_pair == single_db.end()) {
59    NOTREACHED();
60    return;
61  }
62  bool delete_in_backend = false;
63  DeletedDBSet::iterator db_to_delete = deleted_dbs_.find(database_id);
64  bool db_marked_for_deletion = db_to_delete != deleted_dbs_.end();
65  // Don't bother deleting the file if we're going to delete its whole
66  // database directory soon.
67  delete_in_backend = blob_pair->second && !db_marked_for_deletion;
68  single_db.erase(blob_pair);
69  if (single_db.empty()) {
70    use_tracker_.erase(db_pair);
71    if (db_marked_for_deletion) {
72      delete_in_backend = true;
73      blob_key = DatabaseMetaDataKey::kAllBlobsKey;
74      deleted_dbs_.erase(db_to_delete);
75    }
76  }
77  if (delete_in_backend)
78    backing_store_->ReportBlobUnused(database_id, blob_key);
79  if (use_tracker_.empty()) {
80    backing_store_->factory()->ReportOutstandingBlobs(
81        backing_store_->origin_url(), false);
82  }
83}
84
85bool IndexedDBActiveBlobRegistry::MarkDeletedCheckIfUsed(int64 database_id,
86                                                         int64 blob_key) {
87  DCHECK(backing_store_);
88  DCHECK(backing_store_->task_runner()->RunsTasksOnCurrentThread());
89  DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
90  AllDBsMap::iterator db_pair = use_tracker_.find(database_id);
91  if (db_pair == use_tracker_.end())
92    return false;
93
94  if (blob_key == DatabaseMetaDataKey::kAllBlobsKey) {
95    deleted_dbs_.insert(database_id);
96    return true;
97  }
98
99  SingleDBMap& single_db = db_pair->second;
100  SingleDBMap::iterator iter = single_db.find(blob_key);
101  if (iter == single_db.end())
102    return false;
103
104  iter->second = true;
105  return true;
106}
107
108void IndexedDBActiveBlobRegistry::ReleaseBlobRefThreadSafe(
109    scoped_refptr<base::TaskRunner> task_runner,
110    base::WeakPtr<IndexedDBActiveBlobRegistry> weak_ptr,
111    int64 database_id,
112    int64 blob_key,
113    const base::FilePath& unused) {
114  task_runner->PostTask(FROM_HERE,
115                        base::Bind(&IndexedDBActiveBlobRegistry::ReleaseBlobRef,
116                                   weak_ptr,
117                                   database_id,
118                                   blob_key));
119}
120
121storage::ShareableFileReference::FinalReleaseCallback
122IndexedDBActiveBlobRegistry::GetFinalReleaseCallback(int64 database_id,
123                                                     int64 blob_key) {
124  return base::Bind(
125      &IndexedDBActiveBlobRegistry::ReleaseBlobRefThreadSafe,
126      scoped_refptr<base::TaskRunner>(backing_store_->task_runner()),
127      weak_factory_.GetWeakPtr(),
128      database_id,
129      blob_key);
130}
131
132base::Closure IndexedDBActiveBlobRegistry::GetAddBlobRefCallback(
133    int64 database_id,
134    int64 blob_key) {
135  return base::Bind(&IndexedDBActiveBlobRegistry::AddBlobRef,
136                    weak_factory_.GetWeakPtr(),
137                    database_id,
138                    blob_key);
139}
140
141void IndexedDBActiveBlobRegistry::ForceShutdown() {
142  weak_factory_.InvalidateWeakPtrs();
143  use_tracker_.clear();
144  backing_store_ = NULL;
145}
146
147}  // namespace content
148