Chromium Code Reviews| OLD | NEW |
|---|---|
| 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/location.h" | 8 #include "base/location.h" |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "base/time.h" | 10 #include "base/time.h" |
| 11 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h" | 11 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h" |
| 12 #include "webkit/database/database_util.h" | 12 #include "webkit/database/database_util.h" |
| 13 #include "webkit/dom_storage/dom_storage_map.h" | 13 #include "webkit/dom_storage/dom_storage_map.h" |
| 14 #include "webkit/dom_storage/dom_storage_namespace.h" | 14 #include "webkit/dom_storage/dom_storage_namespace.h" |
| 15 #include "webkit/dom_storage/dom_storage_task_runner.h" | 15 #include "webkit/dom_storage/dom_storage_task_runner.h" |
| 16 #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" | 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 25 matching lines...) Expand all Loading... | |
| 53 } | 55 } |
| 54 | 56 |
| 55 DomStorageArea::DomStorageArea(const GURL& origin, const FilePath& directory, | 57 DomStorageArea::DomStorageArea(const GURL& origin, const FilePath& directory, |
| 56 DomStorageTaskRunner* task_runner) | 58 DomStorageTaskRunner* task_runner) |
| 57 : namespace_id_(kLocalStorageNamespaceId), origin_(origin), | 59 : namespace_id_(kLocalStorageNamespaceId), origin_(origin), |
| 58 directory_(directory), | 60 directory_(directory), |
| 59 task_runner_(task_runner), | 61 task_runner_(task_runner), |
| 60 map_(new DomStorageMap(kPerAreaQuota + kPerAreaOverQuotaAllowance)), | 62 map_(new DomStorageMap(kPerAreaQuota + kPerAreaOverQuotaAllowance)), |
| 61 is_initial_import_done_(true), | 63 is_initial_import_done_(true), |
| 62 is_shutdown_(false), | 64 is_shutdown_(false), |
| 63 commit_batches_in_flight_(0) { | 65 commit_batches_in_flight_(0), |
| 66 deletion_in_progress_(false) { | |
| 64 if (!directory.empty()) { | 67 if (!directory.empty()) { |
| 65 FilePath path = directory.Append(DatabaseFileNameFromOrigin(origin_)); | 68 FilePath path = directory.Append(DatabaseFileNameFromOrigin(origin_)); |
| 66 backing_.reset(new LocalStorageDatabaseAdapter(path)); | 69 backing_.reset(new LocalStorageDatabaseAdapter(path)); |
| 67 is_initial_import_done_ = false; | 70 is_initial_import_done_ = false; |
| 68 } | 71 } |
| 69 } | 72 } |
| 70 | 73 |
| 71 DomStorageArea::DomStorageArea( | 74 DomStorageArea::DomStorageArea( |
| 72 int64 namespace_id, | 75 int64 namespace_id, |
| 73 const std::string& persistent_namespace_id, | 76 const std::string& persistent_namespace_id, |
| 74 const GURL& origin, | 77 const GURL& origin, |
| 78 SessionStorageDatabase* session_storage_backing, | |
| 75 DomStorageTaskRunner* task_runner) | 79 DomStorageTaskRunner* task_runner) |
| 76 : namespace_id_(namespace_id), | 80 : namespace_id_(namespace_id), |
| 77 persistent_namespace_id_(persistent_namespace_id), | 81 persistent_namespace_id_(persistent_namespace_id), |
| 78 origin_(origin), | 82 origin_(origin), |
| 79 task_runner_(task_runner), | 83 task_runner_(task_runner), |
| 80 map_(new DomStorageMap(kPerAreaQuota + kPerAreaOverQuotaAllowance)), | 84 map_(new DomStorageMap(kPerAreaQuota + kPerAreaOverQuotaAllowance)), |
| 85 session_storage_backing_(session_storage_backing), | |
| 81 is_initial_import_done_(true), | 86 is_initial_import_done_(true), |
| 82 is_shutdown_(false), | 87 is_shutdown_(false), |
| 83 commit_batches_in_flight_(0) { | 88 commit_batches_in_flight_(0), |
| 89 deletion_in_progress_(false) { | |
| 84 DCHECK(namespace_id != kLocalStorageNamespaceId); | 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 } | |
| 85 } | 96 } |
| 86 | 97 |
| 87 DomStorageArea::~DomStorageArea() { | 98 DomStorageArea::~DomStorageArea() { |
| 88 } | 99 } |
| 89 | 100 |
| 90 void DomStorageArea::ExtractValues(ValuesMap* map) { | 101 void DomStorageArea::ExtractValues(ValuesMap* map) { |
| 91 if (is_shutdown_) | 102 if (is_shutdown_) |
| 92 return; | 103 return; |
| 93 InitialImportIfNeeded(); | 104 InitialImportIfNeeded(); |
| 94 map_->ExtractValues(map); | 105 map_->ExtractValues(map); |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 161 } | 172 } |
| 162 | 173 |
| 163 return true; | 174 return true; |
| 164 } | 175 } |
| 165 | 176 |
| 166 DomStorageArea* DomStorageArea::ShallowCopy( | 177 DomStorageArea* DomStorageArea::ShallowCopy( |
| 167 int64 destination_namespace_id, | 178 int64 destination_namespace_id, |
| 168 const std::string& destination_persistent_namespace_id) { | 179 const std::string& destination_persistent_namespace_id) { |
| 169 DCHECK_NE(kLocalStorageNamespaceId, namespace_id_); | 180 DCHECK_NE(kLocalStorageNamespaceId, namespace_id_); |
| 170 DCHECK_NE(kLocalStorageNamespaceId, destination_namespace_id); | 181 DCHECK_NE(kLocalStorageNamespaceId, destination_namespace_id); |
| 171 DCHECK(!backing_.get()); // SessionNamespaces aren't stored on disk. | |
| 172 | 182 |
| 173 DomStorageArea* copy = new DomStorageArea( | 183 DomStorageArea* copy = new DomStorageArea( |
| 174 destination_namespace_id, destination_persistent_namespace_id, origin_, | 184 destination_namespace_id, destination_persistent_namespace_id, origin_, |
| 175 task_runner_); | 185 session_storage_backing_, task_runner_); |
| 176 copy->map_ = map_; | 186 copy->map_ = map_; |
| 177 copy->is_shutdown_ = is_shutdown_; | 187 copy->is_shutdown_ = is_shutdown_; |
| 188 copy->is_initial_import_done_ = true; | |
| 189 | |
| 190 // All the uncommitted changes to this area need to happen before the actual | |
| 191 // shallow copy is made (scheduled by the upper layer). Another OnCommitTimer | |
| 192 // call might be in the event queue at this point, but it's handled gracefully | |
| 193 // when it fires. | |
| 194 if (commit_batch_.get()) | |
| 195 OnCommitTimer(); | |
| 178 return copy; | 196 return copy; |
| 179 } | 197 } |
| 180 | 198 |
| 181 bool DomStorageArea::HasUncommittedChanges() const { | 199 bool DomStorageArea::HasUncommittedChanges() const { |
| 182 DCHECK(!is_shutdown_); | 200 DCHECK(!is_shutdown_); |
| 183 return commit_batch_.get() || commit_batches_in_flight_; | 201 return commit_batch_.get() || commit_batches_in_flight_; |
| 184 } | 202 } |
| 185 | 203 |
| 186 void DomStorageArea::DeleteOrigin() { | 204 void DomStorageArea::DeleteOrigin() { |
| 187 DCHECK(!is_shutdown_); | 205 DCHECK(!is_shutdown_); |
| 188 if (HasUncommittedChanges()) { | 206 if (HasUncommittedChanges()) { |
| 189 // TODO(michaeln): This logically deletes the data immediately, | 207 // TODO(michaeln): This logically deletes the data immediately, |
| 190 // and in a matter of a second, deletes the rows from the backing | 208 // and in a matter of a second, deletes the rows from the backing |
| 191 // database file, but the file itself will linger until shutdown | 209 // database file, but the file itself will linger until shutdown |
| 192 // or purge time. Ideally, this should delete the file more | 210 // or purge time. Ideally, this should delete the file more |
| 193 // quickly. | 211 // quickly. |
| 194 Clear(); | 212 Clear(); |
| 195 return; | 213 return; |
| 196 } | 214 } |
| 197 map_ = new DomStorageMap(kPerAreaQuota + kPerAreaOverQuotaAllowance); | 215 map_ = new DomStorageMap(kPerAreaQuota + kPerAreaOverQuotaAllowance); |
| 198 if (backing_.get()) { | 216 if (backing_.get() && !session_storage_backing_.get()) { |
| 217 // This is localStorage. | |
| 199 is_initial_import_done_ = false; | 218 is_initial_import_done_ = false; |
| 200 backing_->Reset(); | 219 backing_->Reset(); |
| 201 backing_->DeleteFiles(); | 220 backing_->DeleteFiles(); |
| 221 } else if (session_storage_backing_.get()) { | |
| 222 // No need to read the data (there will be no data), also, the | |
| 223 // PRIMARY_SEQUENCE thread shouldn't try to read the data while | |
| 224 // SessionStorageDatabase::DeleteArea is in progress. | |
| 225 is_initial_import_done_ = true; | |
| 226 { | |
| 227 base::AutoLock lock(deletion_in_progress_lock_); | |
|
michaeln
2012/07/10 01:16:58
What does the flag+lock accomplish given that is_i
marja
2012/07/10 13:40:52
The lock was guarding the bool (I didn't want to a
| |
| 228 deletion_in_progress_ = true; | |
| 229 } | |
| 230 task_runner_->PostShutdownBlockingTask( | |
| 231 FROM_HERE, | |
| 232 DomStorageTaskRunner::COMMIT_SEQUENCE, | |
| 233 base::Bind(&DomStorageArea::DeleteOriginInCommitSequence, this)); | |
| 202 } | 234 } |
| 203 } | 235 } |
| 204 | 236 |
| 205 void DomStorageArea::PurgeMemory() { | 237 void DomStorageArea::PurgeMemory() { |
| 206 DCHECK(!is_shutdown_); | 238 DCHECK(!is_shutdown_); |
| 207 if (!is_initial_import_done_ || // We're not using any memory. | 239 if (!is_initial_import_done_ || // We're not using any memory. |
| 208 !backing_.get() || // We can't purge anything. | 240 !backing_.get() || // We can't purge anything. |
| 209 HasUncommittedChanges()) // We leave things alone with changes pending. | 241 HasUncommittedChanges()) // We leave things alone with changes pending. |
| 210 return; | 242 return; |
| 211 | 243 |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 229 FROM_HERE, | 261 FROM_HERE, |
| 230 DomStorageTaskRunner::COMMIT_SEQUENCE, | 262 DomStorageTaskRunner::COMMIT_SEQUENCE, |
| 231 base::Bind(&DomStorageArea::ShutdownInCommitSequence, this)); | 263 base::Bind(&DomStorageArea::ShutdownInCommitSequence, this)); |
| 232 DCHECK(success); | 264 DCHECK(success); |
| 233 } | 265 } |
| 234 | 266 |
| 235 void DomStorageArea::InitialImportIfNeeded() { | 267 void DomStorageArea::InitialImportIfNeeded() { |
| 236 if (is_initial_import_done_) | 268 if (is_initial_import_done_) |
| 237 return; | 269 return; |
| 238 | 270 |
| 239 DCHECK_EQ(kLocalStorageNamespaceId, namespace_id_); | 271 is_initial_import_done_ = true; |
| 272 { | |
| 273 base::AutoLock lock(deletion_in_progress_lock_); | |
| 274 if (deletion_in_progress_) { | |
| 275 // The data is getting deleted in the commit sequence; we shouldn't read | |
| 276 // it just before deletion. The data will be empty, so it's ok to not read | |
| 277 // anything. | |
| 278 return; | |
| 279 } | |
| 280 } | |
| 281 | |
| 240 DCHECK(backing_.get()); | 282 DCHECK(backing_.get()); |
| 241 | 283 |
| 242 ValuesMap initial_values; | 284 ValuesMap initial_values; |
| 243 backing_->ReadAllValues(&initial_values); | 285 backing_->ReadAllValues(&initial_values); |
| 244 map_->SwapValues(&initial_values); | 286 map_->SwapValues(&initial_values); |
| 245 is_initial_import_done_ = true; | |
| 246 } | 287 } |
| 247 | 288 |
| 248 DomStorageArea::CommitBatch* DomStorageArea::CreateCommitBatchIfNeeded() { | 289 DomStorageArea::CommitBatch* DomStorageArea::CreateCommitBatchIfNeeded() { |
| 249 DCHECK(!is_shutdown_); | 290 DCHECK(!is_shutdown_); |
| 250 if (!commit_batch_.get()) { | 291 if (!commit_batch_.get()) { |
| 251 commit_batch_.reset(new CommitBatch()); | 292 commit_batch_.reset(new CommitBatch()); |
| 252 | 293 |
| 253 // Start a timer to commit any changes that accrue in the batch, but only if | 294 // Start a timer to commit any changes that accrue in the batch, but only if |
| 254 // no commits are currently in flight. In that case the timer will be | 295 // no commits are currently in flight. In that case the timer will be |
| 255 // started after the commits have happened. | 296 // started after the commits have happened. |
| 256 if (!commit_batches_in_flight_) { | 297 if (!commit_batches_in_flight_) { |
| 257 task_runner_->PostDelayedTask( | 298 task_runner_->PostDelayedTask( |
| 258 FROM_HERE, | 299 FROM_HERE, |
| 259 base::Bind(&DomStorageArea::OnCommitTimer, this), | 300 base::Bind(&DomStorageArea::OnCommitTimer, this), |
| 260 base::TimeDelta::FromSeconds(kCommitTimerSeconds)); | 301 base::TimeDelta::FromSeconds(kCommitTimerSeconds)); |
| 261 } | 302 } |
| 262 } | 303 } |
| 263 return commit_batch_.get(); | 304 return commit_batch_.get(); |
| 264 } | 305 } |
| 265 | 306 |
| 266 void DomStorageArea::OnCommitTimer() { | 307 void DomStorageArea::OnCommitTimer() { |
| 267 DCHECK_EQ(kLocalStorageNamespaceId, namespace_id_); | |
| 268 if (is_shutdown_) | 308 if (is_shutdown_) |
| 269 return; | 309 return; |
| 270 | 310 |
| 271 DCHECK(backing_.get()); | 311 DCHECK(backing_.get()); |
| 272 DCHECK(commit_batch_.get()); | 312 |
| 273 DCHECK(!commit_batches_in_flight_); | 313 // It's possible that there is nothing to commit, since a shallow copy occured |
| 314 // before the timer fired. | |
| 315 if (!commit_batch_.get()) | |
| 316 return; | |
| 274 | 317 |
| 275 // This method executes on the primary sequence, we schedule | 318 // This method executes on the primary sequence, we schedule |
| 276 // a task for immediate execution on the commit sequence. | 319 // a task for immediate execution on the commit sequence. |
| 277 DCHECK(task_runner_->IsRunningOnPrimarySequence()); | 320 DCHECK(task_runner_->IsRunningOnPrimarySequence()); |
| 278 bool success = task_runner_->PostShutdownBlockingTask( | 321 bool success = task_runner_->PostShutdownBlockingTask( |
| 279 FROM_HERE, | 322 FROM_HERE, |
| 280 DomStorageTaskRunner::COMMIT_SEQUENCE, | 323 DomStorageTaskRunner::COMMIT_SEQUENCE, |
| 281 base::Bind(&DomStorageArea::CommitChanges, this, | 324 base::Bind(&DomStorageArea::CommitChanges, this, |
| 282 base::Owned(commit_batch_.release()))); | 325 base::Owned(commit_batch_.release()))); |
| 283 ++commit_batches_in_flight_; | 326 ++commit_batches_in_flight_; |
| 284 DCHECK(success); | 327 DCHECK(success); |
| 285 } | 328 } |
| 286 | 329 |
| 287 void DomStorageArea::CommitChanges(const CommitBatch* commit_batch) { | 330 void DomStorageArea::CommitChanges(const CommitBatch* commit_batch) { |
| 288 // This method executes on the commit sequence. | 331 // This method executes on the commit sequence. |
| 289 DCHECK(task_runner_->IsRunningOnCommitSequence()); | 332 DCHECK(task_runner_->IsRunningOnCommitSequence()); |
| 290 bool success = backing_->CommitChanges(commit_batch->clear_all_first, | 333 bool success = backing_->CommitChanges(commit_batch->clear_all_first, |
| 291 commit_batch->changed_values); | 334 commit_batch->changed_values); |
| 292 DCHECK(success); // TODO(michaeln): what if it fails? | 335 DCHECK(success); // TODO(michaeln): what if it fails? |
| 293 task_runner_->PostTask( | 336 task_runner_->PostTask( |
| 294 FROM_HERE, | 337 FROM_HERE, |
| 295 base::Bind(&DomStorageArea::OnCommitComplete, this)); | 338 base::Bind(&DomStorageArea::OnCommitComplete, this)); |
| 296 } | 339 } |
| 297 | 340 |
| 298 void DomStorageArea::OnCommitComplete() { | 341 void DomStorageArea::OnCommitComplete() { |
| 299 // We're back on the primary sequence in this method. | 342 // We're back on the primary sequence in this method. |
| 300 DCHECK(task_runner_->IsRunningOnPrimarySequence()); | 343 DCHECK(task_runner_->IsRunningOnPrimarySequence()); |
| 344 --commit_batches_in_flight_; | |
| 301 if (is_shutdown_) | 345 if (is_shutdown_) |
| 302 return; | 346 return; |
| 303 --commit_batches_in_flight_; | |
| 304 if (commit_batch_.get() && !commit_batches_in_flight_) { | 347 if (commit_batch_.get() && !commit_batches_in_flight_) { |
| 305 // More changes have accrued, restart the timer. | 348 // More changes have accrued, restart the timer. |
| 306 task_runner_->PostDelayedTask( | 349 task_runner_->PostDelayedTask( |
| 307 FROM_HERE, | 350 FROM_HERE, |
| 308 base::Bind(&DomStorageArea::OnCommitTimer, this), | 351 base::Bind(&DomStorageArea::OnCommitTimer, this), |
| 309 base::TimeDelta::FromSeconds(kCommitTimerSeconds)); | 352 base::TimeDelta::FromSeconds(kCommitTimerSeconds)); |
| 310 } | 353 } |
| 311 } | 354 } |
| 312 | 355 |
| 356 void DomStorageArea::DeleteOriginInCommitSequence() { | |
| 357 session_storage_backing_->DeleteArea(persistent_namespace_id_, origin_); | |
| 358 base::AutoLock lock(deletion_in_progress_lock_); | |
| 359 deletion_in_progress_ = false; | |
| 360 } | |
| 361 | |
| 313 void DomStorageArea::ShutdownInCommitSequence() { | 362 void DomStorageArea::ShutdownInCommitSequence() { |
| 314 // This method executes on the commit sequence. | 363 // This method executes on the commit sequence. |
| 315 DCHECK(task_runner_->IsRunningOnCommitSequence()); | 364 DCHECK(task_runner_->IsRunningOnCommitSequence()); |
| 316 DCHECK(backing_.get()); | 365 DCHECK(backing_.get()); |
| 317 if (commit_batch_.get()) { | 366 if (commit_batch_.get()) { |
| 318 // Commit any changes that accrued prior to the timer firing. | 367 // Commit any changes that accrued prior to the timer firing. |
| 319 bool success = backing_->CommitChanges( | 368 bool success = backing_->CommitChanges( |
| 320 commit_batch_->clear_all_first, | 369 commit_batch_->clear_all_first, |
| 321 commit_batch_->changed_values); | 370 commit_batch_->changed_values); |
| 322 DCHECK(success); | 371 DCHECK(success); |
| 323 } | 372 } |
| 324 commit_batch_.reset(); | 373 commit_batch_.reset(); |
| 325 backing_.reset(); | 374 backing_.reset(); |
| 375 session_storage_backing_ = NULL; | |
| 326 } | 376 } |
| 327 | 377 |
| 328 } // namespace dom_storage | 378 } // namespace dom_storage |
| OLD | NEW |