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

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: Code review. Created 8 years, 6 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 + kPerAreaOverQuotaAllowance)), 63 map_(new DomStorageMap(kPerAreaQuota + kPerAreaOverQuotaAllowance)),
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,
77 const std::string& persistent_namespace_id,
78 const GURL& origin,
79 SessionStorageDatabase* session_storage_backing,
80 DomStorageTaskRunner* task_runner)
81 : namespace_id_(namespace_id),
82 persistent_namespace_id_(persistent_namespace_id),
83 origin_(origin),
84 task_runner_(task_runner),
85 map_(new DomStorageMap(kPerAreaQuota)),
86 session_storage_backing_(session_storage_backing),
87 is_initial_import_done_(true),
88 is_shutdown_(false),
89 commit_batches_in_flight_(0) {
90 DCHECK(namespace_id != kLocalStorageNamespaceId);
91 if (session_storage_backing) {
92 backing_.reset(new SessionStorageDatabaseAdapter(
93 session_storage_backing, persistent_namespace_id, origin));
94 is_initial_import_done_ = false;
95 }
96 }
71 97
72 DomStorageArea::~DomStorageArea() { 98 DomStorageArea::~DomStorageArea() {
73 } 99 }
74 100
75 void DomStorageArea::ExtractValues(ValuesMap* map) { 101 void DomStorageArea::ExtractValues(ValuesMap* map) {
76 if (is_shutdown_) 102 if (is_shutdown_)
77 return; 103 return;
78 InitialImportIfNeeded(); 104 InitialImportIfNeeded();
79 map_->ExtractValues(map); 105 map_->ExtractValues(map);
80 } 106 }
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
141 167
142 if (backing_.get()) { 168 if (backing_.get()) {
143 CommitBatch* commit_batch = CreateCommitBatchIfNeeded(); 169 CommitBatch* commit_batch = CreateCommitBatchIfNeeded();
144 commit_batch->clear_all_first = true; 170 commit_batch->clear_all_first = true;
145 commit_batch->changed_values.clear(); 171 commit_batch->changed_values.clear();
146 } 172 }
147 173
148 return true; 174 return true;
149 } 175 }
150 176
151 DomStorageArea* DomStorageArea::ShallowCopy(int64 destination_namespace_id) { 177 DomStorageArea* DomStorageArea::ShallowCopy(
178 int64 destination_namespace_id,
179 const std::string& destination_persistent_namespace_id) {
152 DCHECK_NE(kLocalStorageNamespaceId, namespace_id_); 180 DCHECK_NE(kLocalStorageNamespaceId, namespace_id_);
153 DCHECK_NE(kLocalStorageNamespaceId, destination_namespace_id); 181 DCHECK_NE(kLocalStorageNamespaceId, destination_namespace_id);
154 DCHECK(!backing_.get()); // SessionNamespaces aren't stored on disk.
155 182
156 DomStorageArea* copy = new DomStorageArea(destination_namespace_id, origin_, 183 DomStorageArea* copy = new DomStorageArea(
157 FilePath(), task_runner_); 184 destination_namespace_id, persistent_namespace_id_, origin_,
185 session_storage_backing_, task_runner_);
158 copy->map_ = map_; 186 copy->map_ = map_;
159 copy->is_shutdown_ = is_shutdown_; 187 copy->is_shutdown_ = is_shutdown_;
188
189 // All the uncommitted changes to this area need to happen before the actual
190 // shallow copy is made (scheduled by the upper layer). Another OnCommitTimer
191 // call might be in the event queue at this point, but it's handled gracefully
192 // when it fires.
193 if (commit_batch_.get())
194 OnCommitTimer();
160 return copy; 195 return copy;
161 } 196 }
162 197
163 bool DomStorageArea::HasUncommittedChanges() const { 198 bool DomStorageArea::HasUncommittedChanges() const {
164 DCHECK(!is_shutdown_); 199 DCHECK(!is_shutdown_);
165 return commit_batch_.get() || commit_batches_in_flight_; 200 return commit_batch_.get() || commit_batches_in_flight_;
166 } 201 }
167 202
168 void DomStorageArea::DeleteOrigin() { 203 void DomStorageArea::DeleteOrigin() {
169 DCHECK(!is_shutdown_); 204 DCHECK(!is_shutdown_);
170 if (HasUncommittedChanges()) { 205 if (HasUncommittedChanges()) {
171 // TODO(michaeln): This logically deletes the data immediately, 206 // TODO(michaeln): This logically deletes the data immediately,
172 // and in a matter of a second, deletes the rows from the backing 207 // and in a matter of a second, deletes the rows from the backing
173 // database file, but the file itself will linger until shutdown 208 // database file, but the file itself will linger until shutdown
174 // or purge time. Ideally, this should delete the file more 209 // or purge time. Ideally, this should delete the file more
175 // quickly. 210 // quickly.
176 Clear(); 211 Clear();
177 return; 212 return;
178 } 213 }
179 map_ = new DomStorageMap(kPerAreaQuota + kPerAreaOverQuotaAllowance); 214 map_ = new DomStorageMap(kPerAreaQuota + kPerAreaOverQuotaAllowance);
180 if (backing_.get()) { 215 if (backing_.get() && !session_storage_backing_.get()) {
216 // This is localStorage.
181 is_initial_import_done_ = false; 217 is_initial_import_done_ = false;
182 backing_.reset(new DomStorageDatabase(backing_->file_path())); 218 backing_->Reset();
183 file_util::Delete(backing_->file_path(), false); 219 backing_->DeleteFiles();
184 file_util::Delete( 220 } else if (session_storage_backing_.get()) {
185 DomStorageDatabase::GetJournalFilePath(backing_->file_path()), false); 221 // No need to read the data (there will be no data), also, the
222 // PRIMARY_SEQUENCE thread shouldn't try to read the data while
223 // SessionStorageDatabase::DeleteArea is in progress.
224 is_initial_import_done_ = true;
225 task_runner_->PostShutdownBlockingTask(
226 FROM_HERE,
227 DomStorageTaskRunner::COMMIT_SEQUENCE,
228 base::Bind(base::IgnoreResult(&SessionStorageDatabase::DeleteArea),
229 session_storage_backing_.get(),
230 persistent_namespace_id_, origin_));
186 } 231 }
187 } 232 }
188 233
189 void DomStorageArea::PurgeMemory() { 234 void DomStorageArea::PurgeMemory() {
190 DCHECK(!is_shutdown_); 235 DCHECK(!is_shutdown_);
191 if (!is_initial_import_done_ || // We're not using any memory. 236 if (!is_initial_import_done_ || // We're not using any memory.
192 !backing_.get() || // We can't purge anything. 237 !backing_.get() || // We can't purge anything.
193 HasUncommittedChanges()) // We leave things alone with changes pending. 238 HasUncommittedChanges()) // We leave things alone with changes pending.
194 return; 239 return;
195 240
196 // Drop the in memory cache, we'll reload when needed. 241 // Drop the in memory cache, we'll reload when needed.
197 is_initial_import_done_ = false; 242 is_initial_import_done_ = false;
198 map_ = new DomStorageMap(kPerAreaQuota + kPerAreaOverQuotaAllowance); 243 map_ = new DomStorageMap(kPerAreaQuota + kPerAreaOverQuotaAllowance);
199 244
200 // Recreate the database object, this frees up the open sqlite connection 245 // Recreate the database object, this frees up the open sqlite connection
201 // and its page cache. 246 // and its page cache.
202 backing_.reset(new DomStorageDatabase(backing_->file_path())); 247 backing_->Reset();
203 } 248 }
204 249
205 void DomStorageArea::Shutdown() { 250 void DomStorageArea::Shutdown() {
206 DCHECK(!is_shutdown_); 251 DCHECK(!is_shutdown_);
207 is_shutdown_ = true; 252 is_shutdown_ = true;
208 map_ = NULL; 253 map_ = NULL;
209 if (!backing_.get()) 254 if (!backing_.get())
210 return; 255 return;
211 256
212 bool success = task_runner_->PostShutdownBlockingTask( 257 bool success = task_runner_->PostShutdownBlockingTask(
213 FROM_HERE, 258 FROM_HERE,
214 DomStorageTaskRunner::COMMIT_SEQUENCE, 259 DomStorageTaskRunner::COMMIT_SEQUENCE,
215 base::Bind(&DomStorageArea::ShutdownInCommitSequence, this)); 260 base::Bind(&DomStorageArea::ShutdownInCommitSequence, this));
216 DCHECK(success); 261 DCHECK(success);
217 } 262 }
218 263
219 void DomStorageArea::InitialImportIfNeeded() { 264 void DomStorageArea::InitialImportIfNeeded() {
220 if (is_initial_import_done_) 265 if (is_initial_import_done_)
221 return; 266 return;
222 267
223 DCHECK_EQ(kLocalStorageNamespaceId, namespace_id_);
224 DCHECK(backing_.get()); 268 DCHECK(backing_.get());
225 269
226 ValuesMap initial_values; 270 ValuesMap initial_values;
227 backing_->ReadAllValues(&initial_values); 271 backing_->ReadAllValues(&initial_values);
228 map_->SwapValues(&initial_values); 272 map_->SwapValues(&initial_values);
229 is_initial_import_done_ = true; 273 is_initial_import_done_ = true;
230 } 274 }
231 275
232 DomStorageArea::CommitBatch* DomStorageArea::CreateCommitBatchIfNeeded() { 276 DomStorageArea::CommitBatch* DomStorageArea::CreateCommitBatchIfNeeded() {
233 DCHECK(!is_shutdown_); 277 DCHECK(!is_shutdown_);
234 if (!commit_batch_.get()) { 278 if (!commit_batch_.get()) {
235 commit_batch_.reset(new CommitBatch()); 279 commit_batch_.reset(new CommitBatch());
236 280
237 // Start a timer to commit any changes that accrue in the batch, but only if 281 // 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 282 // no commits are currently in flight. In that case the timer will be
239 // started after the commits have happened. 283 // started after the commits have happened.
240 if (!commit_batches_in_flight_) { 284 if (!commit_batches_in_flight_) {
241 task_runner_->PostDelayedTask( 285 task_runner_->PostDelayedTask(
242 FROM_HERE, 286 FROM_HERE,
243 base::Bind(&DomStorageArea::OnCommitTimer, this), 287 base::Bind(&DomStorageArea::OnCommitTimer, this),
244 base::TimeDelta::FromSeconds(kCommitTimerSeconds)); 288 base::TimeDelta::FromSeconds(kCommitTimerSeconds));
245 } 289 }
246 } 290 }
247 return commit_batch_.get(); 291 return commit_batch_.get();
248 } 292 }
249 293
250 void DomStorageArea::OnCommitTimer() { 294 void DomStorageArea::OnCommitTimer() {
251 DCHECK_EQ(kLocalStorageNamespaceId, namespace_id_);
252 if (is_shutdown_) 295 if (is_shutdown_)
253 return; 296 return;
254 297
255 DCHECK(backing_.get()); 298 DCHECK(backing_.get());
256 DCHECK(commit_batch_.get()); 299
257 DCHECK(!commit_batches_in_flight_); 300 // It's possible that there is nothing to commit, since a shallow copy occured
301 // before the timer fired.
302 if (!commit_batch_.get())
303 return;
258 304
259 // This method executes on the primary sequence, we schedule 305 // This method executes on the primary sequence, we schedule
260 // a task for immediate execution on the commit sequence. 306 // a task for immediate execution on the commit sequence.
261 DCHECK(task_runner_->IsRunningOnPrimarySequence()); 307 DCHECK(task_runner_->IsRunningOnPrimarySequence());
262 bool success = task_runner_->PostShutdownBlockingTask( 308 bool success = task_runner_->PostShutdownBlockingTask(
263 FROM_HERE, 309 FROM_HERE,
264 DomStorageTaskRunner::COMMIT_SEQUENCE, 310 DomStorageTaskRunner::COMMIT_SEQUENCE,
265 base::Bind(&DomStorageArea::CommitChanges, this, 311 base::Bind(&DomStorageArea::CommitChanges, this,
266 base::Owned(commit_batch_.release()))); 312 base::Owned(commit_batch_.release())));
267 ++commit_batches_in_flight_; 313 ++commit_batches_in_flight_;
268 DCHECK(success); 314 DCHECK(success);
269 } 315 }
270 316
271 void DomStorageArea::CommitChanges(const CommitBatch* commit_batch) { 317 void DomStorageArea::CommitChanges(const CommitBatch* commit_batch) {
272 // This method executes on the commit sequence. 318 // This method executes on the commit sequence.
273 DCHECK(task_runner_->IsRunningOnCommitSequence()); 319 DCHECK(task_runner_->IsRunningOnCommitSequence());
274 bool success = backing_->CommitChanges(commit_batch->clear_all_first, 320 bool success = backing_->CommitChanges(commit_batch->clear_all_first,
275 commit_batch->changed_values); 321 commit_batch->changed_values);
276 DCHECK(success); // TODO(michaeln): what if it fails? 322 DCHECK(success); // TODO(michaeln): what if it fails?
277 task_runner_->PostTask( 323 task_runner_->PostTask(
278 FROM_HERE, 324 FROM_HERE,
279 base::Bind(&DomStorageArea::OnCommitComplete, this)); 325 base::Bind(&DomStorageArea::OnCommitComplete, this));
280 } 326 }
281 327
282 void DomStorageArea::OnCommitComplete() { 328 void DomStorageArea::OnCommitComplete() {
283 // We're back on the primary sequence in this method. 329 // We're back on the primary sequence in this method.
284 DCHECK(task_runner_->IsRunningOnPrimarySequence()); 330 DCHECK(task_runner_->IsRunningOnPrimarySequence());
331 --commit_batches_in_flight_;
285 if (is_shutdown_) 332 if (is_shutdown_)
286 return; 333 return;
287 --commit_batches_in_flight_;
288 if (commit_batch_.get() && !commit_batches_in_flight_) { 334 if (commit_batch_.get() && !commit_batches_in_flight_) {
289 // More changes have accrued, restart the timer. 335 // More changes have accrued, restart the timer.
290 task_runner_->PostDelayedTask( 336 task_runner_->PostDelayedTask(
291 FROM_HERE, 337 FROM_HERE,
292 base::Bind(&DomStorageArea::OnCommitTimer, this), 338 base::Bind(&DomStorageArea::OnCommitTimer, this),
293 base::TimeDelta::FromSeconds(kCommitTimerSeconds)); 339 base::TimeDelta::FromSeconds(kCommitTimerSeconds));
294 } 340 }
295 } 341 }
296 342
297 void DomStorageArea::ShutdownInCommitSequence() { 343 void DomStorageArea::ShutdownInCommitSequence() {
298 // This method executes on the commit sequence. 344 // This method executes on the commit sequence.
299 DCHECK(task_runner_->IsRunningOnCommitSequence()); 345 DCHECK(task_runner_->IsRunningOnCommitSequence());
300 DCHECK(backing_.get()); 346 DCHECK(backing_.get());
301 if (commit_batch_.get()) { 347 if (commit_batch_.get()) {
302 // Commit any changes that accrued prior to the timer firing. 348 // Commit any changes that accrued prior to the timer firing.
303 bool success = backing_->CommitChanges( 349 bool success = backing_->CommitChanges(
304 commit_batch_->clear_all_first, 350 commit_batch_->clear_all_first,
305 commit_batch_->changed_values); 351 commit_batch_->changed_values);
306 DCHECK(success); 352 DCHECK(success);
307 } 353 }
308 commit_batch_.reset(); 354 commit_batch_.reset();
309 backing_.reset(); 355 backing_.reset();
356 session_storage_backing_ = NULL;
310 } 357 }
311 358
312 } // namespace dom_storage 359 } // namespace dom_storage
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698