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