| 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/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 Loading... |
| 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 Loading... |
| 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 |
| 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()) { |
| 185 DomStorageDatabase::GetJournalFilePath(backing_->file_path()), false); | 215 task_runner_->PostShutdownBlockingTask( |
| 216 FROM_HERE, |
| 217 DomStorageTaskRunner::COMMIT_SEQUENCE, |
| 218 base::Bind(base::IgnoreResult(&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 |
| OLD | NEW |