1 // Copyright 2013 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.
5 #include "content/browser/dom_storage/dom_storage_namespace.h"
10 #include "base/basictypes.h"
11 #include "base/bind.h"
12 #include "base/location.h"
13 #include "base/logging.h"
14 #include "base/stl_util.h"
15 #include "content/browser/dom_storage/dom_storage_area.h"
16 #include "content/browser/dom_storage/dom_storage_task_runner.h"
17 #include "content/browser/dom_storage/session_storage_database.h"
18 #include "content/common/child_process_host_impl.h"
19 #include "content/common/dom_storage/dom_storage_types.h"
25 static const unsigned int kMaxTransactionLogEntries = 8 * 1024;
29 DOMStorageNamespace::DOMStorageNamespace(
30 const base::FilePath& directory,
31 DOMStorageTaskRunner* task_runner)
32 : namespace_id_(kLocalStorageNamespaceId),
33 directory_(directory),
34 task_runner_(task_runner) {
37 DOMStorageNamespace::DOMStorageNamespace(
39 const std::string& persistent_namespace_id,
40 SessionStorageDatabase* session_storage_database,
41 DOMStorageTaskRunner* task_runner)
42 : namespace_id_(namespace_id),
43 persistent_namespace_id_(persistent_namespace_id),
44 task_runner_(task_runner),
45 session_storage_database_(session_storage_database) {
46 DCHECK_NE(kLocalStorageNamespaceId, namespace_id);
49 DOMStorageNamespace::~DOMStorageNamespace() {
50 STLDeleteValues(&transactions_);
53 DOMStorageArea* DOMStorageNamespace::OpenStorageArea(const GURL& origin) {
54 if (AreaHolder* holder = GetAreaHolder(origin)) {
55 ++(holder->open_count_);
56 return holder->area_.get();
59 if (namespace_id_ == kLocalStorageNamespaceId) {
60 area = new DOMStorageArea(origin, directory_, task_runner_.get());
62 area = new DOMStorageArea(
63 namespace_id_, persistent_namespace_id_, origin,
64 session_storage_database_.get(), task_runner_.get());
66 areas_[origin] = AreaHolder(area, 1);
70 void DOMStorageNamespace::CloseStorageArea(DOMStorageArea* area) {
71 AreaHolder* holder = GetAreaHolder(area->origin());
73 DCHECK_EQ(holder->area_.get(), area);
74 --(holder->open_count_);
75 // TODO(michaeln): Clean up areas that aren't needed in memory anymore.
76 // The in-process-webkit based impl didn't do this either, but would be nice.
79 DOMStorageArea* DOMStorageNamespace::GetOpenStorageArea(const GURL& origin) {
80 AreaHolder* holder = GetAreaHolder(origin);
81 if (holder && holder->open_count_)
82 return holder->area_.get();
86 DOMStorageNamespace* DOMStorageNamespace::Clone(
87 int64 clone_namespace_id,
88 const std::string& clone_persistent_namespace_id) {
89 DCHECK_NE(kLocalStorageNamespaceId, namespace_id_);
90 DCHECK_NE(kLocalStorageNamespaceId, clone_namespace_id);
91 DOMStorageNamespace* clone = new DOMStorageNamespace(
92 clone_namespace_id, clone_persistent_namespace_id,
93 session_storage_database_.get(), task_runner_.get());
94 AreaMap::const_iterator it = areas_.begin();
95 // Clone the in-memory structures.
96 for (; it != areas_.end(); ++it) {
97 DOMStorageArea* area = it->second.area_->ShallowCopy(
98 clone_namespace_id, clone_persistent_namespace_id);
99 clone->areas_[it->first] = AreaHolder(area, 0);
101 // And clone the on-disk structures, too.
102 if (session_storage_database_.get()) {
103 task_runner_->PostShutdownBlockingTask(
105 DOMStorageTaskRunner::COMMIT_SEQUENCE,
106 base::Bind(base::IgnoreResult(&SessionStorageDatabase::CloneNamespace),
107 session_storage_database_.get(), persistent_namespace_id_,
108 clone_persistent_namespace_id));
113 void DOMStorageNamespace::DeleteLocalStorageOrigin(const GURL& origin) {
114 DCHECK(!session_storage_database_.get());
115 AreaHolder* holder = GetAreaHolder(origin);
117 holder->area_->DeleteOrigin();
120 if (!directory_.empty()) {
121 scoped_refptr<DOMStorageArea> area =
122 new DOMStorageArea(origin, directory_, task_runner_.get());
123 area->DeleteOrigin();
127 void DOMStorageNamespace::DeleteSessionStorageOrigin(const GURL& origin) {
128 DOMStorageArea* area = OpenStorageArea(origin);
130 CloseStorageArea(area);
133 void DOMStorageNamespace::PurgeMemory(PurgeOption option) {
134 if (directory_.empty())
135 return; // We can't purge w/o backing on disk.
136 AreaMap::iterator it = areas_.begin();
137 while (it != areas_.end()) {
138 // Leave it alone if changes are pending
139 if (it->second.area_->HasUncommittedChanges()) {
144 // If not in use, we can shut it down and remove
145 // it from our collection entirely.
146 if (it->second.open_count_ == 0) {
147 it->second.area_->Shutdown();
152 if (option == PURGE_AGGRESSIVE) {
153 // If aggressive is true, we clear caches and such
155 it->second.area_->PurgeMemory();
162 void DOMStorageNamespace::Shutdown() {
163 AreaMap::const_iterator it = areas_.begin();
164 for (; it != areas_.end(); ++it)
165 it->second.area_->Shutdown();
168 unsigned int DOMStorageNamespace::CountInMemoryAreas() const {
169 unsigned int area_count = 0;
170 for (AreaMap::const_iterator it = areas_.begin(); it != areas_.end(); ++it) {
171 if (it->second.area_->IsLoadedInMemory())
177 DOMStorageNamespace::AreaHolder*
178 DOMStorageNamespace::GetAreaHolder(const GURL& origin) {
179 AreaMap::iterator found = areas_.find(origin);
180 if (found == areas_.end())
182 return &(found->second);
185 void DOMStorageNamespace::AddTransactionLogProcessId(int process_id) {
186 DCHECK(process_id != ChildProcessHostImpl::kInvalidChildProcessId);
187 DCHECK(transactions_.count(process_id) == 0);
188 TransactionData* transaction_data = new TransactionData;
189 transactions_[process_id] = transaction_data;
192 void DOMStorageNamespace::RemoveTransactionLogProcessId(int process_id) {
193 DCHECK(process_id != ChildProcessHostImpl::kInvalidChildProcessId);
194 DCHECK(transactions_.count(process_id) == 1);
195 delete transactions_[process_id];
196 transactions_.erase(process_id);
199 SessionStorageNamespace::MergeResult DOMStorageNamespace::CanMerge(
201 DOMStorageNamespace* other) {
202 if (transactions_.count(process_id) < 1)
203 return SessionStorageNamespace::MERGE_RESULT_NOT_LOGGING;
204 TransactionData* data = transactions_[process_id];
205 if (data->max_log_size_exceeded)
206 return SessionStorageNamespace::MERGE_RESULT_TOO_MANY_TRANSACTIONS;
207 if (data->log.size() < 1)
208 return SessionStorageNamespace::MERGE_RESULT_NO_TRANSACTIONS;
210 // skip_areas and skip_keys store areas and (area, key) pairs, respectively,
211 // that have already been handled previously. Any further modifications to
212 // them will not change the result of the hypothetical merge.
213 std::set<GURL> skip_areas;
214 typedef std::pair<GURL, base::string16> OriginKey;
215 std::set<OriginKey> skip_keys;
216 // Indicates whether we could still merge the namespaces preserving all
217 // individual transactions.
218 for (unsigned int i = 0; i < data->log.size(); i++) {
219 TransactionRecord& transaction = data->log[i];
220 if (transaction.transaction_type == TRANSACTION_CLEAR) {
221 skip_areas.insert(transaction.origin);
224 if (skip_areas.find(transaction.origin) != skip_areas.end())
226 if (skip_keys.find(OriginKey(transaction.origin, transaction.key))
227 != skip_keys.end()) {
230 if (transaction.transaction_type == TRANSACTION_REMOVE ||
231 transaction.transaction_type == TRANSACTION_WRITE) {
232 skip_keys.insert(OriginKey(transaction.origin, transaction.key));
235 if (transaction.transaction_type == TRANSACTION_READ) {
236 DOMStorageArea* area = other->OpenStorageArea(transaction.origin);
237 base::NullableString16 other_value = area->GetItem(transaction.key);
238 other->CloseStorageArea(area);
239 if (transaction.value != other_value)
240 return SessionStorageNamespace::MERGE_RESULT_NOT_MERGEABLE;
245 return SessionStorageNamespace::MERGE_RESULT_MERGEABLE;
248 bool DOMStorageNamespace::IsLoggingRenderer(int process_id) {
249 DCHECK(process_id != ChildProcessHostImpl::kInvalidChildProcessId);
250 if (transactions_.count(process_id) < 1)
252 return !transactions_[process_id]->max_log_size_exceeded;
255 void DOMStorageNamespace::AddTransaction(
256 int process_id, const TransactionRecord& transaction) {
257 if (!IsLoggingRenderer(process_id))
259 TransactionData* transaction_data = transactions_[process_id];
260 DCHECK(transaction_data);
261 if (transaction_data->max_log_size_exceeded)
263 transaction_data->log.push_back(transaction);
264 if (transaction_data->log.size() > kMaxTransactionLogEntries) {
265 transaction_data->max_log_size_exceeded = true;
266 transaction_data->log.clear();
270 DOMStorageNamespace::TransactionData::TransactionData()
271 : max_log_size_exceeded(false) {
274 DOMStorageNamespace::TransactionData::~TransactionData() {
277 DOMStorageNamespace::TransactionRecord::TransactionRecord() {
280 DOMStorageNamespace::TransactionRecord::~TransactionRecord() {
285 DOMStorageNamespace::AreaHolder::AreaHolder()
289 DOMStorageNamespace::AreaHolder::AreaHolder(
290 DOMStorageArea* area, int count)
291 : area_(area), open_count_(count) {
294 DOMStorageNamespace::AreaHolder::~AreaHolder() {
297 } // namespace content