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_impl.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 IndexedDBFactoryImpl
{
31 explicit MockIDBFactory(IndexedDBContextImpl
* context
)
32 : IndexedDBFactoryImpl(context
) {}
33 scoped_refptr
<IndexedDBBackingStore
> TestOpenBackingStore(
35 const base::FilePath
& data_directory
) {
36 blink::WebIDBDataLoss data_loss
=
37 blink::WebIDBDataLossNone
;
38 std::string data_loss_message
;
41 scoped_refptr
<IndexedDBBackingStore
> backing_store
=
42 OpenBackingStore(origin
,
44 NULL
/* request_context */,
49 EXPECT_EQ(blink::WebIDBDataLossNone
, data_loss
);
53 void TestCloseBackingStore(IndexedDBBackingStore
* backing_store
) {
54 CloseBackingStore(backing_store
->origin_url());
57 void TestReleaseBackingStore(IndexedDBBackingStore
* backing_store
,
59 ReleaseBackingStore(backing_store
->origin_url(), immediate
);
63 virtual ~MockIDBFactory() {}
65 DISALLOW_COPY_AND_ASSIGN(MockIDBFactory
);
70 class IndexedDBFactoryTest
: public testing::Test
{
72 IndexedDBFactoryTest() {
73 task_runner_
= new base::TestSimpleTaskRunner();
74 context_
= new IndexedDBContextImpl(base::FilePath(),
75 NULL
/* special_storage_policy */,
76 NULL
/* quota_manager_proxy */,
78 idb_factory_
= new MockIDBFactory(context_
.get());
82 // For timers to post events.
83 base::MessageLoop loop_
;
85 MockIDBFactory
* factory() const { return idb_factory_
.get(); }
86 void clear_factory() { idb_factory_
= NULL
; }
87 IndexedDBContextImpl
* context() const { return context_
.get(); }
90 scoped_refptr
<base::TestSimpleTaskRunner
> task_runner_
;
91 scoped_refptr
<IndexedDBContextImpl
> context_
;
92 scoped_refptr
<MockIDBFactory
> idb_factory_
;
94 DISALLOW_COPY_AND_ASSIGN(IndexedDBFactoryTest
);
97 TEST_F(IndexedDBFactoryTest
, BackingStoreLifetime
) {
98 GURL
origin1("http://localhost:81");
99 GURL
origin2("http://localhost:82");
101 base::ScopedTempDir temp_directory
;
102 ASSERT_TRUE(temp_directory
.CreateUniqueTempDir());
103 scoped_refptr
<IndexedDBBackingStore
> disk_store1
=
104 factory()->TestOpenBackingStore(origin1
, temp_directory
.path());
106 scoped_refptr
<IndexedDBBackingStore
> disk_store2
=
107 factory()->TestOpenBackingStore(origin1
, temp_directory
.path());
108 EXPECT_EQ(disk_store1
.get(), disk_store2
.get());
110 scoped_refptr
<IndexedDBBackingStore
> disk_store3
=
111 factory()->TestOpenBackingStore(origin2
, temp_directory
.path());
113 factory()->TestCloseBackingStore(disk_store1
.get());
114 factory()->TestCloseBackingStore(disk_store3
.get());
116 EXPECT_FALSE(disk_store1
->HasOneRef());
117 EXPECT_FALSE(disk_store2
->HasOneRef());
118 EXPECT_TRUE(disk_store3
->HasOneRef());
121 EXPECT_TRUE(disk_store1
->HasOneRef());
124 TEST_F(IndexedDBFactoryTest
, BackingStoreLazyClose
) {
125 GURL
origin("http://localhost:81");
127 base::ScopedTempDir temp_directory
;
128 ASSERT_TRUE(temp_directory
.CreateUniqueTempDir());
129 scoped_refptr
<IndexedDBBackingStore
> store
=
130 factory()->TestOpenBackingStore(origin
, temp_directory
.path());
132 // Give up the local refptr so that the factory has the only
133 // outstanding reference.
134 IndexedDBBackingStore
* store_ptr
= store
.get();
136 EXPECT_FALSE(store_ptr
->close_timer()->IsRunning());
137 factory()->TestReleaseBackingStore(store_ptr
, false);
138 EXPECT_TRUE(store_ptr
->close_timer()->IsRunning());
140 factory()->TestOpenBackingStore(origin
, temp_directory
.path());
141 EXPECT_FALSE(store_ptr
->close_timer()->IsRunning());
142 factory()->TestReleaseBackingStore(store_ptr
, false);
143 EXPECT_TRUE(store_ptr
->close_timer()->IsRunning());
145 // Take back a ref ptr and ensure that the actual close
146 // stops a running timer.
148 factory()->TestCloseBackingStore(store_ptr
);
149 EXPECT_FALSE(store_ptr
->close_timer()->IsRunning());
152 TEST_F(IndexedDBFactoryTest
, MemoryBackingStoreLifetime
) {
153 GURL
origin1("http://localhost:81");
154 GURL
origin2("http://localhost:82");
156 scoped_refptr
<IndexedDBBackingStore
> mem_store1
=
157 factory()->TestOpenBackingStore(origin1
, base::FilePath());
159 scoped_refptr
<IndexedDBBackingStore
> mem_store2
=
160 factory()->TestOpenBackingStore(origin1
, base::FilePath());
161 EXPECT_EQ(mem_store1
.get(), mem_store2
.get());
163 scoped_refptr
<IndexedDBBackingStore
> mem_store3
=
164 factory()->TestOpenBackingStore(origin2
, base::FilePath());
166 factory()->TestCloseBackingStore(mem_store1
.get());
167 factory()->TestCloseBackingStore(mem_store3
.get());
169 EXPECT_FALSE(mem_store1
->HasOneRef());
170 EXPECT_FALSE(mem_store2
->HasOneRef());
171 EXPECT_FALSE(mem_store3
->HasOneRef());
174 EXPECT_FALSE(mem_store1
->HasOneRef()); // mem_store1 and 2
175 EXPECT_FALSE(mem_store2
->HasOneRef()); // mem_store1 and 2
176 EXPECT_TRUE(mem_store3
->HasOneRef());
179 EXPECT_TRUE(mem_store1
->HasOneRef());
182 TEST_F(IndexedDBFactoryTest
, RejectLongOrigins
) {
183 base::ScopedTempDir temp_directory
;
184 ASSERT_TRUE(temp_directory
.CreateUniqueTempDir());
185 const base::FilePath base_path
= temp_directory
.path();
187 int limit
= base::GetMaximumPathComponentLength(base_path
);
190 std::string
origin(limit
+ 1, 'x');
191 GURL
too_long_origin("http://" + origin
+ ":81/");
192 scoped_refptr
<IndexedDBBackingStore
> diskStore1
=
193 factory()->TestOpenBackingStore(too_long_origin
, base_path
);
194 EXPECT_FALSE(diskStore1
.get());
196 GURL
ok_origin("http://someorigin.com:82/");
197 scoped_refptr
<IndexedDBBackingStore
> diskStore2
=
198 factory()->TestOpenBackingStore(ok_origin
, base_path
);
199 EXPECT_TRUE(diskStore2
.get());
202 class DiskFullFactory
: public IndexedDBFactoryImpl
{
204 explicit DiskFullFactory(IndexedDBContextImpl
* context
)
205 : IndexedDBFactoryImpl(context
) {}
208 virtual ~DiskFullFactory() {}
209 virtual scoped_refptr
<IndexedDBBackingStore
> OpenBackingStore(
210 const GURL
& origin_url
,
211 const base::FilePath
& data_directory
,
212 net::URLRequestContext
* request_context
,
213 blink::WebIDBDataLoss
* data_loss
,
214 std::string
* data_loss_message
,
216 leveldb::Status
* s
) OVERRIDE
{
218 *s
= leveldb::Status::IOError("Disk is full");
219 return scoped_refptr
<IndexedDBBackingStore
>();
222 DISALLOW_COPY_AND_ASSIGN(DiskFullFactory
);
225 class LookingForQuotaErrorMockCallbacks
: public IndexedDBCallbacks
{
227 LookingForQuotaErrorMockCallbacks()
228 : IndexedDBCallbacks(NULL
, 0, 0), error_called_(false) {}
229 virtual void OnError(const IndexedDBDatabaseError
& error
) OVERRIDE
{
230 error_called_
= true;
231 EXPECT_EQ(blink::WebIDBDatabaseExceptionQuotaError
, error
.code());
233 bool error_called() const { return error_called_
; }
236 virtual ~LookingForQuotaErrorMockCallbacks() {}
239 DISALLOW_COPY_AND_ASSIGN(LookingForQuotaErrorMockCallbacks
);
242 TEST_F(IndexedDBFactoryTest
, QuotaErrorOnDiskFull
) {
243 const GURL
origin("http://localhost:81");
244 base::ScopedTempDir temp_directory
;
245 ASSERT_TRUE(temp_directory
.CreateUniqueTempDir());
247 scoped_refptr
<DiskFullFactory
> factory
= new DiskFullFactory(context());
248 scoped_refptr
<LookingForQuotaErrorMockCallbacks
> callbacks
=
249 new LookingForQuotaErrorMockCallbacks
;
250 scoped_refptr
<IndexedDBDatabaseCallbacks
> dummy_database_callbacks
=
251 new IndexedDBDatabaseCallbacks(NULL
, 0, 0);
252 const base::string16
name(ASCIIToUTF16("name"));
253 IndexedDBPendingConnection
connection(callbacks
,
254 dummy_database_callbacks
,
255 0, /* child_process_id */
256 2, /* transaction_id */
260 NULL
/* request_context */,
262 temp_directory
.path());
263 EXPECT_TRUE(callbacks
->error_called());
266 TEST_F(IndexedDBFactoryTest
, BackingStoreReleasedOnForcedClose
) {
267 GURL
origin("http://localhost:81");
269 base::ScopedTempDir temp_directory
;
270 ASSERT_TRUE(temp_directory
.CreateUniqueTempDir());
272 scoped_refptr
<MockIndexedDBCallbacks
> callbacks(new MockIndexedDBCallbacks());
273 scoped_refptr
<MockIndexedDBDatabaseCallbacks
> db_callbacks(
274 new MockIndexedDBDatabaseCallbacks());
275 const int64 transaction_id
= 1;
276 IndexedDBPendingConnection
connection(
279 0, /* child_process_id */
281 IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
);
282 factory()->Open(ASCIIToUTF16("db"),
284 NULL
/* request_context */,
286 temp_directory
.path());
288 EXPECT_TRUE(callbacks
->connection());
290 EXPECT_TRUE(factory()->IsBackingStoreOpen(origin
));
291 EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin
));
293 callbacks
->connection()->ForceClose();
295 EXPECT_FALSE(factory()->IsBackingStoreOpen(origin
));
296 EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin
));
299 TEST_F(IndexedDBFactoryTest
, BackingStoreReleaseDelayedOnClose
) {
300 GURL
origin("http://localhost:81");
302 base::ScopedTempDir temp_directory
;
303 ASSERT_TRUE(temp_directory
.CreateUniqueTempDir());
305 scoped_refptr
<MockIndexedDBCallbacks
> callbacks(new MockIndexedDBCallbacks());
306 scoped_refptr
<MockIndexedDBDatabaseCallbacks
> db_callbacks(
307 new MockIndexedDBDatabaseCallbacks());
308 const int64 transaction_id
= 1;
309 IndexedDBPendingConnection
connection(
312 0, /* child_process_id */
314 IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
);
315 factory()->Open(ASCIIToUTF16("db"),
317 NULL
/* request_context */,
319 temp_directory
.path());
321 EXPECT_TRUE(callbacks
->connection());
322 IndexedDBBackingStore
* store
=
323 callbacks
->connection()->database()->backing_store();
324 EXPECT_FALSE(store
->HasOneRef()); // Factory and database.
326 EXPECT_TRUE(factory()->IsBackingStoreOpen(origin
));
327 callbacks
->connection()->Close();
328 EXPECT_TRUE(store
->HasOneRef()); // Factory.
329 EXPECT_TRUE(factory()->IsBackingStoreOpen(origin
));
330 EXPECT_TRUE(factory()->IsBackingStorePendingClose(origin
));
331 EXPECT_TRUE(store
->close_timer()->IsRunning());
333 // Take a ref so it won't be destroyed out from under the test.
334 scoped_refptr
<IndexedDBBackingStore
> store_ref
= store
;
335 // Now simulate shutdown, which should stop the timer.
336 factory()->ContextDestroyed();
337 EXPECT_TRUE(store
->HasOneRef()); // Local.
338 EXPECT_FALSE(store
->close_timer()->IsRunning());
339 EXPECT_FALSE(factory()->IsBackingStoreOpen(origin
));
340 EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin
));
343 TEST_F(IndexedDBFactoryTest
, DeleteDatabaseClosesBackingStore
) {
344 GURL
origin("http://localhost:81");
346 base::ScopedTempDir temp_directory
;
347 ASSERT_TRUE(temp_directory
.CreateUniqueTempDir());
349 EXPECT_FALSE(factory()->IsBackingStoreOpen(origin
));
351 const bool expect_connection
= false;
352 scoped_refptr
<MockIndexedDBCallbacks
> callbacks(
353 new MockIndexedDBCallbacks(expect_connection
));
354 factory()->DeleteDatabase(ASCIIToUTF16("db"),
355 NULL
/* request_context */,
358 temp_directory
.path());
360 EXPECT_TRUE(factory()->IsBackingStoreOpen(origin
));
361 EXPECT_TRUE(factory()->IsBackingStorePendingClose(origin
));
363 // Now simulate shutdown, which should stop the timer.
364 factory()->ContextDestroyed();
366 EXPECT_FALSE(factory()->IsBackingStoreOpen(origin
));
367 EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin
));
370 TEST_F(IndexedDBFactoryTest
, GetDatabaseNamesClosesBackingStore
) {
371 GURL
origin("http://localhost:81");
373 base::ScopedTempDir temp_directory
;
374 ASSERT_TRUE(temp_directory
.CreateUniqueTempDir());
376 EXPECT_FALSE(factory()->IsBackingStoreOpen(origin
));
378 const bool expect_connection
= false;
379 scoped_refptr
<MockIndexedDBCallbacks
> callbacks(
380 new MockIndexedDBCallbacks(expect_connection
));
381 factory()->GetDatabaseNames(
382 callbacks
, origin
, temp_directory
.path(), NULL
/* request_context */);
384 EXPECT_TRUE(factory()->IsBackingStoreOpen(origin
));
385 EXPECT_TRUE(factory()->IsBackingStorePendingClose(origin
));
387 // Now simulate shutdown, which should stop the timer.
388 factory()->ContextDestroyed();
390 EXPECT_FALSE(factory()->IsBackingStoreOpen(origin
));
391 EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin
));
394 TEST_F(IndexedDBFactoryTest
, ForceCloseReleasesBackingStore
) {
395 GURL
origin("http://localhost:81");
397 base::ScopedTempDir temp_directory
;
398 ASSERT_TRUE(temp_directory
.CreateUniqueTempDir());
400 scoped_refptr
<MockIndexedDBCallbacks
> callbacks(new MockIndexedDBCallbacks());
401 scoped_refptr
<MockIndexedDBDatabaseCallbacks
> db_callbacks(
402 new MockIndexedDBDatabaseCallbacks());
403 const int64 transaction_id
= 1;
404 IndexedDBPendingConnection
connection(
407 0, /* child_process_id */
409 IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
);
410 factory()->Open(ASCIIToUTF16("db"),
412 NULL
/* request_context */,
414 temp_directory
.path());
416 EXPECT_TRUE(callbacks
->connection());
417 EXPECT_TRUE(factory()->IsBackingStoreOpen(origin
));
418 EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin
));
420 callbacks
->connection()->Close();
422 EXPECT_TRUE(factory()->IsBackingStoreOpen(origin
));
423 EXPECT_TRUE(factory()->IsBackingStorePendingClose(origin
));
425 factory()->ForceClose(origin
);
427 EXPECT_FALSE(factory()->IsBackingStoreOpen(origin
));
428 EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin
));
430 // Ensure it is safe if the store is not open.
431 factory()->ForceClose(origin
);
434 class UpgradeNeededCallbacks
: public MockIndexedDBCallbacks
{
436 UpgradeNeededCallbacks() {}
438 virtual void OnSuccess(scoped_ptr
<IndexedDBConnection
> connection
,
439 const IndexedDBDatabaseMetadata
& metadata
) OVERRIDE
{
440 EXPECT_TRUE(connection_
.get());
441 EXPECT_FALSE(connection
.get());
444 virtual void OnUpgradeNeeded(
446 scoped_ptr
<IndexedDBConnection
> connection
,
447 const content::IndexedDBDatabaseMetadata
& metadata
) OVERRIDE
{
448 connection_
= connection
.Pass();
452 virtual ~UpgradeNeededCallbacks() {}
455 DISALLOW_COPY_AND_ASSIGN(UpgradeNeededCallbacks
);
458 class ErrorCallbacks
: public MockIndexedDBCallbacks
{
460 ErrorCallbacks() : MockIndexedDBCallbacks(false), saw_error_(false) {}
462 virtual void OnError(const IndexedDBDatabaseError
& error
) OVERRIDE
{
465 bool saw_error() const { return saw_error_
; }
468 virtual ~ErrorCallbacks() {}
471 DISALLOW_COPY_AND_ASSIGN(ErrorCallbacks
);
474 TEST_F(IndexedDBFactoryTest
, DatabaseFailedOpen
) {
475 GURL
origin("http://localhost:81");
477 base::ScopedTempDir temp_directory
;
478 ASSERT_TRUE(temp_directory
.CreateUniqueTempDir());
480 const base::string16
db_name(ASCIIToUTF16("db"));
481 const int64 db_version
= 2;
482 const int64 transaction_id
= 1;
483 scoped_refptr
<IndexedDBDatabaseCallbacks
> db_callbacks(
484 new MockIndexedDBDatabaseCallbacks());
486 // Open at version 2, then close.
488 scoped_refptr
<MockIndexedDBCallbacks
> callbacks(
489 new UpgradeNeededCallbacks());
490 IndexedDBPendingConnection
connection(callbacks
,
492 0, /* child_process_id */
495 factory()->Open(db_name
,
497 NULL
/* request_context */,
499 temp_directory
.path());
500 EXPECT_TRUE(factory()->IsDatabaseOpen(origin
, db_name
));
502 // Pump the message loop so the upgrade transaction can run.
503 base::MessageLoop::current()->RunUntilIdle();
504 EXPECT_TRUE(callbacks
->connection());
505 callbacks
->connection()->database()->Commit(transaction_id
);
507 callbacks
->connection()->Close();
508 EXPECT_FALSE(factory()->IsDatabaseOpen(origin
, db_name
));
511 // Open at version < 2, which will fail; ensure factory doesn't retain
512 // the database object.
514 scoped_refptr
<ErrorCallbacks
> callbacks(new ErrorCallbacks());
515 IndexedDBPendingConnection
connection(callbacks
,
517 0, /* child_process_id */
520 factory()->Open(db_name
,
522 NULL
/* request_context */,
524 temp_directory
.path());
525 EXPECT_TRUE(callbacks
->saw_error());
526 EXPECT_FALSE(factory()->IsDatabaseOpen(origin
, db_name
));
529 // Terminate all pending-close timers.
530 factory()->ForceClose(origin
);
533 } // namespace content