Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(486)

Side by Side Diff: webkit/dom_storage/dom_storage_area.cc

Issue 9963107: Persist sessionStorage on disk. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: . Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "webkit/dom_storage/dom_storage_area.h" 5 #include "webkit/dom_storage/dom_storage_area.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/file_util.h" 8 #include "base/file_util.h"
9 #include "base/location.h" 9 #include "base/location.h"
10 #include "base/logging.h" 10 #include "base/logging.h"
11 #include "base/time.h" 11 #include "base/time.h"
12 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h" 12 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h"
13 #include "webkit/database/database_util.h" 13 #include "webkit/database/database_util.h"
14 #include "webkit/dom_storage/dom_storage_map.h" 14 #include "webkit/dom_storage/dom_storage_map.h"
15 #include "webkit/dom_storage/dom_storage_namespace.h" 15 #include "webkit/dom_storage/dom_storage_namespace.h"
16 #include "webkit/dom_storage/dom_storage_task_runner.h" 16 #include "webkit/dom_storage/dom_storage_task_runner.h"
17 #include "webkit/dom_storage/dom_storage_types.h" 17 #include "webkit/dom_storage/dom_storage_types.h"
18 #include "webkit/dom_storage/session_storage_database.h"
18 #include "webkit/fileapi/file_system_util.h" 19 #include "webkit/fileapi/file_system_util.h"
19 #include "webkit/glue/webkit_glue.h" 20 #include "webkit/glue/webkit_glue.h"
20 21
21 using webkit_database::DatabaseUtil; 22 using webkit_database::DatabaseUtil;
22 23
23 namespace dom_storage { 24 namespace dom_storage {
24 25
25 static const int kCommitTimerSeconds = 1; 26 static const int kCommitTimerSeconds = 1;
26 27
27 DomStorageArea::CommitBatch::CommitBatch() 28 DomStorageArea::CommitBatch::CommitBatch()
(...skipping 24 matching lines...) Expand all
52 return DatabaseUtil::GetOriginFromIdentifier(origin_id); 53 return DatabaseUtil::GetOriginFromIdentifier(origin_id);
53 } 54 }
54 55
55 DomStorageArea::DomStorageArea( 56 DomStorageArea::DomStorageArea(
56 int64 namespace_id, const GURL& origin, 57 int64 namespace_id, const GURL& origin,
57 const FilePath& directory, DomStorageTaskRunner* task_runner) 58 const FilePath& directory, DomStorageTaskRunner* task_runner)
58 : namespace_id_(namespace_id), origin_(origin), 59 : namespace_id_(namespace_id), origin_(origin),
59 directory_(directory), 60 directory_(directory),
60 task_runner_(task_runner), 61 task_runner_(task_runner),
61 map_(new DomStorageMap(kPerAreaQuota)), 62 map_(new DomStorageMap(kPerAreaQuota)),
63 is_initial_import_done_(false),
64 is_shutdown_(false),
65 is_shallow_copy_(false) {
66 DCHECK(!directory.empty());
67 DCHECK_EQ(kLocalStorageNamespaceId, namespace_id);
68 FilePath path = directory.Append(DatabaseFileNameFromOrigin(origin_));
69 backing_.reset(new LocalStorageDatabase(path));
70 }
71
72 DomStorageArea::DomStorageArea(
73 int64 namespace_id, const GURL& origin,
74 SessionStorageDatabase* session_storage_backing,
75 DomStorageTaskRunner* task_runner)
76 : namespace_id_(namespace_id), origin_(origin),
77 task_runner_(task_runner),
78 map_(new DomStorageMap(kPerAreaQuota)),
79 session_storage_backing_(session_storage_backing),
80 is_initial_import_done_(false),
81 is_shutdown_(false),
82 is_shallow_copy_(false) {
83 DCHECK(namespace_id != kLocalStorageNamespaceId);
84 }
85
86 DomStorageArea::DomStorageArea(int64 namespace_id,
87 const GURL& origin,
88 DomStorageTaskRunner* task_runner)
89 : namespace_id_(namespace_id), origin_(origin),
90 task_runner_(task_runner),
91 map_(new DomStorageMap(kPerAreaQuota)),
62 is_initial_import_done_(true), 92 is_initial_import_done_(true),
63 is_shutdown_(false) { 93 is_shutdown_(false),
64 if (namespace_id == kLocalStorageNamespaceId && !directory.empty()) { 94 is_shallow_copy_(false) {
65 FilePath path = directory.Append(DatabaseFileNameFromOrigin(origin_));
66 backing_.reset(new DomStorageDatabase(path));
67 is_initial_import_done_ = false;
68 }
69 } 95 }
70 96
71 DomStorageArea::~DomStorageArea() { 97 DomStorageArea::~DomStorageArea() {
72 } 98 }
73 99
74 unsigned DomStorageArea::Length() { 100 unsigned DomStorageArea::Length() {
75 if (is_shutdown_) 101 if (is_shutdown_)
76 return 0; 102 return 0;
77 InitialImportIfNeeded(); 103 InitialImportIfNeeded();
78 return map_->Length(); 104 return map_->Length();
(...skipping 12 matching lines...) Expand all
91 InitialImportIfNeeded(); 117 InitialImportIfNeeded();
92 return map_->GetItem(key); 118 return map_->GetItem(key);
93 } 119 }
94 120
95 bool DomStorageArea::SetItem(const string16& key, 121 bool DomStorageArea::SetItem(const string16& key,
96 const string16& value, 122 const string16& value,
97 NullableString16* old_value) { 123 NullableString16* old_value) {
98 if (is_shutdown_) 124 if (is_shutdown_)
99 return false; 125 return false;
100 InitialImportIfNeeded(); 126 InitialImportIfNeeded();
101 if (!map_->HasOneRef()) 127 if (is_shallow_copy_) {
102 map_ = map_->DeepCopy(); 128 map_ = map_->DeepCopy();
129 if (session_storage_backing_.get()) {
130 session_storage_backing_->DeepCopy(namespace_id_, origin_);
michaeln 2012/04/12 01:48:46 see comments below about serializing deepcopy with
marja 2012/04/19 10:20:50 Done.
131 }
132 is_shallow_copy_ = false;
133 }
103 bool success = map_->SetItem(key, value, old_value); 134 bool success = map_->SetItem(key, value, old_value);
104 if (success && backing_.get()) { 135 if (success && GetBacking()) {
105 CommitBatch* commit_batch = CreateCommitBatchIfNeeded(); 136 CommitBatch* commit_batch = CreateCommitBatchIfNeeded();
106 commit_batch->changed_values[key] = NullableString16(value, false); 137 commit_batch->changed_values[key] = NullableString16(value, false);
107 } 138 }
108 return success; 139 return success;
109 } 140 }
110 141
111 bool DomStorageArea::RemoveItem(const string16& key, string16* old_value) { 142 bool DomStorageArea::RemoveItem(const string16& key, string16* old_value) {
112 if (is_shutdown_) 143 if (is_shutdown_)
113 return false; 144 return false;
114 InitialImportIfNeeded(); 145 InitialImportIfNeeded();
115 if (!map_->HasOneRef()) 146 if (is_shallow_copy_) {
116 map_ = map_->DeepCopy(); 147 map_ = map_->DeepCopy();
148 if (session_storage_backing_.get()) {
149 session_storage_backing_->DeepCopy(namespace_id_, origin_);
michaeln 2012/04/12 01:48:46 The changes to the backing store made by DeepCopy
marja 2012/04/19 10:20:50 Done. deep_copy_first doens't work, since the same
150 }
151 is_shallow_copy_ = false;
152 }
117 bool success = map_->RemoveItem(key, old_value); 153 bool success = map_->RemoveItem(key, old_value);
118 if (success && backing_.get()) { 154 if (success && GetBacking()) {
119 CommitBatch* commit_batch = CreateCommitBatchIfNeeded(); 155 CommitBatch* commit_batch = CreateCommitBatchIfNeeded();
120 commit_batch->changed_values[key] = NullableString16(true); 156 commit_batch->changed_values[key] = NullableString16(true);
121 } 157 }
122 return success; 158 return success;
123 } 159 }
124 160
125 bool DomStorageArea::Clear() { 161 bool DomStorageArea::Clear() {
126 if (is_shutdown_) 162 if (is_shutdown_)
127 return false; 163 return false;
128 InitialImportIfNeeded(); 164 InitialImportIfNeeded();
129 if (map_->Length() == 0) 165 if (map_->Length() == 0)
130 return false; 166 return false;
131 167
132 map_ = new DomStorageMap(kPerAreaQuota); 168 map_ = new DomStorageMap(kPerAreaQuota);
michaeln 2012/04/12 01:48:46 if this area is a 'shallow copy', we'll need to co
marja 2012/04/19 10:20:50 Done.
133 169
134 if (backing_.get()) { 170 if (GetBacking()) {
135 CommitBatch* commit_batch = CreateCommitBatchIfNeeded(); 171 CommitBatch* commit_batch = CreateCommitBatchIfNeeded();
136 commit_batch->clear_all_first = true; 172 commit_batch->clear_all_first = true;
137 commit_batch->changed_values.clear(); 173 commit_batch->changed_values.clear();
138 } 174 }
139 175
140 return true; 176 return true;
141 } 177 }
142 178
143 DomStorageArea* DomStorageArea::ShallowCopy(int64 destination_namespace_id) { 179 DomStorageArea* DomStorageArea::ShallowCopy(int64 destination_namespace_id) {
144 DCHECK_NE(kLocalStorageNamespaceId, namespace_id_); 180 DCHECK_NE(kLocalStorageNamespaceId, namespace_id_);
145 DCHECK_NE(kLocalStorageNamespaceId, destination_namespace_id); 181 DCHECK_NE(kLocalStorageNamespaceId, destination_namespace_id);
146 DCHECK(!backing_.get()); // SessionNamespaces aren't stored on disk. 182 DCHECK(session_storage_backing_.get());
147 183
148 DomStorageArea* copy = new DomStorageArea(destination_namespace_id, origin_, 184 session_storage_backing_->ShallowCopy(namespace_id_, origin_,
michaeln 2012/04/12 01:48:46 see earlier comments about serializing changes in
marja 2012/04/19 10:20:50 Done.
149 FilePath(), task_runner_); 185 destination_namespace_id);
186
187 DomStorageArea* copy = new DomStorageArea(
188 destination_namespace_id, origin_, session_storage_backing_,
189 task_runner_);
190 copy->is_shallow_copy_ = true;
150 copy->map_ = map_; 191 copy->map_ = map_;
151 copy->is_shutdown_ = is_shutdown_; 192 copy->is_shutdown_ = is_shutdown_;
152 return copy; 193 return copy;
153 } 194 }
154 195
155 bool DomStorageArea::HasUncommittedChanges() const { 196 bool DomStorageArea::HasUncommittedChanges() const {
156 DCHECK(!is_shutdown_); 197 DCHECK(!is_shutdown_);
157 return commit_batch_.get() || in_flight_commit_batch_.get(); 198 return commit_batch_.get() || in_flight_commit_batch_.get();
158 } 199 }
159 200
160 void DomStorageArea::DeleteOrigin() { 201 void DomStorageArea::DeleteOrigin() {
161 DCHECK(!is_shutdown_); 202 DCHECK(!is_shutdown_);
162 if (HasUncommittedChanges()) { 203 if (HasUncommittedChanges()) {
163 // TODO(michaeln): This logically deletes the data immediately, 204 // TODO(michaeln): This logically deletes the data immediately,
164 // and in a matter of a second, deletes the rows from the backing 205 // and in a matter of a second, deletes the rows from the backing
165 // database file, but the file itself will linger until shutdown 206 // database file, but the file itself will linger until shutdown
166 // or purge time. Ideally, this should delete the file more 207 // or purge time. Ideally, this should delete the file more
167 // quickly. 208 // quickly.
168 Clear(); 209 Clear();
169 return; 210 return;
170 } 211 }
171 map_ = new DomStorageMap(kPerAreaQuota); 212 map_ = new DomStorageMap(kPerAreaQuota);
172 if (backing_.get()) { 213 if (backing_.get()) {
173 is_initial_import_done_ = false; 214 is_initial_import_done_ = false;
174 backing_.reset(new DomStorageDatabase(backing_->file_path())); 215 backing_.reset(new LocalStorageDatabase(backing_->file_path()));
175 file_util::Delete(backing_->file_path(), false); 216 file_util::Delete(backing_->file_path(), false);
176 file_util::Delete( 217 file_util::Delete(
177 DomStorageDatabase::GetJournalFilePath(backing_->file_path()), false); 218 LocalStorageDatabase::GetJournalFilePath(backing_->file_path()), false);
219 } else if (session_storage_backing_.get()) {
220 session_storage_backing_->DeleteOrigin(namespace_id_, origin_);
michaeln 2012/04/12 01:48:46 probably needs to be serialized on the COMMIT_SEQU
marja 2012/04/19 10:20:50 Done.
178 } 221 }
179 } 222 }
180 223
181 void DomStorageArea::PurgeMemory() { 224 void DomStorageArea::PurgeMemory() {
182 DCHECK(!is_shutdown_); 225 DCHECK(!is_shutdown_);
183 if (!is_initial_import_done_ || // We're not using any memory. 226 if (!is_initial_import_done_ || // We're not using any memory.
184 !backing_.get() || // We can't purge anything. 227 !backing_.get() || // We can't purge anything.
185 HasUncommittedChanges()) // We leave things alone with changes pending. 228 HasUncommittedChanges()) // We leave things alone with changes pending.
186 return; 229 return;
187 230
188 // Drop the in memory cache, we'll reload when needed. 231 // Drop the in memory cache, we'll reload when needed.
189 is_initial_import_done_ = false; 232 is_initial_import_done_ = false;
190 map_ = new DomStorageMap(kPerAreaQuota); 233 map_ = new DomStorageMap(kPerAreaQuota);
191 234
192 // Recreate the database object, this frees up the open sqlite connection 235 // Recreate the database object, this frees up the open sqlite connection
193 // and its page cache. 236 // and its page cache.
194 backing_.reset(new DomStorageDatabase(backing_->file_path())); 237 backing_.reset(new LocalStorageDatabase(backing_->file_path()));
195 } 238 }
196 239
197 void DomStorageArea::Shutdown() { 240 void DomStorageArea::Shutdown() {
198 DCHECK(!is_shutdown_); 241 DCHECK(!is_shutdown_);
199 is_shutdown_ = true; 242 is_shutdown_ = true;
200 map_ = NULL; 243 map_ = NULL;
201 if (!backing_.get()) 244 if (!GetBacking())
202 return; 245 return;
203 246
204 bool success = task_runner_->PostShutdownBlockingTask( 247 bool success = task_runner_->PostShutdownBlockingTask(
205 FROM_HERE, 248 FROM_HERE,
206 DomStorageTaskRunner::COMMIT_SEQUENCE, 249 DomStorageTaskRunner::COMMIT_SEQUENCE,
207 base::Bind(&DomStorageArea::ShutdownInCommitSequence, this)); 250 base::Bind(&DomStorageArea::ShutdownInCommitSequence, this));
208 DCHECK(success); 251 DCHECK(success);
209 } 252 }
210 253
211 void DomStorageArea::InitialImportIfNeeded() { 254 void DomStorageArea::InitialImportIfNeeded() {
212 if (is_initial_import_done_) 255 if (is_initial_import_done_)
213 return; 256 return;
214 257
215 DCHECK_EQ(kLocalStorageNamespaceId, namespace_id_); 258 DCHECK(GetBacking());
216 DCHECK(backing_.get());
217 259
218 ValuesMap initial_values; 260 ValuesMap initial_values;
219 backing_->ReadAllValues(&initial_values); 261 GetBacking()->ReadAllValues(namespace_id_, origin_, &initial_values);
220 map_->SwapValues(&initial_values); 262 map_->SwapValues(&initial_values);
221 is_initial_import_done_ = true; 263 is_initial_import_done_ = true;
222 } 264 }
223 265
224 DomStorageArea::CommitBatch* DomStorageArea::CreateCommitBatchIfNeeded() { 266 DomStorageArea::CommitBatch* DomStorageArea::CreateCommitBatchIfNeeded() {
225 DCHECK(!is_shutdown_); 267 DCHECK(!is_shutdown_);
226 if (!commit_batch_.get()) { 268 if (!commit_batch_.get()) {
227 commit_batch_.reset(new CommitBatch()); 269 commit_batch_.reset(new CommitBatch());
228 270
229 // Start a timer to commit any changes that accrue in the batch, 271 // Start a timer to commit any changes that accrue in the batch,
230 // but only if a commit is not currently in flight. In that case 272 // but only if a commit is not currently in flight. In that case
231 // the timer will be started after the current commit has happened. 273 // the timer will be started after the current commit has happened.
232 if (!in_flight_commit_batch_.get()) { 274 if (!in_flight_commit_batch_.get()) {
233 task_runner_->PostDelayedTask( 275 task_runner_->PostDelayedTask(
234 FROM_HERE, 276 FROM_HERE,
235 base::Bind(&DomStorageArea::OnCommitTimer, this), 277 base::Bind(&DomStorageArea::OnCommitTimer, this),
236 base::TimeDelta::FromSeconds(kCommitTimerSeconds)); 278 base::TimeDelta::FromSeconds(kCommitTimerSeconds));
237 } 279 }
238 } 280 }
239 return commit_batch_.get(); 281 return commit_batch_.get();
240 } 282 }
241 283
242 void DomStorageArea::OnCommitTimer() { 284 void DomStorageArea::OnCommitTimer() {
243 DCHECK_EQ(kLocalStorageNamespaceId, namespace_id_);
244 if (is_shutdown_) 285 if (is_shutdown_)
245 return; 286 return;
246 287
247 DCHECK(backing_.get()); 288 DCHECK(GetBacking());
248 DCHECK(commit_batch_.get()); 289 DCHECK(commit_batch_.get());
249 DCHECK(!in_flight_commit_batch_.get()); 290 DCHECK(!in_flight_commit_batch_.get());
250 291
251 // This method executes on the primary sequence, we schedule 292 // This method executes on the primary sequence, we schedule
252 // a task for immediate execution on the commit sequence. 293 // a task for immediate execution on the commit sequence.
253 DCHECK(task_runner_->IsRunningOnPrimarySequence()); 294 DCHECK(task_runner_->IsRunningOnPrimarySequence());
254 in_flight_commit_batch_ = commit_batch_.Pass(); 295 in_flight_commit_batch_ = commit_batch_.Pass();
255 bool success = task_runner_->PostShutdownBlockingTask( 296 bool success = task_runner_->PostShutdownBlockingTask(
256 FROM_HERE, 297 FROM_HERE,
257 DomStorageTaskRunner::COMMIT_SEQUENCE, 298 DomStorageTaskRunner::COMMIT_SEQUENCE,
258 base::Bind(&DomStorageArea::CommitChanges, this)); 299 base::Bind(&DomStorageArea::CommitChanges, this));
259 DCHECK(success); 300 DCHECK(success);
260 } 301 }
261 302
262 void DomStorageArea::CommitChanges() { 303 void DomStorageArea::CommitChanges() {
263 // This method executes on the commit sequence. 304 // This method executes on the commit sequence.
264 DCHECK(task_runner_->IsRunningOnCommitSequence()); 305 DCHECK(task_runner_->IsRunningOnCommitSequence());
265 DCHECK(in_flight_commit_batch_.get()); 306 DCHECK(in_flight_commit_batch_.get());
266 bool success = backing_->CommitChanges( 307 bool success = GetBacking()->CommitChanges(
308 namespace_id_, origin_,
267 in_flight_commit_batch_->clear_all_first, 309 in_flight_commit_batch_->clear_all_first,
268 in_flight_commit_batch_->changed_values); 310 in_flight_commit_batch_->changed_values);
269 DCHECK(success); // TODO(michaeln): what if it fails? 311 DCHECK(success); // TODO(michaeln): what if it fails?
270 task_runner_->PostTask( 312 task_runner_->PostTask(
271 FROM_HERE, 313 FROM_HERE,
272 base::Bind(&DomStorageArea::OnCommitComplete, this)); 314 base::Bind(&DomStorageArea::OnCommitComplete, this));
273 } 315 }
274 316
275 void DomStorageArea::OnCommitComplete() { 317 void DomStorageArea::OnCommitComplete() {
276 // We're back on the primary sequence in this method. 318 // We're back on the primary sequence in this method.
277 DCHECK(task_runner_->IsRunningOnPrimarySequence()); 319 DCHECK(task_runner_->IsRunningOnPrimarySequence());
278 if (is_shutdown_) 320 if (is_shutdown_)
279 return; 321 return;
280 in_flight_commit_batch_.reset(); 322 in_flight_commit_batch_.reset();
281 if (commit_batch_.get()) { 323 if (commit_batch_.get()) {
282 // More changes have accrued, restart the timer. 324 // More changes have accrued, restart the timer.
283 task_runner_->PostDelayedTask( 325 task_runner_->PostDelayedTask(
284 FROM_HERE, 326 FROM_HERE,
285 base::Bind(&DomStorageArea::OnCommitTimer, this), 327 base::Bind(&DomStorageArea::OnCommitTimer, this),
286 base::TimeDelta::FromSeconds(kCommitTimerSeconds)); 328 base::TimeDelta::FromSeconds(kCommitTimerSeconds));
287 } 329 }
288 } 330 }
289 331
290 void DomStorageArea::ShutdownInCommitSequence() { 332 void DomStorageArea::ShutdownInCommitSequence() {
291 // This method executes on the commit sequence. 333 // This method executes on the commit sequence.
292 DCHECK(task_runner_->IsRunningOnCommitSequence()); 334 DCHECK(task_runner_->IsRunningOnCommitSequence());
293 DCHECK(backing_.get()); 335 DCHECK(GetBacking());
294 if (commit_batch_.get()) { 336 if (commit_batch_.get()) {
295 // Commit any changes that accrued prior to the timer firing. 337 // Commit any changes that accrued prior to the timer firing.
296 bool success = backing_->CommitChanges( 338 bool success = GetBacking()->CommitChanges(
339 namespace_id_, origin_,
297 commit_batch_->clear_all_first, 340 commit_batch_->clear_all_first,
298 commit_batch_->changed_values); 341 commit_batch_->changed_values);
299 DCHECK(success); 342 DCHECK(success);
300 } 343 }
301 commit_batch_.reset(); 344 commit_batch_.reset();
302 in_flight_commit_batch_.reset(); 345 in_flight_commit_batch_.reset();
303 backing_.reset(); 346 backing_.reset();
347 session_storage_backing_ = NULL;
348 }
349
350 DomStorageDatabase* DomStorageArea::GetBacking() const {
351 DCHECK(!backing_.get() || !session_storage_backing_.get());
352 if (backing_.get())
353 return backing_.get();
354 return session_storage_backing_.get();
304 } 355 }
305 356
306 } // namespace dom_storage 357 } // namespace dom_storage
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698