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

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: Test update. Created 8 years, 7 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"
9 #include "base/location.h" 8 #include "base/location.h"
10 #include "base/logging.h" 9 #include "base/logging.h"
11 #include "base/time.h" 10 #include "base/time.h"
12 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h" 11 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h"
13 #include "webkit/database/database_util.h" 12 #include "webkit/database/database_util.h"
14 #include "webkit/dom_storage/dom_storage_map.h" 13 #include "webkit/dom_storage/dom_storage_map.h"
15 #include "webkit/dom_storage/dom_storage_namespace.h" 14 #include "webkit/dom_storage/dom_storage_namespace.h"
16 #include "webkit/dom_storage/dom_storage_task_runner.h" 15 #include "webkit/dom_storage/dom_storage_task_runner.h"
17 #include "webkit/dom_storage/dom_storage_types.h" 16 #include "webkit/dom_storage/dom_storage_types.h"
17 #include "webkit/dom_storage/local_storage_database_adapter.h"
18 #include "webkit/dom_storage/session_storage_database.h"
19 #include "webkit/dom_storage/session_storage_database_adapter.h"
18 #include "webkit/fileapi/file_system_util.h" 20 #include "webkit/fileapi/file_system_util.h"
19 #include "webkit/glue/webkit_glue.h" 21 #include "webkit/glue/webkit_glue.h"
20 22
21 using webkit_database::DatabaseUtil; 23 using webkit_database::DatabaseUtil;
22 24
23 namespace dom_storage { 25 namespace dom_storage {
24 26
25 static const int kCommitTimerSeconds = 1; 27 static const int kCommitTimerSeconds = 1;
26 28
27 DomStorageArea::CommitBatch::CommitBatch() 29 DomStorageArea::CommitBatch::CommitBatch()
(...skipping 27 matching lines...) Expand all
55 DomStorageArea::DomStorageArea( 57 DomStorageArea::DomStorageArea(
56 int64 namespace_id, const GURL& origin, 58 int64 namespace_id, const GURL& origin,
57 const FilePath& directory, DomStorageTaskRunner* task_runner) 59 const FilePath& directory, DomStorageTaskRunner* task_runner)
58 : namespace_id_(namespace_id), origin_(origin), 60 : namespace_id_(namespace_id), origin_(origin),
59 directory_(directory), 61 directory_(directory),
60 task_runner_(task_runner), 62 task_runner_(task_runner),
61 map_(new DomStorageMap(kPerAreaQuota)), 63 map_(new DomStorageMap(kPerAreaQuota)),
62 is_initial_import_done_(true), 64 is_initial_import_done_(true),
63 is_shutdown_(false), 65 is_shutdown_(false),
64 commit_batches_in_flight_(0) { 66 commit_batches_in_flight_(0) {
65 if (namespace_id == kLocalStorageNamespaceId && !directory.empty()) { 67 DCHECK_EQ(kLocalStorageNamespaceId, namespace_id);
68 if (!directory.empty()) {
66 FilePath path = directory.Append(DatabaseFileNameFromOrigin(origin_)); 69 FilePath path = directory.Append(DatabaseFileNameFromOrigin(origin_));
67 backing_.reset(new DomStorageDatabase(path)); 70 backing_.reset(new LocalStorageDatabaseAdapter(path));
68 is_initial_import_done_ = false; 71 is_initial_import_done_ = false;
69 } 72 }
70 } 73 }
74
75 DomStorageArea::DomStorageArea(
76 int64 namespace_id, const GURL& origin,
77 SessionStorageDatabase* session_storage_backing,
78 DomStorageTaskRunner* task_runner)
79 : namespace_id_(namespace_id), origin_(origin),
80 task_runner_(task_runner),
81 map_(new DomStorageMap(kPerAreaQuota)),
82 session_storage_backing_(session_storage_backing),
83 is_initial_import_done_(true),
84 is_shutdown_(false),
85 commit_batches_in_flight_(0) {
86 DCHECK(namespace_id != kLocalStorageNamespaceId);
87 if (session_storage_backing) {
88 backing_.reset(new SessionStorageDatabaseAdapter(
89 session_storage_backing, namespace_id, origin));
90 is_initial_import_done_ = false;
91 }
92 }
71 93
72 DomStorageArea::~DomStorageArea() { 94 DomStorageArea::~DomStorageArea() {
73 } 95 }
74 96
75 void DomStorageArea::ExtractValues(ValuesMap* map) { 97 void DomStorageArea::ExtractValues(ValuesMap* map) {
76 if (is_shutdown_) 98 if (is_shutdown_)
77 return; 99 return;
78 InitialImportIfNeeded(); 100 InitialImportIfNeeded();
79 map_->ExtractValues(map); 101 map_->ExtractValues(map);
80 } 102 }
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
144 commit_batch->clear_all_first = true; 166 commit_batch->clear_all_first = true;
145 commit_batch->changed_values.clear(); 167 commit_batch->changed_values.clear();
146 } 168 }
147 169
148 return true; 170 return true;
149 } 171 }
150 172
151 DomStorageArea* DomStorageArea::ShallowCopy(int64 destination_namespace_id) { 173 DomStorageArea* DomStorageArea::ShallowCopy(int64 destination_namespace_id) {
152 DCHECK_NE(kLocalStorageNamespaceId, namespace_id_); 174 DCHECK_NE(kLocalStorageNamespaceId, namespace_id_);
153 DCHECK_NE(kLocalStorageNamespaceId, destination_namespace_id); 175 DCHECK_NE(kLocalStorageNamespaceId, destination_namespace_id);
154 DCHECK(!backing_.get()); // SessionNamespaces aren't stored on disk.
155 176
156 DomStorageArea* copy = new DomStorageArea(destination_namespace_id, origin_, 177 DomStorageArea* copy = new DomStorageArea(
157 FilePath(), task_runner_); 178 destination_namespace_id, origin_, session_storage_backing_,
179 task_runner_);
158 copy->map_ = map_; 180 copy->map_ = map_;
159 copy->is_shutdown_ = is_shutdown_; 181 copy->is_shutdown_ = is_shutdown_;
182
183 // All the uncommitted changes to this area need to happen before the actual
michaeln 2012/05/16 08:10:55 really nice simplifications to this file compared
184 // shallow copy is made (scheduled by the upper layer). Another OnCommitTimer
185 // call might be in the event queue at this point, but it's handled gracefully
186 // when it fires.
187 if (commit_batch_.get())
188 OnCommitTimer();
160 return copy; 189 return copy;
161 } 190 }
162 191
163 bool DomStorageArea::HasUncommittedChanges() const { 192 bool DomStorageArea::HasUncommittedChanges() const {
164 DCHECK(!is_shutdown_); 193 DCHECK(!is_shutdown_);
165 return commit_batch_.get() || commit_batches_in_flight_; 194 return commit_batch_.get() || commit_batches_in_flight_;
166 } 195 }
167 196
168 void DomStorageArea::DeleteOrigin() { 197 void DomStorageArea::DeleteOrigin() {
169 DCHECK(!is_shutdown_); 198 DCHECK(!is_shutdown_);
170 if (HasUncommittedChanges()) { 199 if (HasUncommittedChanges()) {
171 // TODO(michaeln): This logically deletes the data immediately, 200 // TODO(michaeln): This logically deletes the data immediately,
172 // and in a matter of a second, deletes the rows from the backing 201 // and in a matter of a second, deletes the rows from the backing
173 // database file, but the file itself will linger until shutdown 202 // database file, but the file itself will linger until shutdown
174 // or purge time. Ideally, this should delete the file more 203 // or purge time. Ideally, this should delete the file more
175 // quickly. 204 // quickly.
176 Clear(); 205 Clear();
177 return; 206 return;
178 } 207 }
179 map_ = new DomStorageMap(kPerAreaQuota); 208 map_ = new DomStorageMap(kPerAreaQuota);
180 if (backing_.get()) { 209 if (backing_.get() && !session_storage_backing_.get()) {
210 // This is localStorage.
181 is_initial_import_done_ = false; 211 is_initial_import_done_ = false;
182 backing_.reset(new DomStorageDatabase(backing_->file_path())); 212 backing_->Reset();
183 file_util::Delete(backing_->file_path(), false); 213 backing_->DeleteFiles();
184 file_util::Delete( 214 } else if (session_storage_backing_.get()) {
michaeln 2012/05/16 08:10:55 should is_initial_import_done_ = true be set here?
marja 2012/05/25 13:04:06 Do you mean is_initial_import_done_ = false, like
michaeln 2012/05/30 01:29:19 I actually meant 'true'. Suppose RemoveItem() is c
marja 2012/05/30 11:46:19 Done. I was confused, = true makes sense. But why
185 DomStorageDatabase::GetJournalFilePath(backing_->file_path()), false); 215 task_runner_->PostShutdownBlockingTask(
216 FROM_HERE,
217 DomStorageTaskRunner::COMMIT_SEQUENCE,
218 base::Bind(&SessionStorageDatabase::DeleteArea,
219 session_storage_backing_.get(),
220 namespace_id_, origin_));
186 } 221 }
187 } 222 }
188 223
189 void DomStorageArea::PurgeMemory() { 224 void DomStorageArea::PurgeMemory() {
190 DCHECK(!is_shutdown_); 225 DCHECK(!is_shutdown_);
191 if (!is_initial_import_done_ || // We're not using any memory. 226 if (!is_initial_import_done_ || // We're not using any memory.
192 !backing_.get() || // We can't purge anything. 227 !backing_.get() || // We can't purge anything.
193 HasUncommittedChanges()) // We leave things alone with changes pending. 228 HasUncommittedChanges()) // We leave things alone with changes pending.
194 return; 229 return;
195 230
196 // Drop the in memory cache, we'll reload when needed. 231 // Drop the in memory cache, we'll reload when needed.
197 is_initial_import_done_ = false; 232 is_initial_import_done_ = false;
198 map_ = new DomStorageMap(kPerAreaQuota); 233 map_ = new DomStorageMap(kPerAreaQuota);
199 234
200 // Recreate the database object, this frees up the open sqlite connection 235 // Recreate the database object, this frees up the open sqlite connection
201 // and its page cache. 236 // and its page cache.
202 backing_.reset(new DomStorageDatabase(backing_->file_path())); 237 backing_->Reset();
203 } 238 }
204 239
205 void DomStorageArea::Shutdown() { 240 void DomStorageArea::Shutdown() {
206 DCHECK(!is_shutdown_); 241 DCHECK(!is_shutdown_);
207 is_shutdown_ = true; 242 is_shutdown_ = true;
208 map_ = NULL; 243 map_ = NULL;
209 if (!backing_.get()) 244 if (!backing_.get())
210 return; 245 return;
211 246
212 bool success = task_runner_->PostShutdownBlockingTask( 247 bool success = task_runner_->PostShutdownBlockingTask(
213 FROM_HERE, 248 FROM_HERE,
214 DomStorageTaskRunner::COMMIT_SEQUENCE, 249 DomStorageTaskRunner::COMMIT_SEQUENCE,
215 base::Bind(&DomStorageArea::ShutdownInCommitSequence, this)); 250 base::Bind(&DomStorageArea::ShutdownInCommitSequence, this));
216 DCHECK(success); 251 DCHECK(success);
217 } 252 }
218 253
219 void DomStorageArea::InitialImportIfNeeded() { 254 void DomStorageArea::InitialImportIfNeeded() {
220 if (is_initial_import_done_) 255 if (is_initial_import_done_)
221 return; 256 return;
222 257
223 DCHECK_EQ(kLocalStorageNamespaceId, namespace_id_);
224 DCHECK(backing_.get()); 258 DCHECK(backing_.get());
225 259
226 ValuesMap initial_values; 260 ValuesMap initial_values;
227 backing_->ReadAllValues(&initial_values); 261 backing_->ReadAllValues(&initial_values);
228 map_->SwapValues(&initial_values); 262 map_->SwapValues(&initial_values);
229 is_initial_import_done_ = true; 263 is_initial_import_done_ = true;
230 } 264 }
231 265
232 DomStorageArea::CommitBatch* DomStorageArea::CreateCommitBatchIfNeeded() { 266 DomStorageArea::CommitBatch* DomStorageArea::CreateCommitBatchIfNeeded() {
233 DCHECK(!is_shutdown_); 267 DCHECK(!is_shutdown_);
234 if (!commit_batch_.get()) { 268 if (!commit_batch_.get()) {
235 commit_batch_.reset(new CommitBatch()); 269 commit_batch_.reset(new CommitBatch());
236 270
237 // Start a timer to commit any changes that accrue in the batch, but only if 271 // Start a timer to commit any changes that accrue in the batch, but only if
238 // no commits are currently in flight. In that case the timer will be 272 // no commits are currently in flight. In that case the timer will be
239 // started after the commits have happened. 273 // started after the commits have happened.
240 if (!commit_batches_in_flight_) { 274 if (!commit_batches_in_flight_) {
241 task_runner_->PostDelayedTask( 275 task_runner_->PostDelayedTask(
242 FROM_HERE, 276 FROM_HERE,
243 base::Bind(&DomStorageArea::OnCommitTimer, this), 277 base::Bind(&DomStorageArea::OnCommitTimer, this),
244 base::TimeDelta::FromSeconds(kCommitTimerSeconds)); 278 base::TimeDelta::FromSeconds(kCommitTimerSeconds));
245 } 279 }
246 } 280 }
247 return commit_batch_.get(); 281 return commit_batch_.get();
248 } 282 }
249 283
250 void DomStorageArea::OnCommitTimer() { 284 void DomStorageArea::OnCommitTimer() {
251 DCHECK_EQ(kLocalStorageNamespaceId, namespace_id_);
252 if (is_shutdown_) 285 if (is_shutdown_)
253 return; 286 return;
254 287
255 DCHECK(backing_.get()); 288 DCHECK(backing_.get());
256 DCHECK(commit_batch_.get()); 289
257 DCHECK(!commit_batches_in_flight_); 290 // It's possible that there is nothing to commit, since a shallow copy occured
291 // before the timer fired.
292 if (!commit_batch_.get())
293 return;
258 294
259 // This method executes on the primary sequence, we schedule 295 // This method executes on the primary sequence, we schedule
260 // a task for immediate execution on the commit sequence. 296 // a task for immediate execution on the commit sequence.
261 DCHECK(task_runner_->IsRunningOnPrimarySequence()); 297 DCHECK(task_runner_->IsRunningOnPrimarySequence());
262 bool success = task_runner_->PostShutdownBlockingTask( 298 bool success = task_runner_->PostShutdownBlockingTask(
263 FROM_HERE, 299 FROM_HERE,
264 DomStorageTaskRunner::COMMIT_SEQUENCE, 300 DomStorageTaskRunner::COMMIT_SEQUENCE,
265 base::Bind(&DomStorageArea::CommitChanges, this, 301 base::Bind(&DomStorageArea::CommitChanges, this,
266 base::Owned(commit_batch_.release()))); 302 base::Owned(commit_batch_.release())));
267 ++commit_batches_in_flight_; 303 ++commit_batches_in_flight_;
268 DCHECK(success); 304 DCHECK(success);
269 } 305 }
270 306
271 void DomStorageArea::CommitChanges(const CommitBatch* commit_batch) { 307 void DomStorageArea::CommitChanges(const CommitBatch* commit_batch) {
272 // This method executes on the commit sequence. 308 // This method executes on the commit sequence.
273 DCHECK(task_runner_->IsRunningOnCommitSequence()); 309 DCHECK(task_runner_->IsRunningOnCommitSequence());
274 bool success = backing_->CommitChanges(commit_batch->clear_all_first, 310 bool success = backing_->CommitChanges(commit_batch->clear_all_first,
275 commit_batch->changed_values); 311 commit_batch->changed_values);
276 DCHECK(success); // TODO(michaeln): what if it fails? 312 DCHECK(success); // TODO(michaeln): what if it fails?
277 task_runner_->PostTask( 313 task_runner_->PostTask(
278 FROM_HERE, 314 FROM_HERE,
279 base::Bind(&DomStorageArea::OnCommitComplete, this)); 315 base::Bind(&DomStorageArea::OnCommitComplete, this));
280 } 316 }
281 317
282 void DomStorageArea::OnCommitComplete() { 318 void DomStorageArea::OnCommitComplete() {
283 // We're back on the primary sequence in this method. 319 // We're back on the primary sequence in this method.
284 DCHECK(task_runner_->IsRunningOnPrimarySequence()); 320 DCHECK(task_runner_->IsRunningOnPrimarySequence());
321 --commit_batches_in_flight_;
285 if (is_shutdown_) 322 if (is_shutdown_)
286 return; 323 return;
287 --commit_batches_in_flight_;
288 if (commit_batch_.get() && !commit_batches_in_flight_) { 324 if (commit_batch_.get() && !commit_batches_in_flight_) {
289 // More changes have accrued, restart the timer. 325 // More changes have accrued, restart the timer.
290 task_runner_->PostDelayedTask( 326 task_runner_->PostDelayedTask(
291 FROM_HERE, 327 FROM_HERE,
292 base::Bind(&DomStorageArea::OnCommitTimer, this), 328 base::Bind(&DomStorageArea::OnCommitTimer, this),
293 base::TimeDelta::FromSeconds(kCommitTimerSeconds)); 329 base::TimeDelta::FromSeconds(kCommitTimerSeconds));
294 } 330 }
295 } 331 }
296 332
297 void DomStorageArea::ShutdownInCommitSequence() { 333 void DomStorageArea::ShutdownInCommitSequence() {
298 // This method executes on the commit sequence. 334 // This method executes on the commit sequence.
299 DCHECK(task_runner_->IsRunningOnCommitSequence()); 335 DCHECK(task_runner_->IsRunningOnCommitSequence());
300 DCHECK(backing_.get()); 336 DCHECK(backing_.get());
301 if (commit_batch_.get()) { 337 if (commit_batch_.get()) {
302 // Commit any changes that accrued prior to the timer firing. 338 // Commit any changes that accrued prior to the timer firing.
303 bool success = backing_->CommitChanges( 339 bool success = backing_->CommitChanges(
304 commit_batch_->clear_all_first, 340 commit_batch_->clear_all_first,
305 commit_batch_->changed_values); 341 commit_batch_->changed_values);
306 DCHECK(success); 342 DCHECK(success);
307 } 343 }
308 commit_batch_.reset(); 344 commit_batch_.reset();
309 backing_.reset(); 345 backing_.reset();
346 session_storage_backing_ = NULL;
310 } 347 }
311 348
312 } // namespace dom_storage 349 } // namespace dom_storage
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698