Home | History | Annotate | Download | only in indexed_db
      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 
     22 using base::ASCIIToUTF16;
     23 
     24 namespace content {
     25 
     26 namespace {
     27 
     28 class 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 
     69 class 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 
     96 TEST_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 
    123 TEST_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 
    151 TEST_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 
    181 TEST_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 
    201 class 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 
    224 class 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 
    241 TEST_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 
    265 TEST_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 
    298 TEST_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 
    342 TEST_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 
    369 TEST_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 
    393 TEST_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 
    433 class 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 
    457 class 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 
    473 TEST_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