- add sources.
[platform/framework/web/crosswalk.git] / src / content / browser / dom_storage / dom_storage_namespace.cc
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.
4
5 #include "content/browser/dom_storage/dom_storage_namespace.h"
6
7 #include <set>
8 #include <utility>
9
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"
20
21 namespace content {
22
23 namespace {
24
25 static const unsigned int kMaxTransactionLogEntries = 8 * 1024;
26
27 }  // namespace
28
29 DOMStorageNamespace::DOMStorageNamespace(
30     const base::FilePath& directory,
31     DOMStorageTaskRunner* task_runner)
32     : namespace_id_(kLocalStorageNamespaceId),
33       directory_(directory),
34       task_runner_(task_runner) {
35 }
36
37 DOMStorageNamespace::DOMStorageNamespace(
38     int64 namespace_id,
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);
47 }
48
49 DOMStorageNamespace::~DOMStorageNamespace() {
50   STLDeleteValues(&transactions_);
51 }
52
53 DOMStorageArea* DOMStorageNamespace::OpenStorageArea(const GURL& origin) {
54   if (AreaHolder* holder = GetAreaHolder(origin)) {
55     ++(holder->open_count_);
56     return holder->area_.get();
57   }
58   DOMStorageArea* area;
59   if (namespace_id_ == kLocalStorageNamespaceId) {
60     area = new DOMStorageArea(origin, directory_, task_runner_.get());
61   } else {
62     area = new DOMStorageArea(
63         namespace_id_, persistent_namespace_id_, origin,
64         session_storage_database_.get(), task_runner_.get());
65   }
66   areas_[origin] = AreaHolder(area, 1);
67   return area;
68 }
69
70 void DOMStorageNamespace::CloseStorageArea(DOMStorageArea* area) {
71   AreaHolder* holder = GetAreaHolder(area->origin());
72   DCHECK(holder);
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.
77 }
78
79 DOMStorageArea* DOMStorageNamespace::GetOpenStorageArea(const GURL& origin) {
80   AreaHolder* holder = GetAreaHolder(origin);
81   if (holder && holder->open_count_)
82     return holder->area_.get();
83   return NULL;
84 }
85
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);
100   }
101   // And clone the on-disk structures, too.
102   if (session_storage_database_.get()) {
103     task_runner_->PostShutdownBlockingTask(
104         FROM_HERE,
105         DOMStorageTaskRunner::COMMIT_SEQUENCE,
106         base::Bind(base::IgnoreResult(&SessionStorageDatabase::CloneNamespace),
107                    session_storage_database_.get(), persistent_namespace_id_,
108                    clone_persistent_namespace_id));
109   }
110   return clone;
111 }
112
113 void DOMStorageNamespace::DeleteLocalStorageOrigin(const GURL& origin) {
114   DCHECK(!session_storage_database_.get());
115   AreaHolder* holder = GetAreaHolder(origin);
116   if (holder) {
117     holder->area_->DeleteOrigin();
118     return;
119   }
120   if (!directory_.empty()) {
121     scoped_refptr<DOMStorageArea> area =
122         new DOMStorageArea(origin, directory_, task_runner_.get());
123     area->DeleteOrigin();
124   }
125 }
126
127 void DOMStorageNamespace::DeleteSessionStorageOrigin(const GURL& origin) {
128   DOMStorageArea* area = OpenStorageArea(origin);
129   area->FastClear();
130   CloseStorageArea(area);
131 }
132
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()) {
140       ++it;
141       continue;
142     }
143
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();
148       areas_.erase(it++);
149       continue;
150     }
151
152     if (option == PURGE_AGGRESSIVE) {
153       // If aggressive is true, we clear caches and such
154       // for opened areas.
155       it->second.area_->PurgeMemory();
156     }
157
158     ++it;
159   }
160 }
161
162 void DOMStorageNamespace::Shutdown() {
163   AreaMap::const_iterator it = areas_.begin();
164   for (; it != areas_.end(); ++it)
165     it->second.area_->Shutdown();
166 }
167
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())
172       ++area_count;
173   }
174   return area_count;
175 }
176
177 DOMStorageNamespace::AreaHolder*
178 DOMStorageNamespace::GetAreaHolder(const GURL& origin) {
179   AreaMap::iterator found = areas_.find(origin);
180   if (found == areas_.end())
181     return NULL;
182   return &(found->second);
183 }
184
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;
190 }
191
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);
197 }
198
199 SessionStorageNamespace::MergeResult DOMStorageNamespace::CanMerge(
200     int process_id,
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;
209
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);
222       continue;
223     }
224     if (skip_areas.find(transaction.origin) != skip_areas.end())
225       continue;
226     if (skip_keys.find(OriginKey(transaction.origin, transaction.key))
227         != skip_keys.end()) {
228       continue;
229     }
230     if (transaction.transaction_type == TRANSACTION_REMOVE ||
231         transaction.transaction_type == TRANSACTION_WRITE) {
232       skip_keys.insert(OriginKey(transaction.origin, transaction.key));
233       continue;
234     }
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;
241       continue;
242     }
243     NOTREACHED();
244   }
245   return SessionStorageNamespace::MERGE_RESULT_MERGEABLE;
246 }
247
248 bool DOMStorageNamespace::IsLoggingRenderer(int process_id) {
249   DCHECK(process_id != ChildProcessHostImpl::kInvalidChildProcessId);
250   if (transactions_.count(process_id) < 1)
251     return false;
252   return !transactions_[process_id]->max_log_size_exceeded;
253 }
254
255 void DOMStorageNamespace::AddTransaction(
256     int process_id, const TransactionRecord& transaction) {
257   if (!IsLoggingRenderer(process_id))
258     return;
259   TransactionData* transaction_data = transactions_[process_id];
260   DCHECK(transaction_data);
261   if (transaction_data->max_log_size_exceeded)
262     return;
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();
267   }
268 }
269
270 DOMStorageNamespace::TransactionData::TransactionData()
271     : max_log_size_exceeded(false) {
272 }
273
274 DOMStorageNamespace::TransactionData::~TransactionData() {
275 }
276
277 DOMStorageNamespace::TransactionRecord::TransactionRecord() {
278 }
279
280 DOMStorageNamespace::TransactionRecord::~TransactionRecord() {
281 }
282
283 // AreaHolder
284
285 DOMStorageNamespace::AreaHolder::AreaHolder()
286     : open_count_(0) {
287 }
288
289 DOMStorageNamespace::AreaHolder::AreaHolder(
290     DOMStorageArea* area, int count)
291     : area_(area), open_count_(count) {
292 }
293
294 DOMStorageNamespace::AreaHolder::~AreaHolder() {
295 }
296
297 }  // namespace content