Delete chrome.mediaGalleriesPrivate because the functionality unique to it has since...
[chromium-blink-merge.git] / content / browser / dom_storage / dom_storage_context_impl.cc
blobdb9681cfe72208acbdc22d1d61366e12fbfaad82
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.
5 #include "content/browser/dom_storage/dom_storage_context_impl.h"
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/files/file_enumerator.h"
10 #include "base/files/file_util.h"
11 #include "base/guid.h"
12 #include "base/location.h"
13 #include "base/time/time.h"
14 #include "content/browser/dom_storage/dom_storage_area.h"
15 #include "content/browser/dom_storage/dom_storage_database.h"
16 #include "content/browser/dom_storage/dom_storage_namespace.h"
17 #include "content/browser/dom_storage/dom_storage_task_runner.h"
18 #include "content/browser/dom_storage/session_storage_database.h"
19 #include "content/common/dom_storage/dom_storage_types.h"
20 #include "content/public/browser/dom_storage_context.h"
21 #include "content/public/browser/local_storage_usage_info.h"
22 #include "content/public/browser/session_storage_usage_info.h"
23 #include "storage/browser/quota/special_storage_policy.h"
25 namespace content {
27 static const int kSessionStoraceScavengingSeconds = 60;
29 DOMStorageContextImpl::DOMStorageContextImpl(
30 const base::FilePath& localstorage_directory,
31 const base::FilePath& sessionstorage_directory,
32 storage::SpecialStoragePolicy* special_storage_policy,
33 DOMStorageTaskRunner* task_runner)
34 : localstorage_directory_(localstorage_directory),
35 sessionstorage_directory_(sessionstorage_directory),
36 task_runner_(task_runner),
37 is_shutdown_(false),
38 force_keep_session_state_(false),
39 special_storage_policy_(special_storage_policy),
40 scavenging_started_(false) {
41 // AtomicSequenceNum starts at 0 but we want to start session
42 // namespace ids at one since zero is reserved for the
43 // kLocalStorageNamespaceId.
44 session_id_sequence_.GetNext();
47 DOMStorageContextImpl::~DOMStorageContextImpl() {
48 if (session_storage_database_.get()) {
49 // SessionStorageDatabase shouldn't be deleted right away: deleting it will
50 // potentially involve waiting in leveldb::DBImpl::~DBImpl, and waiting
51 // shouldn't happen on this thread.
52 SessionStorageDatabase* to_release = session_storage_database_.get();
53 to_release->AddRef();
54 session_storage_database_ = NULL;
55 task_runner_->PostShutdownBlockingTask(
56 FROM_HERE,
57 DOMStorageTaskRunner::COMMIT_SEQUENCE,
58 base::Bind(&SessionStorageDatabase::Release,
59 base::Unretained(to_release)));
63 DOMStorageNamespace* DOMStorageContextImpl::GetStorageNamespace(
64 int64 namespace_id) {
65 if (is_shutdown_)
66 return NULL;
67 StorageNamespaceMap::iterator found = namespaces_.find(namespace_id);
68 if (found == namespaces_.end()) {
69 if (namespace_id == kLocalStorageNamespaceId) {
70 if (!localstorage_directory_.empty()) {
71 if (!base::CreateDirectory(localstorage_directory_)) {
72 LOG(ERROR) << "Failed to create 'Local Storage' directory,"
73 " falling back to in-memory only.";
74 localstorage_directory_ = base::FilePath();
77 DOMStorageNamespace* local =
78 new DOMStorageNamespace(localstorage_directory_, task_runner_.get());
79 namespaces_[kLocalStorageNamespaceId] = local;
80 return local;
82 return NULL;
84 return found->second.get();
87 void DOMStorageContextImpl::GetLocalStorageUsage(
88 std::vector<LocalStorageUsageInfo>* infos,
89 bool include_file_info) {
90 if (localstorage_directory_.empty())
91 return;
92 base::FileEnumerator enumerator(localstorage_directory_, false,
93 base::FileEnumerator::FILES);
94 for (base::FilePath path = enumerator.Next(); !path.empty();
95 path = enumerator.Next()) {
96 if (path.MatchesExtension(DOMStorageArea::kDatabaseFileExtension)) {
97 LocalStorageUsageInfo info;
98 info.origin = DOMStorageArea::OriginFromDatabaseFileName(path);
99 if (include_file_info) {
100 base::FileEnumerator::FileInfo find_info = enumerator.GetInfo();
101 info.data_size = find_info.GetSize();
102 info.last_modified = find_info.GetLastModifiedTime();
104 infos->push_back(info);
109 void DOMStorageContextImpl::GetSessionStorageUsage(
110 std::vector<SessionStorageUsageInfo>* infos) {
111 if (!session_storage_database_.get())
112 return;
113 std::map<std::string, std::vector<GURL> > namespaces_and_origins;
114 session_storage_database_->ReadNamespacesAndOrigins(
115 &namespaces_and_origins);
116 for (std::map<std::string, std::vector<GURL> >::const_iterator it =
117 namespaces_and_origins.begin();
118 it != namespaces_and_origins.end(); ++it) {
119 for (std::vector<GURL>::const_iterator origin_it = it->second.begin();
120 origin_it != it->second.end(); ++origin_it) {
121 SessionStorageUsageInfo info;
122 info.persistent_namespace_id = it->first;
123 info.origin = *origin_it;
124 infos->push_back(info);
129 void DOMStorageContextImpl::DeleteLocalStorage(const GURL& origin) {
130 DCHECK(!is_shutdown_);
131 DOMStorageNamespace* local = GetStorageNamespace(kLocalStorageNamespaceId);
132 local->DeleteLocalStorageOrigin(origin);
133 // Synthesize a 'cleared' event if the area is open so CachedAreas in
134 // renderers get emptied out too.
135 DOMStorageArea* area = local->GetOpenStorageArea(origin);
136 if (area)
137 NotifyAreaCleared(area, origin);
140 void DOMStorageContextImpl::DeleteSessionStorage(
141 const SessionStorageUsageInfo& usage_info) {
142 DCHECK(!is_shutdown_);
143 DOMStorageNamespace* dom_storage_namespace = NULL;
144 std::map<std::string, int64>::const_iterator it =
145 persistent_namespace_id_to_namespace_id_.find(
146 usage_info.persistent_namespace_id);
147 if (it != persistent_namespace_id_to_namespace_id_.end()) {
148 dom_storage_namespace = GetStorageNamespace(it->second);
149 } else {
150 int64 namespace_id = AllocateSessionId();
151 CreateSessionNamespace(namespace_id, usage_info.persistent_namespace_id);
152 dom_storage_namespace = GetStorageNamespace(namespace_id);
154 dom_storage_namespace->DeleteSessionStorageOrigin(usage_info.origin);
155 // Synthesize a 'cleared' event if the area is open so CachedAreas in
156 // renderers get emptied out too.
157 DOMStorageArea* area =
158 dom_storage_namespace->GetOpenStorageArea(usage_info.origin);
159 if (area)
160 NotifyAreaCleared(area, usage_info.origin);
163 void DOMStorageContextImpl::Shutdown() {
164 is_shutdown_ = true;
165 StorageNamespaceMap::const_iterator it = namespaces_.begin();
166 for (; it != namespaces_.end(); ++it)
167 it->second->Shutdown();
169 if (localstorage_directory_.empty() && !session_storage_database_.get())
170 return;
172 // Respect the content policy settings about what to
173 // keep and what to discard.
174 if (force_keep_session_state_)
175 return; // Keep everything.
177 bool has_session_only_origins =
178 special_storage_policy_.get() &&
179 special_storage_policy_->HasSessionOnlyOrigins();
181 if (has_session_only_origins) {
182 // We may have to delete something. We continue on the
183 // commit sequence after area shutdown tasks have cycled
184 // thru that sequence (and closed their database files).
185 bool success = task_runner_->PostShutdownBlockingTask(
186 FROM_HERE,
187 DOMStorageTaskRunner::COMMIT_SEQUENCE,
188 base::Bind(&DOMStorageContextImpl::ClearSessionOnlyOrigins, this));
189 DCHECK(success);
193 void DOMStorageContextImpl::AddEventObserver(EventObserver* observer) {
194 event_observers_.AddObserver(observer);
197 void DOMStorageContextImpl::RemoveEventObserver(EventObserver* observer) {
198 event_observers_.RemoveObserver(observer);
201 void DOMStorageContextImpl::NotifyItemSet(
202 const DOMStorageArea* area,
203 const base::string16& key,
204 const base::string16& new_value,
205 const base::NullableString16& old_value,
206 const GURL& page_url) {
207 FOR_EACH_OBSERVER(
208 EventObserver, event_observers_,
209 OnDOMStorageItemSet(area, key, new_value, old_value, page_url));
212 void DOMStorageContextImpl::NotifyItemRemoved(
213 const DOMStorageArea* area,
214 const base::string16& key,
215 const base::string16& old_value,
216 const GURL& page_url) {
217 FOR_EACH_OBSERVER(
218 EventObserver, event_observers_,
219 OnDOMStorageItemRemoved(area, key, old_value, page_url));
222 void DOMStorageContextImpl::NotifyAreaCleared(
223 const DOMStorageArea* area,
224 const GURL& page_url) {
225 FOR_EACH_OBSERVER(
226 EventObserver, event_observers_,
227 OnDOMStorageAreaCleared(area, page_url));
230 std::string DOMStorageContextImpl::AllocatePersistentSessionId() {
231 std::string guid = base::GenerateGUID();
232 std::replace(guid.begin(), guid.end(), '-', '_');
233 return guid;
236 void DOMStorageContextImpl::CreateSessionNamespace(
237 int64 namespace_id,
238 const std::string& persistent_namespace_id) {
239 if (is_shutdown_)
240 return;
241 DCHECK(namespace_id != kLocalStorageNamespaceId);
242 DCHECK(namespaces_.find(namespace_id) == namespaces_.end());
243 namespaces_[namespace_id] = new DOMStorageNamespace(
244 namespace_id, persistent_namespace_id, session_storage_database_.get(),
245 task_runner_.get());
246 persistent_namespace_id_to_namespace_id_[persistent_namespace_id] =
247 namespace_id;
250 void DOMStorageContextImpl::DeleteSessionNamespace(
251 int64 namespace_id, bool should_persist_data) {
252 DCHECK_NE(kLocalStorageNamespaceId, namespace_id);
253 StorageNamespaceMap::const_iterator it = namespaces_.find(namespace_id);
254 if (it == namespaces_.end())
255 return;
256 std::string persistent_namespace_id = it->second->persistent_namespace_id();
257 if (session_storage_database_.get()) {
258 if (!should_persist_data) {
259 task_runner_->PostShutdownBlockingTask(
260 FROM_HERE,
261 DOMStorageTaskRunner::COMMIT_SEQUENCE,
262 base::Bind(
263 base::IgnoreResult(&SessionStorageDatabase::DeleteNamespace),
264 session_storage_database_,
265 persistent_namespace_id));
266 } else {
267 // Ensure that the data gets committed before we shut down.
268 it->second->Shutdown();
269 if (!scavenging_started_) {
270 // Protect the persistent namespace ID from scavenging.
271 protected_persistent_session_ids_.insert(persistent_namespace_id);
275 persistent_namespace_id_to_namespace_id_.erase(persistent_namespace_id);
276 namespaces_.erase(namespace_id);
279 void DOMStorageContextImpl::CloneSessionNamespace(
280 int64 existing_id, int64 new_id,
281 const std::string& new_persistent_id) {
282 if (is_shutdown_)
283 return;
284 DCHECK_NE(kLocalStorageNamespaceId, existing_id);
285 DCHECK_NE(kLocalStorageNamespaceId, new_id);
286 StorageNamespaceMap::iterator found = namespaces_.find(existing_id);
287 if (found != namespaces_.end())
288 namespaces_[new_id] = found->second->Clone(new_id, new_persistent_id);
289 else
290 CreateSessionNamespace(new_id, new_persistent_id);
293 void DOMStorageContextImpl::ClearSessionOnlyOrigins() {
294 if (!localstorage_directory_.empty()) {
295 std::vector<LocalStorageUsageInfo> infos;
296 const bool kDontIncludeFileInfo = false;
297 GetLocalStorageUsage(&infos, kDontIncludeFileInfo);
298 for (size_t i = 0; i < infos.size(); ++i) {
299 const GURL& origin = infos[i].origin;
300 if (special_storage_policy_->IsStorageProtected(origin))
301 continue;
302 if (!special_storage_policy_->IsStorageSessionOnly(origin))
303 continue;
305 base::FilePath database_file_path = localstorage_directory_.Append(
306 DOMStorageArea::DatabaseFileNameFromOrigin(origin));
307 sql::Connection::Delete(database_file_path);
310 if (session_storage_database_.get()) {
311 std::vector<SessionStorageUsageInfo> infos;
312 GetSessionStorageUsage(&infos);
313 for (size_t i = 0; i < infos.size(); ++i) {
314 const GURL& origin = infos[i].origin;
315 if (special_storage_policy_->IsStorageProtected(origin))
316 continue;
317 if (!special_storage_policy_->IsStorageSessionOnly(origin))
318 continue;
319 session_storage_database_->DeleteArea(infos[i].persistent_namespace_id,
320 origin);
325 void DOMStorageContextImpl::SetSaveSessionStorageOnDisk() {
326 DCHECK(namespaces_.empty());
327 if (!sessionstorage_directory_.empty()) {
328 session_storage_database_ = new SessionStorageDatabase(
329 sessionstorage_directory_);
333 void DOMStorageContextImpl::StartScavengingUnusedSessionStorage() {
334 if (session_storage_database_.get()) {
335 task_runner_->PostDelayedTask(
336 FROM_HERE, base::Bind(&DOMStorageContextImpl::FindUnusedNamespaces,
337 this),
338 base::TimeDelta::FromSeconds(kSessionStoraceScavengingSeconds));
342 void DOMStorageContextImpl::FindUnusedNamespaces() {
343 DCHECK(session_storage_database_.get());
344 if (scavenging_started_)
345 return;
346 scavenging_started_ = true;
347 std::set<std::string> namespace_ids_in_use;
348 for (StorageNamespaceMap::const_iterator it = namespaces_.begin();
349 it != namespaces_.end(); ++it)
350 namespace_ids_in_use.insert(it->second->persistent_namespace_id());
351 std::set<std::string> protected_persistent_session_ids;
352 protected_persistent_session_ids.swap(protected_persistent_session_ids_);
353 task_runner_->PostShutdownBlockingTask(
354 FROM_HERE, DOMStorageTaskRunner::COMMIT_SEQUENCE,
355 base::Bind(
356 &DOMStorageContextImpl::FindUnusedNamespacesInCommitSequence,
357 this, namespace_ids_in_use, protected_persistent_session_ids));
360 void DOMStorageContextImpl::FindUnusedNamespacesInCommitSequence(
361 const std::set<std::string>& namespace_ids_in_use,
362 const std::set<std::string>& protected_persistent_session_ids) {
363 DCHECK(session_storage_database_.get());
364 // Delete all namespaces which don't have an associated DOMStorageNamespace
365 // alive.
366 std::map<std::string, std::vector<GURL> > namespaces_and_origins;
367 session_storage_database_->ReadNamespacesAndOrigins(&namespaces_and_origins);
368 for (std::map<std::string, std::vector<GURL> >::const_iterator it =
369 namespaces_and_origins.begin();
370 it != namespaces_and_origins.end(); ++it) {
371 if (namespace_ids_in_use.find(it->first) == namespace_ids_in_use.end() &&
372 protected_persistent_session_ids.find(it->first) ==
373 protected_persistent_session_ids.end()) {
374 deletable_persistent_namespace_ids_.push_back(it->first);
377 if (!deletable_persistent_namespace_ids_.empty()) {
378 task_runner_->PostDelayedTask(
379 FROM_HERE, base::Bind(
380 &DOMStorageContextImpl::DeleteNextUnusedNamespace,
381 this),
382 base::TimeDelta::FromSeconds(kSessionStoraceScavengingSeconds));
386 void DOMStorageContextImpl::DeleteNextUnusedNamespace() {
387 if (is_shutdown_)
388 return;
389 task_runner_->PostShutdownBlockingTask(
390 FROM_HERE, DOMStorageTaskRunner::COMMIT_SEQUENCE,
391 base::Bind(
392 &DOMStorageContextImpl::DeleteNextUnusedNamespaceInCommitSequence,
393 this));
396 void DOMStorageContextImpl::DeleteNextUnusedNamespaceInCommitSequence() {
397 if (deletable_persistent_namespace_ids_.empty())
398 return;
399 const std::string& persistent_id = deletable_persistent_namespace_ids_.back();
400 session_storage_database_->DeleteNamespace(persistent_id);
401 deletable_persistent_namespace_ids_.pop_back();
402 if (!deletable_persistent_namespace_ids_.empty()) {
403 task_runner_->PostDelayedTask(
404 FROM_HERE, base::Bind(
405 &DOMStorageContextImpl::DeleteNextUnusedNamespace,
406 this),
407 base::TimeDelta::FromSeconds(kSessionStoraceScavengingSeconds));
411 } // namespace content