1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "base/files/file_util.h" 6#include "base/files/scoped_temp_dir.h" 7#include "base/logging.h" 8#include "base/message_loop/message_loop.h" 9#include "base/strings/utf_string_conversions.h" 10#include "base/test/test_simple_task_runner.h" 11#include "content/browser/indexed_db/indexed_db_connection.h" 12#include "content/browser/indexed_db/indexed_db_context_impl.h" 13#include "content/browser/indexed_db/indexed_db_factory_impl.h" 14#include "content/browser/indexed_db/mock_indexed_db_callbacks.h" 15#include "content/browser/indexed_db/mock_indexed_db_database_callbacks.h" 16#include "storage/common/database/database_identifier.h" 17#include "testing/gtest/include/gtest/gtest.h" 18#include "third_party/WebKit/public/platform/WebIDBDatabaseException.h" 19#include "third_party/WebKit/public/platform/WebIDBTypes.h" 20#include "url/gurl.h" 21 22using base::ASCIIToUTF16; 23 24namespace content { 25 26namespace { 27 28class MockIDBFactory : public IndexedDBFactoryImpl { 29 public: 30 explicit MockIDBFactory(IndexedDBContextImpl* context) 31 : IndexedDBFactoryImpl(context) {} 32 scoped_refptr<IndexedDBBackingStore> TestOpenBackingStore( 33 const GURL& origin, 34 const base::FilePath& data_directory) { 35 blink::WebIDBDataLoss data_loss = 36 blink::WebIDBDataLossNone; 37 std::string data_loss_message; 38 bool disk_full; 39 leveldb::Status s; 40 scoped_refptr<IndexedDBBackingStore> backing_store = 41 OpenBackingStore(origin, 42 data_directory, 43 NULL /* request_context */, 44 &data_loss, 45 &data_loss_message, 46 &disk_full, 47 &s); 48 EXPECT_EQ(blink::WebIDBDataLossNone, data_loss); 49 return backing_store; 50 } 51 52 void TestCloseBackingStore(IndexedDBBackingStore* backing_store) { 53 CloseBackingStore(backing_store->origin_url()); 54 } 55 56 void TestReleaseBackingStore(IndexedDBBackingStore* backing_store, 57 bool immediate) { 58 ReleaseBackingStore(backing_store->origin_url(), immediate); 59 } 60 61 private: 62 virtual ~MockIDBFactory() {} 63 64 DISALLOW_COPY_AND_ASSIGN(MockIDBFactory); 65}; 66 67} // namespace 68 69class IndexedDBFactoryTest : public testing::Test { 70 public: 71 IndexedDBFactoryTest() { 72 task_runner_ = new base::TestSimpleTaskRunner(); 73 context_ = new IndexedDBContextImpl(base::FilePath(), 74 NULL /* special_storage_policy */, 75 NULL /* quota_manager_proxy */, 76 task_runner_.get()); 77 idb_factory_ = new MockIDBFactory(context_.get()); 78 } 79 80 protected: 81 // For timers to post events. 82 base::MessageLoop loop_; 83 84 MockIDBFactory* factory() const { return idb_factory_.get(); } 85 void clear_factory() { idb_factory_ = NULL; } 86 IndexedDBContextImpl* context() const { return context_.get(); } 87 88 private: 89 scoped_refptr<base::TestSimpleTaskRunner> task_runner_; 90 scoped_refptr<IndexedDBContextImpl> context_; 91 scoped_refptr<MockIDBFactory> idb_factory_; 92 93 DISALLOW_COPY_AND_ASSIGN(IndexedDBFactoryTest); 94}; 95 96TEST_F(IndexedDBFactoryTest, BackingStoreLifetime) { 97 GURL origin1("http://localhost:81"); 98 GURL origin2("http://localhost:82"); 99 100 base::ScopedTempDir temp_directory; 101 ASSERT_TRUE(temp_directory.CreateUniqueTempDir()); 102 scoped_refptr<IndexedDBBackingStore> disk_store1 = 103 factory()->TestOpenBackingStore(origin1, temp_directory.path()); 104 105 scoped_refptr<IndexedDBBackingStore> disk_store2 = 106 factory()->TestOpenBackingStore(origin1, temp_directory.path()); 107 EXPECT_EQ(disk_store1.get(), disk_store2.get()); 108 109 scoped_refptr<IndexedDBBackingStore> disk_store3 = 110 factory()->TestOpenBackingStore(origin2, temp_directory.path()); 111 112 factory()->TestCloseBackingStore(disk_store1.get()); 113 factory()->TestCloseBackingStore(disk_store3.get()); 114 115 EXPECT_FALSE(disk_store1->HasOneRef()); 116 EXPECT_FALSE(disk_store2->HasOneRef()); 117 EXPECT_TRUE(disk_store3->HasOneRef()); 118 119 disk_store2 = NULL; 120 EXPECT_TRUE(disk_store1->HasOneRef()); 121} 122 123TEST_F(IndexedDBFactoryTest, BackingStoreLazyClose) { 124 GURL origin("http://localhost:81"); 125 126 base::ScopedTempDir temp_directory; 127 ASSERT_TRUE(temp_directory.CreateUniqueTempDir()); 128 scoped_refptr<IndexedDBBackingStore> store = 129 factory()->TestOpenBackingStore(origin, temp_directory.path()); 130 131 // Give up the local refptr so that the factory has the only 132 // outstanding reference. 133 IndexedDBBackingStore* store_ptr = store.get(); 134 store = NULL; 135 EXPECT_FALSE(store_ptr->close_timer()->IsRunning()); 136 factory()->TestReleaseBackingStore(store_ptr, false); 137 EXPECT_TRUE(store_ptr->close_timer()->IsRunning()); 138 139 factory()->TestOpenBackingStore(origin, temp_directory.path()); 140 EXPECT_FALSE(store_ptr->close_timer()->IsRunning()); 141 factory()->TestReleaseBackingStore(store_ptr, false); 142 EXPECT_TRUE(store_ptr->close_timer()->IsRunning()); 143 144 // Take back a ref ptr and ensure that the actual close 145 // stops a running timer. 146 store = store_ptr; 147 factory()->TestCloseBackingStore(store_ptr); 148 EXPECT_FALSE(store_ptr->close_timer()->IsRunning()); 149} 150 151TEST_F(IndexedDBFactoryTest, MemoryBackingStoreLifetime) { 152 GURL origin1("http://localhost:81"); 153 GURL origin2("http://localhost:82"); 154 155 scoped_refptr<IndexedDBBackingStore> mem_store1 = 156 factory()->TestOpenBackingStore(origin1, base::FilePath()); 157 158 scoped_refptr<IndexedDBBackingStore> mem_store2 = 159 factory()->TestOpenBackingStore(origin1, base::FilePath()); 160 EXPECT_EQ(mem_store1.get(), mem_store2.get()); 161 162 scoped_refptr<IndexedDBBackingStore> mem_store3 = 163 factory()->TestOpenBackingStore(origin2, base::FilePath()); 164 165 factory()->TestCloseBackingStore(mem_store1.get()); 166 factory()->TestCloseBackingStore(mem_store3.get()); 167 168 EXPECT_FALSE(mem_store1->HasOneRef()); 169 EXPECT_FALSE(mem_store2->HasOneRef()); 170 EXPECT_FALSE(mem_store3->HasOneRef()); 171 172 clear_factory(); 173 EXPECT_FALSE(mem_store1->HasOneRef()); // mem_store1 and 2 174 EXPECT_FALSE(mem_store2->HasOneRef()); // mem_store1 and 2 175 EXPECT_TRUE(mem_store3->HasOneRef()); 176 177 mem_store2 = NULL; 178 EXPECT_TRUE(mem_store1->HasOneRef()); 179} 180 181TEST_F(IndexedDBFactoryTest, RejectLongOrigins) { 182 base::ScopedTempDir temp_directory; 183 ASSERT_TRUE(temp_directory.CreateUniqueTempDir()); 184 const base::FilePath base_path = temp_directory.path(); 185 186 int limit = base::GetMaximumPathComponentLength(base_path); 187 EXPECT_GT(limit, 0); 188 189 std::string origin(limit + 1, 'x'); 190 GURL too_long_origin("http://" + origin + ":81/"); 191 scoped_refptr<IndexedDBBackingStore> diskStore1 = 192 factory()->TestOpenBackingStore(too_long_origin, base_path); 193 EXPECT_FALSE(diskStore1.get()); 194 195 GURL ok_origin("http://someorigin.com:82/"); 196 scoped_refptr<IndexedDBBackingStore> diskStore2 = 197 factory()->TestOpenBackingStore(ok_origin, base_path); 198 EXPECT_TRUE(diskStore2.get()); 199} 200 201class DiskFullFactory : public IndexedDBFactoryImpl { 202 public: 203 explicit DiskFullFactory(IndexedDBContextImpl* context) 204 : IndexedDBFactoryImpl(context) {} 205 206 private: 207 virtual ~DiskFullFactory() {} 208 virtual scoped_refptr<IndexedDBBackingStore> OpenBackingStore( 209 const GURL& origin_url, 210 const base::FilePath& data_directory, 211 net::URLRequestContext* request_context, 212 blink::WebIDBDataLoss* data_loss, 213 std::string* data_loss_message, 214 bool* disk_full, 215 leveldb::Status* s) OVERRIDE { 216 *disk_full = true; 217 *s = leveldb::Status::IOError("Disk is full"); 218 return scoped_refptr<IndexedDBBackingStore>(); 219 } 220 221 DISALLOW_COPY_AND_ASSIGN(DiskFullFactory); 222}; 223 224class LookingForQuotaErrorMockCallbacks : public IndexedDBCallbacks { 225 public: 226 LookingForQuotaErrorMockCallbacks() 227 : IndexedDBCallbacks(NULL, 0, 0), error_called_(false) {} 228 virtual void OnError(const IndexedDBDatabaseError& error) OVERRIDE { 229 error_called_ = true; 230 EXPECT_EQ(blink::WebIDBDatabaseExceptionQuotaError, error.code()); 231 } 232 bool error_called() const { return error_called_; } 233 234 private: 235 virtual ~LookingForQuotaErrorMockCallbacks() {} 236 bool error_called_; 237 238 DISALLOW_COPY_AND_ASSIGN(LookingForQuotaErrorMockCallbacks); 239}; 240 241TEST_F(IndexedDBFactoryTest, QuotaErrorOnDiskFull) { 242 const GURL origin("http://localhost:81"); 243 base::ScopedTempDir temp_directory; 244 ASSERT_TRUE(temp_directory.CreateUniqueTempDir()); 245 246 scoped_refptr<DiskFullFactory> factory = new DiskFullFactory(context()); 247 scoped_refptr<LookingForQuotaErrorMockCallbacks> callbacks = 248 new LookingForQuotaErrorMockCallbacks; 249 scoped_refptr<IndexedDBDatabaseCallbacks> dummy_database_callbacks = 250 new IndexedDBDatabaseCallbacks(NULL, 0, 0); 251 const base::string16 name(ASCIIToUTF16("name")); 252 IndexedDBPendingConnection connection(callbacks, 253 dummy_database_callbacks, 254 0, /* child_process_id */ 255 2, /* transaction_id */ 256 1 /* version */); 257 factory->Open(name, 258 connection, 259 NULL /* request_context */, 260 origin, 261 temp_directory.path()); 262 EXPECT_TRUE(callbacks->error_called()); 263} 264 265TEST_F(IndexedDBFactoryTest, BackingStoreReleasedOnForcedClose) { 266 GURL origin("http://localhost:81"); 267 268 base::ScopedTempDir temp_directory; 269 ASSERT_TRUE(temp_directory.CreateUniqueTempDir()); 270 271 scoped_refptr<MockIndexedDBCallbacks> callbacks(new MockIndexedDBCallbacks()); 272 scoped_refptr<MockIndexedDBDatabaseCallbacks> db_callbacks( 273 new MockIndexedDBDatabaseCallbacks()); 274 const int64 transaction_id = 1; 275 IndexedDBPendingConnection connection( 276 callbacks, 277 db_callbacks, 278 0, /* child_process_id */ 279 transaction_id, 280 IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION); 281 factory()->Open(ASCIIToUTF16("db"), 282 connection, 283 NULL /* request_context */, 284 origin, 285 temp_directory.path()); 286 287 EXPECT_TRUE(callbacks->connection()); 288 289 EXPECT_TRUE(factory()->IsBackingStoreOpen(origin)); 290 EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin)); 291 292 callbacks->connection()->ForceClose(); 293 294 EXPECT_FALSE(factory()->IsBackingStoreOpen(origin)); 295 EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin)); 296} 297 298TEST_F(IndexedDBFactoryTest, BackingStoreReleaseDelayedOnClose) { 299 GURL origin("http://localhost:81"); 300 301 base::ScopedTempDir temp_directory; 302 ASSERT_TRUE(temp_directory.CreateUniqueTempDir()); 303 304 scoped_refptr<MockIndexedDBCallbacks> callbacks(new MockIndexedDBCallbacks()); 305 scoped_refptr<MockIndexedDBDatabaseCallbacks> db_callbacks( 306 new MockIndexedDBDatabaseCallbacks()); 307 const int64 transaction_id = 1; 308 IndexedDBPendingConnection connection( 309 callbacks, 310 db_callbacks, 311 0, /* child_process_id */ 312 transaction_id, 313 IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION); 314 factory()->Open(ASCIIToUTF16("db"), 315 connection, 316 NULL /* request_context */, 317 origin, 318 temp_directory.path()); 319 320 EXPECT_TRUE(callbacks->connection()); 321 IndexedDBBackingStore* store = 322 callbacks->connection()->database()->backing_store(); 323 EXPECT_FALSE(store->HasOneRef()); // Factory and database. 324 325 EXPECT_TRUE(factory()->IsBackingStoreOpen(origin)); 326 callbacks->connection()->Close(); 327 EXPECT_TRUE(store->HasOneRef()); // Factory. 328 EXPECT_TRUE(factory()->IsBackingStoreOpen(origin)); 329 EXPECT_TRUE(factory()->IsBackingStorePendingClose(origin)); 330 EXPECT_TRUE(store->close_timer()->IsRunning()); 331 332 // Take a ref so it won't be destroyed out from under the test. 333 scoped_refptr<IndexedDBBackingStore> store_ref = store; 334 // Now simulate shutdown, which should stop the timer. 335 factory()->ContextDestroyed(); 336 EXPECT_TRUE(store->HasOneRef()); // Local. 337 EXPECT_FALSE(store->close_timer()->IsRunning()); 338 EXPECT_FALSE(factory()->IsBackingStoreOpen(origin)); 339 EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin)); 340} 341 342TEST_F(IndexedDBFactoryTest, DeleteDatabaseClosesBackingStore) { 343 GURL origin("http://localhost:81"); 344 345 base::ScopedTempDir temp_directory; 346 ASSERT_TRUE(temp_directory.CreateUniqueTempDir()); 347 348 EXPECT_FALSE(factory()->IsBackingStoreOpen(origin)); 349 350 const bool expect_connection = false; 351 scoped_refptr<MockIndexedDBCallbacks> callbacks( 352 new MockIndexedDBCallbacks(expect_connection)); 353 factory()->DeleteDatabase(ASCIIToUTF16("db"), 354 NULL /* request_context */, 355 callbacks, 356 origin, 357 temp_directory.path()); 358 359 EXPECT_TRUE(factory()->IsBackingStoreOpen(origin)); 360 EXPECT_TRUE(factory()->IsBackingStorePendingClose(origin)); 361 362 // Now simulate shutdown, which should stop the timer. 363 factory()->ContextDestroyed(); 364 365 EXPECT_FALSE(factory()->IsBackingStoreOpen(origin)); 366 EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin)); 367} 368 369TEST_F(IndexedDBFactoryTest, GetDatabaseNamesClosesBackingStore) { 370 GURL origin("http://localhost:81"); 371 372 base::ScopedTempDir temp_directory; 373 ASSERT_TRUE(temp_directory.CreateUniqueTempDir()); 374 375 EXPECT_FALSE(factory()->IsBackingStoreOpen(origin)); 376 377 const bool expect_connection = false; 378 scoped_refptr<MockIndexedDBCallbacks> callbacks( 379 new MockIndexedDBCallbacks(expect_connection)); 380 factory()->GetDatabaseNames( 381 callbacks, origin, temp_directory.path(), NULL /* request_context */); 382 383 EXPECT_TRUE(factory()->IsBackingStoreOpen(origin)); 384 EXPECT_TRUE(factory()->IsBackingStorePendingClose(origin)); 385 386 // Now simulate shutdown, which should stop the timer. 387 factory()->ContextDestroyed(); 388 389 EXPECT_FALSE(factory()->IsBackingStoreOpen(origin)); 390 EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin)); 391} 392 393TEST_F(IndexedDBFactoryTest, ForceCloseReleasesBackingStore) { 394 GURL origin("http://localhost:81"); 395 396 base::ScopedTempDir temp_directory; 397 ASSERT_TRUE(temp_directory.CreateUniqueTempDir()); 398 399 scoped_refptr<MockIndexedDBCallbacks> callbacks(new MockIndexedDBCallbacks()); 400 scoped_refptr<MockIndexedDBDatabaseCallbacks> db_callbacks( 401 new MockIndexedDBDatabaseCallbacks()); 402 const int64 transaction_id = 1; 403 IndexedDBPendingConnection connection( 404 callbacks, 405 db_callbacks, 406 0, /* child_process_id */ 407 transaction_id, 408 IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION); 409 factory()->Open(ASCIIToUTF16("db"), 410 connection, 411 NULL /* request_context */, 412 origin, 413 temp_directory.path()); 414 415 EXPECT_TRUE(callbacks->connection()); 416 EXPECT_TRUE(factory()->IsBackingStoreOpen(origin)); 417 EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin)); 418 419 callbacks->connection()->Close(); 420 421 EXPECT_TRUE(factory()->IsBackingStoreOpen(origin)); 422 EXPECT_TRUE(factory()->IsBackingStorePendingClose(origin)); 423 424 factory()->ForceClose(origin); 425 426 EXPECT_FALSE(factory()->IsBackingStoreOpen(origin)); 427 EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin)); 428 429 // Ensure it is safe if the store is not open. 430 factory()->ForceClose(origin); 431} 432 433class UpgradeNeededCallbacks : public MockIndexedDBCallbacks { 434 public: 435 UpgradeNeededCallbacks() {} 436 437 virtual void OnSuccess(scoped_ptr<IndexedDBConnection> connection, 438 const IndexedDBDatabaseMetadata& metadata) OVERRIDE { 439 EXPECT_TRUE(connection_.get()); 440 EXPECT_FALSE(connection.get()); 441 } 442 443 virtual void OnUpgradeNeeded( 444 int64 old_version, 445 scoped_ptr<IndexedDBConnection> connection, 446 const content::IndexedDBDatabaseMetadata& metadata) OVERRIDE { 447 connection_ = connection.Pass(); 448 } 449 450 protected: 451 virtual ~UpgradeNeededCallbacks() {} 452 453 private: 454 DISALLOW_COPY_AND_ASSIGN(UpgradeNeededCallbacks); 455}; 456 457class ErrorCallbacks : public MockIndexedDBCallbacks { 458 public: 459 ErrorCallbacks() : MockIndexedDBCallbacks(false), saw_error_(false) {} 460 461 virtual void OnError(const IndexedDBDatabaseError& error) OVERRIDE { 462 saw_error_= true; 463 } 464 bool saw_error() const { return saw_error_; } 465 466 private: 467 virtual ~ErrorCallbacks() {} 468 bool saw_error_; 469 470 DISALLOW_COPY_AND_ASSIGN(ErrorCallbacks); 471}; 472 473TEST_F(IndexedDBFactoryTest, DatabaseFailedOpen) { 474 GURL origin("http://localhost:81"); 475 476 base::ScopedTempDir temp_directory; 477 ASSERT_TRUE(temp_directory.CreateUniqueTempDir()); 478 479 const base::string16 db_name(ASCIIToUTF16("db")); 480 const int64 db_version = 2; 481 const int64 transaction_id = 1; 482 scoped_refptr<IndexedDBDatabaseCallbacks> db_callbacks( 483 new MockIndexedDBDatabaseCallbacks()); 484 485 // Open at version 2, then close. 486 { 487 scoped_refptr<MockIndexedDBCallbacks> callbacks( 488 new UpgradeNeededCallbacks()); 489 IndexedDBPendingConnection connection(callbacks, 490 db_callbacks, 491 0, /* child_process_id */ 492 transaction_id, 493 db_version); 494 factory()->Open(db_name, 495 connection, 496 NULL /* request_context */, 497 origin, 498 temp_directory.path()); 499 EXPECT_TRUE(factory()->IsDatabaseOpen(origin, db_name)); 500 501 // Pump the message loop so the upgrade transaction can run. 502 base::MessageLoop::current()->RunUntilIdle(); 503 EXPECT_TRUE(callbacks->connection()); 504 callbacks->connection()->database()->Commit(transaction_id); 505 506 callbacks->connection()->Close(); 507 EXPECT_FALSE(factory()->IsDatabaseOpen(origin, db_name)); 508 } 509 510 // Open at version < 2, which will fail; ensure factory doesn't retain 511 // the database object. 512 { 513 scoped_refptr<ErrorCallbacks> callbacks(new ErrorCallbacks()); 514 IndexedDBPendingConnection connection(callbacks, 515 db_callbacks, 516 0, /* child_process_id */ 517 transaction_id, 518 db_version - 1); 519 factory()->Open(db_name, 520 connection, 521 NULL /* request_context */, 522 origin, 523 temp_directory.path()); 524 EXPECT_TRUE(callbacks->saw_error()); 525 EXPECT_FALSE(factory()->IsDatabaseOpen(origin, db_name)); 526 } 527 528 // Terminate all pending-close timers. 529 factory()->ForceClose(origin); 530} 531 532} // namespace content 533