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.
5 #include "content/browser/indexed_db/indexed_db_factory.h"
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"
21 #include "webkit/common/database/database_identifier.h"
23 using base::ASCIIToUTF16
;
29 class MockIDBFactory
: public IndexedDBFactory
{
31 MockIDBFactory(IndexedDBContextImpl
* context
) : IndexedDBFactory(context
) {}
32 scoped_refptr
<IndexedDBBackingStore
> TestOpenBackingStore(
34 const base::FilePath
& data_directory
) {
35 blink::WebIDBDataLoss data_loss
=
36 blink::WebIDBDataLossNone
;
37 std::string data_loss_message
;
39 scoped_refptr
<IndexedDBBackingStore
> backing_store
=
40 OpenBackingStore(origin
,
42 NULL
/* request_context */,
46 EXPECT_EQ(blink::WebIDBDataLossNone
, data_loss
);
50 void TestCloseBackingStore(IndexedDBBackingStore
* backing_store
) {
51 CloseBackingStore(backing_store
->origin_url());
54 void TestReleaseBackingStore(IndexedDBBackingStore
* backing_store
,
56 ReleaseBackingStore(backing_store
->origin_url(), immediate
);
60 virtual ~MockIDBFactory() {}
65 class IndexedDBFactoryTest
: public testing::Test
{
67 IndexedDBFactoryTest() {
68 task_runner_
= new base::TestSimpleTaskRunner();
69 context_
= new IndexedDBContextImpl(base::FilePath(),
70 NULL
/* special_storage_policy */,
71 NULL
/* quota_manager_proxy */,
73 idb_factory_
= new MockIDBFactory(context_
.get());
77 // For timers to post events.
78 base::MessageLoop loop_
;
80 MockIDBFactory
* factory() const { return idb_factory_
.get(); }
81 void clear_factory() { idb_factory_
= NULL
; }
82 IndexedDBContextImpl
* context() const { return context_
.get(); }
85 scoped_refptr
<base::TestSimpleTaskRunner
> task_runner_
;
86 scoped_refptr
<IndexedDBContextImpl
> context_
;
87 scoped_refptr
<MockIDBFactory
> idb_factory_
;
89 DISALLOW_COPY_AND_ASSIGN(IndexedDBFactoryTest
);
92 TEST_F(IndexedDBFactoryTest
, BackingStoreLifetime
) {
93 GURL
origin1("http://localhost:81");
94 GURL
origin2("http://localhost:82");
96 base::ScopedTempDir temp_directory
;
97 ASSERT_TRUE(temp_directory
.CreateUniqueTempDir());
98 scoped_refptr
<IndexedDBBackingStore
> disk_store1
=
99 factory()->TestOpenBackingStore(origin1
, temp_directory
.path());
101 scoped_refptr
<IndexedDBBackingStore
> disk_store2
=
102 factory()->TestOpenBackingStore(origin1
, temp_directory
.path());
103 EXPECT_EQ(disk_store1
.get(), disk_store2
.get());
105 scoped_refptr
<IndexedDBBackingStore
> disk_store3
=
106 factory()->TestOpenBackingStore(origin2
, temp_directory
.path());
108 factory()->TestCloseBackingStore(disk_store1
);
109 factory()->TestCloseBackingStore(disk_store3
);
111 EXPECT_FALSE(disk_store1
->HasOneRef());
112 EXPECT_FALSE(disk_store2
->HasOneRef());
113 EXPECT_TRUE(disk_store3
->HasOneRef());
116 EXPECT_TRUE(disk_store1
->HasOneRef());
119 TEST_F(IndexedDBFactoryTest
, BackingStoreLazyClose
) {
120 GURL
origin("http://localhost:81");
122 base::ScopedTempDir temp_directory
;
123 ASSERT_TRUE(temp_directory
.CreateUniqueTempDir());
124 scoped_refptr
<IndexedDBBackingStore
> store
=
125 factory()->TestOpenBackingStore(origin
, temp_directory
.path());
127 // Give up the local refptr so that the factory has the only
128 // outstanding reference.
129 IndexedDBBackingStore
* store_ptr
= store
.get();
131 EXPECT_FALSE(store_ptr
->close_timer()->IsRunning());
132 factory()->TestReleaseBackingStore(store_ptr
, false);
133 EXPECT_TRUE(store_ptr
->close_timer()->IsRunning());
135 factory()->TestOpenBackingStore(origin
, temp_directory
.path());
136 EXPECT_FALSE(store_ptr
->close_timer()->IsRunning());
137 factory()->TestReleaseBackingStore(store_ptr
, false);
138 EXPECT_TRUE(store_ptr
->close_timer()->IsRunning());
140 // Take back a ref ptr and ensure that the actual close
141 // stops a running timer.
143 factory()->TestCloseBackingStore(store_ptr
);
144 EXPECT_FALSE(store_ptr
->close_timer()->IsRunning());
147 TEST_F(IndexedDBFactoryTest
, MemoryBackingStoreLifetime
) {
148 GURL
origin1("http://localhost:81");
149 GURL
origin2("http://localhost:82");
151 scoped_refptr
<IndexedDBBackingStore
> mem_store1
=
152 factory()->TestOpenBackingStore(origin1
, base::FilePath());
154 scoped_refptr
<IndexedDBBackingStore
> mem_store2
=
155 factory()->TestOpenBackingStore(origin1
, base::FilePath());
156 EXPECT_EQ(mem_store1
.get(), mem_store2
.get());
158 scoped_refptr
<IndexedDBBackingStore
> mem_store3
=
159 factory()->TestOpenBackingStore(origin2
, base::FilePath());
161 factory()->TestCloseBackingStore(mem_store1
);
162 factory()->TestCloseBackingStore(mem_store3
);
164 EXPECT_FALSE(mem_store1
->HasOneRef());
165 EXPECT_FALSE(mem_store2
->HasOneRef());
166 EXPECT_FALSE(mem_store3
->HasOneRef());
169 EXPECT_FALSE(mem_store1
->HasOneRef()); // mem_store1 and 2
170 EXPECT_FALSE(mem_store2
->HasOneRef()); // mem_store1 and 2
171 EXPECT_TRUE(mem_store3
->HasOneRef());
174 EXPECT_TRUE(mem_store1
->HasOneRef());
177 TEST_F(IndexedDBFactoryTest
, RejectLongOrigins
) {
178 base::ScopedTempDir temp_directory
;
179 ASSERT_TRUE(temp_directory
.CreateUniqueTempDir());
180 const base::FilePath base_path
= temp_directory
.path();
182 int limit
= base::GetMaximumPathComponentLength(base_path
);
185 std::string
origin(limit
+ 1, 'x');
186 GURL
too_long_origin("http://" + origin
+ ":81/");
187 scoped_refptr
<IndexedDBBackingStore
> diskStore1
=
188 factory()->TestOpenBackingStore(too_long_origin
, base_path
);
189 EXPECT_FALSE(diskStore1
);
191 GURL
ok_origin("http://someorigin.com:82/");
192 scoped_refptr
<IndexedDBBackingStore
> diskStore2
=
193 factory()->TestOpenBackingStore(ok_origin
, base_path
);
194 EXPECT_TRUE(diskStore2
);
197 class DiskFullFactory
: public IndexedDBFactory
{
199 DiskFullFactory(IndexedDBContextImpl
* context
) : IndexedDBFactory(context
) {}
202 virtual ~DiskFullFactory() {}
203 virtual scoped_refptr
<IndexedDBBackingStore
> OpenBackingStore(
204 const GURL
& origin_url
,
205 const base::FilePath
& data_directory
,
206 net::URLRequestContext
* request_context
,
207 blink::WebIDBDataLoss
* data_loss
,
208 std::string
* data_loss_message
,
209 bool* disk_full
) OVERRIDE
{
211 return scoped_refptr
<IndexedDBBackingStore
>();
215 class LookingForQuotaErrorMockCallbacks
: public IndexedDBCallbacks
{
217 LookingForQuotaErrorMockCallbacks()
218 : IndexedDBCallbacks(NULL
, 0, 0), error_called_(false) {}
219 virtual void OnError(const IndexedDBDatabaseError
& error
) OVERRIDE
{
220 error_called_
= true;
221 EXPECT_EQ(blink::WebIDBDatabaseExceptionQuotaError
, error
.code());
223 bool error_called() const { return error_called_
; }
226 virtual ~LookingForQuotaErrorMockCallbacks() {}
230 TEST_F(IndexedDBFactoryTest
, QuotaErrorOnDiskFull
) {
231 const GURL
origin("http://localhost:81");
232 base::ScopedTempDir temp_directory
;
233 ASSERT_TRUE(temp_directory
.CreateUniqueTempDir());
235 scoped_refptr
<DiskFullFactory
> factory
= new DiskFullFactory(context());
236 scoped_refptr
<LookingForQuotaErrorMockCallbacks
> callbacks
=
237 new LookingForQuotaErrorMockCallbacks
;
238 scoped_refptr
<IndexedDBDatabaseCallbacks
> dummy_database_callbacks
=
239 new IndexedDBDatabaseCallbacks(NULL
, 0, 0);
240 const base::string16
name(ASCIIToUTF16("name"));
241 IndexedDBPendingConnection
connection(callbacks
,
242 dummy_database_callbacks
,
243 0, /* child_process_id */
244 2, /* transaction_id */
248 NULL
/* request_context */,
250 temp_directory
.path());
251 EXPECT_TRUE(callbacks
->error_called());
254 TEST_F(IndexedDBFactoryTest
, BackingStoreReleasedOnForcedClose
) {
255 GURL
origin("http://localhost:81");
257 base::ScopedTempDir temp_directory
;
258 ASSERT_TRUE(temp_directory
.CreateUniqueTempDir());
260 scoped_refptr
<MockIndexedDBCallbacks
> callbacks(new MockIndexedDBCallbacks());
261 scoped_refptr
<MockIndexedDBDatabaseCallbacks
> db_callbacks(
262 new MockIndexedDBDatabaseCallbacks());
263 const int64 transaction_id
= 1;
264 IndexedDBPendingConnection
connection(
267 0, /* child_process_id */
269 IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
);
270 factory()->Open(ASCIIToUTF16("db"),
272 NULL
/* request_context */,
274 temp_directory
.path());
276 EXPECT_TRUE(callbacks
->connection());
278 EXPECT_TRUE(factory()->IsBackingStoreOpen(origin
));
279 EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin
));
281 callbacks
->connection()->ForceClose();
283 EXPECT_FALSE(factory()->IsBackingStoreOpen(origin
));
284 EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin
));
287 TEST_F(IndexedDBFactoryTest
, BackingStoreReleaseDelayedOnClose
) {
288 GURL
origin("http://localhost:81");
290 base::ScopedTempDir temp_directory
;
291 ASSERT_TRUE(temp_directory
.CreateUniqueTempDir());
293 scoped_refptr
<MockIndexedDBCallbacks
> callbacks(new MockIndexedDBCallbacks());
294 scoped_refptr
<MockIndexedDBDatabaseCallbacks
> db_callbacks(
295 new MockIndexedDBDatabaseCallbacks());
296 const int64 transaction_id
= 1;
297 IndexedDBPendingConnection
connection(
300 0, /* child_process_id */
302 IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
);
303 factory()->Open(ASCIIToUTF16("db"),
305 NULL
/* request_context */,
307 temp_directory
.path());
309 EXPECT_TRUE(callbacks
->connection());
310 IndexedDBBackingStore
* store
=
311 callbacks
->connection()->database()->backing_store();
312 EXPECT_FALSE(store
->HasOneRef()); // Factory and database.
314 EXPECT_TRUE(factory()->IsBackingStoreOpen(origin
));
315 callbacks
->connection()->Close();
316 EXPECT_TRUE(store
->HasOneRef()); // Factory.
317 EXPECT_TRUE(factory()->IsBackingStoreOpen(origin
));
318 EXPECT_TRUE(factory()->IsBackingStorePendingClose(origin
));
319 EXPECT_TRUE(store
->close_timer()->IsRunning());
321 // Take a ref so it won't be destroyed out from under the test.
322 scoped_refptr
<IndexedDBBackingStore
> store_ref
= store
;
323 // Now simulate shutdown, which should stop the timer.
324 factory()->ContextDestroyed();
325 EXPECT_TRUE(store
->HasOneRef()); // Local.
326 EXPECT_FALSE(store
->close_timer()->IsRunning());
327 EXPECT_FALSE(factory()->IsBackingStoreOpen(origin
));
328 EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin
));
331 TEST_F(IndexedDBFactoryTest
, DeleteDatabaseClosesBackingStore
) {
332 GURL
origin("http://localhost:81");
334 base::ScopedTempDir temp_directory
;
335 ASSERT_TRUE(temp_directory
.CreateUniqueTempDir());
337 EXPECT_FALSE(factory()->IsBackingStoreOpen(origin
));
339 const bool expect_connection
= false;
340 scoped_refptr
<MockIndexedDBCallbacks
> callbacks(
341 new MockIndexedDBCallbacks(expect_connection
));
342 factory()->DeleteDatabase(ASCIIToUTF16("db"),
343 NULL
/* request_context */,
346 temp_directory
.path());
348 EXPECT_TRUE(factory()->IsBackingStoreOpen(origin
));
349 EXPECT_TRUE(factory()->IsBackingStorePendingClose(origin
));
351 // Now simulate shutdown, which should stop the timer.
352 factory()->ContextDestroyed();
354 EXPECT_FALSE(factory()->IsBackingStoreOpen(origin
));
355 EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin
));
358 TEST_F(IndexedDBFactoryTest
, GetDatabaseNamesClosesBackingStore
) {
359 GURL
origin("http://localhost:81");
361 base::ScopedTempDir temp_directory
;
362 ASSERT_TRUE(temp_directory
.CreateUniqueTempDir());
364 EXPECT_FALSE(factory()->IsBackingStoreOpen(origin
));
366 const bool expect_connection
= false;
367 scoped_refptr
<MockIndexedDBCallbacks
> callbacks(
368 new MockIndexedDBCallbacks(expect_connection
));
369 factory()->GetDatabaseNames(
370 callbacks
, origin
, temp_directory
.path(), NULL
/* request_context */);
372 EXPECT_TRUE(factory()->IsBackingStoreOpen(origin
));
373 EXPECT_TRUE(factory()->IsBackingStorePendingClose(origin
));
375 // Now simulate shutdown, which should stop the timer.
376 factory()->ContextDestroyed();
378 EXPECT_FALSE(factory()->IsBackingStoreOpen(origin
));
379 EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin
));
382 TEST_F(IndexedDBFactoryTest
, ForceCloseReleasesBackingStore
) {
383 GURL
origin("http://localhost:81");
385 base::ScopedTempDir temp_directory
;
386 ASSERT_TRUE(temp_directory
.CreateUniqueTempDir());
388 scoped_refptr
<MockIndexedDBCallbacks
> callbacks(new MockIndexedDBCallbacks());
389 scoped_refptr
<MockIndexedDBDatabaseCallbacks
> db_callbacks(
390 new MockIndexedDBDatabaseCallbacks());
391 const int64 transaction_id
= 1;
392 IndexedDBPendingConnection
connection(
395 0, /* child_process_id */
397 IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
);
398 factory()->Open(ASCIIToUTF16("db"),
400 NULL
/* request_context */,
402 temp_directory
.path());
404 EXPECT_TRUE(callbacks
->connection());
405 EXPECT_TRUE(factory()->IsBackingStoreOpen(origin
));
406 EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin
));
408 callbacks
->connection()->Close();
410 EXPECT_TRUE(factory()->IsBackingStoreOpen(origin
));
411 EXPECT_TRUE(factory()->IsBackingStorePendingClose(origin
));
413 factory()->ForceClose(origin
);
415 EXPECT_FALSE(factory()->IsBackingStoreOpen(origin
));
416 EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin
));
418 // Ensure it is safe if the store is not open.
419 factory()->ForceClose(origin
);
422 class UpgradeNeededCallbacks
: public MockIndexedDBCallbacks
{
424 virtual void OnSuccess(scoped_ptr
<IndexedDBConnection
> connection
,
425 const IndexedDBDatabaseMetadata
& metadata
) OVERRIDE
{
426 EXPECT_TRUE(connection_
.get());
427 EXPECT_FALSE(connection
.get());
430 virtual void OnUpgradeNeeded(
432 scoped_ptr
<IndexedDBConnection
> connection
,
433 const content::IndexedDBDatabaseMetadata
& metadata
) OVERRIDE
{
434 connection_
= connection
.Pass();
438 virtual ~UpgradeNeededCallbacks() {}
441 class ErrorCallbacks
: public MockIndexedDBCallbacks
{
443 ErrorCallbacks() : MockIndexedDBCallbacks(false), saw_error_(false) {}
445 virtual void OnError(const IndexedDBDatabaseError
& error
) OVERRIDE
{
448 bool saw_error() const { return saw_error_
; }
451 virtual ~ErrorCallbacks() {}
455 TEST_F(IndexedDBFactoryTest
, DatabaseFailedOpen
) {
456 GURL
origin("http://localhost:81");
458 base::ScopedTempDir temp_directory
;
459 ASSERT_TRUE(temp_directory
.CreateUniqueTempDir());
461 const base::string16
db_name(ASCIIToUTF16("db"));
462 const int64 db_version
= 2;
463 const int64 transaction_id
= 1;
464 scoped_refptr
<IndexedDBDatabaseCallbacks
> db_callbacks(
465 new MockIndexedDBDatabaseCallbacks());
467 // Open at version 2, then close.
469 scoped_refptr
<MockIndexedDBCallbacks
> callbacks(
470 new UpgradeNeededCallbacks());
471 IndexedDBPendingConnection
connection(callbacks
,
473 0, /* child_process_id */
476 factory()->Open(db_name
,
478 NULL
/* request_context */,
480 temp_directory
.path());
481 EXPECT_TRUE(factory()->IsDatabaseOpen(origin
, db_name
));
483 // Pump the message loop so the upgrade transaction can run.
484 base::MessageLoop::current()->RunUntilIdle();
485 EXPECT_TRUE(callbacks
->connection());
486 callbacks
->connection()->database()->Commit(transaction_id
);
488 callbacks
->connection()->Close();
489 EXPECT_FALSE(factory()->IsDatabaseOpen(origin
, db_name
));
492 // Open at version < 2, which will fail; ensure factory doesn't retain
493 // the database object.
495 scoped_refptr
<ErrorCallbacks
> callbacks(new ErrorCallbacks());
496 IndexedDBPendingConnection
connection(callbacks
,
498 0, /* child_process_id */
501 factory()->Open(db_name
,
503 NULL
/* request_context */,
505 temp_directory
.path());
506 EXPECT_TRUE(callbacks
->saw_error());
507 EXPECT_FALSE(factory()->IsDatabaseOpen(origin
, db_name
));
510 // Terminate all pending-close timers.
511 factory()->ForceClose(origin
);
514 } // namespace content