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 <stdint.h>
6 #include <memory>
7 #include <utility>
8
9 #include "base/auto_reset.h"
10 #include "base/barrier_closure.h"
11 #include "base/files/file_util.h"
12 #include "base/files/scoped_temp_dir.h"
13 #include "base/macros.h"
14 #include "base/memory/scoped_refptr.h"
15 #include "base/run_loop.h"
16 #include "base/stl_util.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "base/test/bind.h"
19 #include "base/test/scoped_feature_list.h"
20 #include "base/test/simple_test_clock.h"
21 #include "base/test/task_environment.h"
22 #include "base/test/test_simple_task_runner.h"
23 #include "base/threading/sequenced_task_runner_handle.h"
24 #include "base/time/default_clock.h"
25 #include "components/services/storage/indexed_db/leveldb/fake_leveldb_factory.h"
26 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_database.h"
27 #include "components/services/storage/public/mojom/indexed_db_control.mojom-test-utils.h"
28 #include "content/browser/indexed_db/indexed_db_backing_store.h"
29 #include "content/browser/indexed_db/indexed_db_class_factory.h"
30 #include "content/browser/indexed_db/indexed_db_connection.h"
31 #include "content/browser/indexed_db/indexed_db_context_impl.h"
32 #include "content/browser/indexed_db/indexed_db_data_format_version.h"
33 #include "content/browser/indexed_db/indexed_db_factory_impl.h"
34 #include "content/browser/indexed_db/indexed_db_leveldb_env.h"
35 #include "content/browser/indexed_db/indexed_db_origin_state.h"
36 #include "content/browser/indexed_db/indexed_db_pre_close_task_queue.h"
37 #include "content/browser/indexed_db/indexed_db_transaction.h"
38 #include "content/browser/indexed_db/mock_indexed_db_callbacks.h"
39 #include "content/browser/indexed_db/mock_indexed_db_database_callbacks.h"
40 #include "mojo/public/cpp/bindings/associated_remote.h"
41 #include "storage/browser/test/mock_quota_manager_proxy.h"
42 #include "storage/browser/test/mock_special_storage_policy.h"
43 #include "testing/gtest/include/gtest/gtest.h"
44 #include "third_party/blink/public/common/indexeddb/web_idb_types.h"
45 #include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
46 #include "url/gurl.h"
47 #include "url/origin.h"
48
49 using base::ASCIIToUTF16;
50 using blink::IndexedDBDatabaseMetadata;
51 using url::Origin;
52
53 namespace content {
54
55 namespace {
56
CreateAndBindTransactionPlaceholder(base::WeakPtr<IndexedDBTransaction> transaction)57 void CreateAndBindTransactionPlaceholder(
58 base::WeakPtr<IndexedDBTransaction> transaction) {}
59
60 } // namespace
61
62 class IndexedDBFactoryTest : public testing::Test {
63 public:
IndexedDBFactoryTest()64 IndexedDBFactoryTest()
65 : task_environment_(std::make_unique<base::test::TaskEnvironment>()) {}
66
IndexedDBFactoryTest(std::unique_ptr<base::test::TaskEnvironment> task_environment)67 explicit IndexedDBFactoryTest(
68 std::unique_ptr<base::test::TaskEnvironment> task_environment)
69 : task_environment_(std::move(task_environment)) {}
70
SetUp()71 void SetUp() override {
72 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
73 quota_policy_ = base::MakeRefCounted<storage::MockSpecialStoragePolicy>();
74 quota_manager_ = base::MakeRefCounted<storage::MockQuotaManager>(
75 false /*is_incognito*/, temp_dir_.GetPath(),
76 base::ThreadTaskRunnerHandle::Get().get(), quota_policy_.get());
77
78 quota_manager_proxy_ = base::MakeRefCounted<storage::MockQuotaManagerProxy>(
79 quota_manager_.get(), base::ThreadTaskRunnerHandle::Get().get());
80 }
81
TearDown()82 void TearDown() override {
83 quota_manager_proxy_->SimulateQuotaManagerDestroyed();
84
85 if (context_ && !context_->IsInMemoryContext()) {
86 IndexedDBFactoryImpl* factory = context_->GetIDBFactory();
87
88 // Loop through all open origins, and force close them, and request the
89 // deletion of the leveldb state. Once the states are no longer around,
90 // delete all of the databases on disk.
91 auto open_factory_origins = factory->GetOpenOrigins();
92 for (auto origin : open_factory_origins) {
93 context_->ForceCloseSync(
94 origin,
95 storage::mojom::ForceCloseReason::FORCE_CLOSE_DELETE_ORIGIN);
96 }
97 // All leveldb databases are closed, and they can be deleted.
98 for (auto origin : context_->GetAllOrigins()) {
99 bool success = false;
100 storage::mojom::IndexedDBControlAsyncWaiter waiter(context_.get());
101 waiter.DeleteForOrigin(origin, &success);
102 EXPECT_TRUE(success);
103 }
104 }
105 if (temp_dir_.IsValid())
106 ASSERT_TRUE(temp_dir_.Delete());
107 IndexedDBClassFactory::Get()->SetLevelDBFactoryForTesting(nullptr);
108 quota_manager_.reset();
109 }
110
SetupContext()111 void SetupContext() {
112 context_ = base::MakeRefCounted<IndexedDBContextImpl>(
113 temp_dir_.GetPath(), quota_manager_proxy_.get(),
114 base::DefaultClock::GetInstance(),
115 /*blob_storage_context=*/mojo::NullRemote(),
116 /*native_file_system_context=*/mojo::NullRemote(),
117 base::SequencedTaskRunnerHandle::Get(),
118 base::SequencedTaskRunnerHandle::Get());
119 }
120
SetupInMemoryContext()121 void SetupInMemoryContext() {
122 context_ = base::MakeRefCounted<IndexedDBContextImpl>(
123 base::FilePath(), quota_manager_proxy_.get(),
124 base::DefaultClock::GetInstance(),
125 /*blob_storage_context=*/mojo::NullRemote(),
126 /*native_file_system_context=*/mojo::NullRemote(),
127 base::SequencedTaskRunnerHandle::Get(),
128 base::SequencedTaskRunnerHandle::Get());
129 }
130
SetupContextWithFactories(LevelDBFactory * factory,base::Clock * clock)131 void SetupContextWithFactories(LevelDBFactory* factory, base::Clock* clock) {
132 context_ = base::MakeRefCounted<IndexedDBContextImpl>(
133 temp_dir_.GetPath(), quota_manager_proxy_.get(), clock,
134 /*blob_storage_context=*/mojo::NullRemote(),
135 /*native_file_system_context=*/mojo::NullRemote(),
136 base::SequencedTaskRunnerHandle::Get(),
137 base::SequencedTaskRunnerHandle::Get());
138 if (factory)
139 IndexedDBClassFactory::Get()->SetLevelDBFactoryForTesting(factory);
140 }
141
142 // Runs through the upgrade flow to create a basic database connection. There
143 // is no actual data in the database.
144 std::tuple<std::unique_ptr<IndexedDBConnection>,
145 scoped_refptr<MockIndexedDBDatabaseCallbacks>>
CreateConnectionForDatatabase(const Origin & origin,const base::string16 & name)146 CreateConnectionForDatatabase(const Origin& origin,
147 const base::string16& name) {
148 auto callbacks = base::MakeRefCounted<MockIndexedDBCallbacks>();
149 auto db_callbacks = base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>();
150 const int64_t transaction_id = 1;
151 auto create_transaction_callback =
152 base::BindOnce(&CreateAndBindTransactionPlaceholder);
153 auto connection = std::make_unique<IndexedDBPendingConnection>(
154 callbacks, db_callbacks,
155 transaction_id, IndexedDBDatabaseMetadata::NO_VERSION,
156 std::move(create_transaction_callback));
157
158 // Do the first half of the upgrade, and request the upgrade from renderer.
159 {
160 base::RunLoop loop;
161 callbacks->CallOnUpgradeNeeded(
162 base::BindLambdaForTesting([&]() { loop.Quit(); }));
163 factory()->Open(name, std::move(connection), origin,
164 context()->data_path());
165 loop.Run();
166 }
167
168 EXPECT_TRUE(callbacks->upgrade_called());
169 EXPECT_TRUE(callbacks->connection());
170 EXPECT_TRUE(callbacks->connection()->database());
171 if (!callbacks->connection())
172 return {nullptr, nullptr};
173
174 // Finish the upgrade by committing the transaction.
175 {
176 base::RunLoop loop;
177 callbacks->CallOnDBSuccess(
178 base::BindLambdaForTesting([&]() { loop.Quit(); }));
179 callbacks->connection()
180 ->transactions()
181 .find(transaction_id)
182 ->second->SetCommitFlag();
183 loop.Run();
184 }
185 return {callbacks->TakeConnection(), db_callbacks};
186 }
187
RunPostedTasks()188 void RunPostedTasks() {
189 base::RunLoop loop;
190 base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE,
191 loop.QuitClosure());
192 loop.Run();
193 }
194
195 protected:
context() const196 IndexedDBContextImpl* context() const { return context_.get(); }
197
factory() const198 IndexedDBFactoryImpl* factory() const { return context_->GetIDBFactory(); }
199
task_environment() const200 base::test::TaskEnvironment* task_environment() const {
201 return task_environment_.get();
202 }
203
OriginStateFromHandle(IndexedDBOriginStateHandle & handle)204 IndexedDBOriginState* OriginStateFromHandle(
205 IndexedDBOriginStateHandle& handle) {
206 return handle.origin_state();
207 }
208
quota_manager()209 storage::MockQuotaManager* quota_manager() { return quota_manager_.get(); }
210
211 private:
212 std::unique_ptr<base::test::TaskEnvironment> task_environment_;
213
214 base::ScopedTempDir temp_dir_;
215 scoped_refptr<storage::MockSpecialStoragePolicy> quota_policy_;
216 scoped_refptr<storage::MockQuotaManager> quota_manager_;
217 scoped_refptr<storage::MockQuotaManagerProxy> quota_manager_proxy_;
218 scoped_refptr<IndexedDBContextImpl> context_;
219
220 DISALLOW_COPY_AND_ASSIGN(IndexedDBFactoryTest);
221 };
222
223 class IndexedDBFactoryTestWithMockTime : public IndexedDBFactoryTest {
224 public:
IndexedDBFactoryTestWithMockTime()225 IndexedDBFactoryTestWithMockTime()
226 : IndexedDBFactoryTest(std::make_unique<base::test::TaskEnvironment>(
227 base::test::TaskEnvironment::TimeSource::MOCK_TIME)) {}
228
229 private:
230 DISALLOW_COPY_AND_ASSIGN(IndexedDBFactoryTestWithMockTime);
231 };
232
TEST_F(IndexedDBFactoryTest,BasicFactoryCreationAndTearDown)233 TEST_F(IndexedDBFactoryTest, BasicFactoryCreationAndTearDown) {
234 SetupContext();
235
236 const Origin origin1 = Origin::Create(GURL("http://localhost:81"));
237 const Origin origin2 = Origin::Create(GURL("http://localhost:82"));
238
239 IndexedDBOriginStateHandle origin_state1_handle;
240 IndexedDBOriginStateHandle origin_state2_handle;
241 leveldb::Status s;
242
243 std::tie(origin_state1_handle, s, std::ignore, std::ignore, std::ignore) =
244 factory()->GetOrOpenOriginFactory(origin1, context()->data_path(),
245 /*create_if_missing=*/true);
246 EXPECT_TRUE(origin_state1_handle.IsHeld()) << s.ToString();
247 EXPECT_TRUE(s.ok()) << s.ToString();
248
249 std::tie(origin_state2_handle, s, std::ignore, std::ignore, std::ignore) =
250 factory()->GetOrOpenOriginFactory(origin2, context()->data_path(),
251 /*create_if_missing=*/true);
252 EXPECT_TRUE(origin_state2_handle.IsHeld()) << s.ToString();
253 EXPECT_TRUE(s.ok()) << s.ToString();
254
255 std::vector<storage::mojom::IndexedDBStorageUsageInfoPtr> origin_info;
256 storage::mojom::IndexedDBControlAsyncWaiter sync_control(context());
257 sync_control.GetUsage(&origin_info);
258
259 EXPECT_EQ(2ul, origin_info.size());
260 EXPECT_EQ(2ul, factory()->GetOpenOrigins().size());
261 }
262
TEST_F(IndexedDBFactoryTest,CloseSequenceStarts)263 TEST_F(IndexedDBFactoryTest, CloseSequenceStarts) {
264 SetupContext();
265
266 const Origin origin = Origin::Create(GURL("http://localhost:81"));
267
268 IndexedDBOriginStateHandle origin_state_handle;
269 leveldb::Status s;
270
271 std::tie(origin_state_handle, s, std::ignore, std::ignore, std::ignore) =
272 factory()->GetOrOpenOriginFactory(origin, context()->data_path(),
273 /*create_if_missing=*/true);
274 EXPECT_TRUE(origin_state_handle.IsHeld()) << s.ToString();
275 origin_state_handle.Release();
276
277 EXPECT_TRUE(factory()->GetOriginFactory(origin));
278 EXPECT_TRUE(factory()->GetOriginFactory(origin)->IsClosing());
279
280 factory()->ForceClose(origin, false);
281 RunPostedTasks();
282 EXPECT_FALSE(factory()->GetOriginFactory(origin));
283 }
284
TEST_F(IndexedDBFactoryTest,ImmediateClose)285 TEST_F(IndexedDBFactoryTest, ImmediateClose) {
286 base::CommandLine::ForCurrentProcess()->AppendSwitch(
287 kIDBCloseImmediatelySwitch);
288 SetupContext();
289
290 const Origin origin = Origin::Create(GURL("http://localhost:81"));
291
292 IndexedDBOriginStateHandle origin_state_handle;
293 leveldb::Status s;
294
295 std::tie(origin_state_handle, s, std::ignore, std::ignore, std::ignore) =
296 factory()->GetOrOpenOriginFactory(origin, context()->data_path(),
297 /*create_if_missing=*/true);
298 EXPECT_TRUE(origin_state_handle.IsHeld()) << s.ToString();
299 origin_state_handle.Release();
300
301 EXPECT_TRUE(factory()->GetOriginFactory(origin));
302 RunPostedTasks();
303 EXPECT_FALSE(factory()->GetOriginFactory(origin));
304 EXPECT_EQ(0ul, factory()->GetOpenOrigins().size());
305 }
306
TEST_F(IndexedDBFactoryTestWithMockTime,PreCloseTasksStart)307 TEST_F(IndexedDBFactoryTestWithMockTime, PreCloseTasksStart) {
308 base::SimpleTestClock clock;
309 clock.SetNow(base::Time::Now());
310 SetupContextWithFactories(nullptr, &clock);
311
312 const Origin origin = Origin::Create(GURL("http://localhost:81"));
313
314 IndexedDBOriginStateHandle origin_state_handle;
315 leveldb::Status s;
316
317 // Open a connection & immediately release it to cause the closing sequence to
318 // start.
319 std::tie(origin_state_handle, s, std::ignore, std::ignore, std::ignore) =
320 factory()->GetOrOpenOriginFactory(origin, context()->data_path(),
321 /*create_if_missing=*/true);
322 EXPECT_TRUE(origin_state_handle.IsHeld()) << s.ToString();
323 origin_state_handle.Release();
324
325 EXPECT_TRUE(factory()->GetOriginFactory(origin));
326 EXPECT_TRUE(factory()->GetOriginFactory(origin)->IsClosing());
327
328 EXPECT_EQ(IndexedDBOriginState::ClosingState::kPreCloseGracePeriod,
329 factory()->GetOriginFactory(origin)->closing_stage());
330
331 factory()->GetOriginFactory(origin)->close_timer()->FireNow();
332
333 // Since the compaction task always runs, the test assumes it is running.
334 ASSERT_TRUE(factory()->GetOriginFactory(origin));
335 EXPECT_EQ(IndexedDBOriginState::ClosingState::kRunningPreCloseTasks,
336 factory()->GetOriginFactory(origin)->closing_stage());
337 ASSERT_TRUE(factory()->GetOriginFactory(origin)->pre_close_task_queue());
338 EXPECT_TRUE(
339 factory()->GetOriginFactory(origin)->pre_close_task_queue()->started());
340 }
341
TEST_F(IndexedDBFactoryTestWithMockTime,TombstoneSweeperTiming)342 TEST_F(IndexedDBFactoryTestWithMockTime, TombstoneSweeperTiming) {
343 base::SimpleTestClock clock;
344 clock.SetNow(base::Time::Now());
345 SetupContextWithFactories(nullptr, &clock);
346
347 const Origin origin = Origin::Create(GURL("http://localhost:81"));
348
349 IndexedDBOriginStateHandle origin_state_handle;
350 leveldb::Status s;
351
352 // Open a connection & immediately release it to cause the closing sequence to
353 // start.
354 std::tie(origin_state_handle, s, std::ignore, std::ignore, std::ignore) =
355 factory()->GetOrOpenOriginFactory(origin, context()->data_path(),
356 /*create_if_missing=*/true);
357 EXPECT_TRUE(origin_state_handle.IsHeld()) << s.ToString();
358
359 // The factory should be closed, as the pre close tasks are delayed.
360 EXPECT_FALSE(origin_state_handle.origin_state()->ShouldRunTombstoneSweeper());
361
362 // Move the clock to run the tasks in the next close sequence.
363 clock.Advance(IndexedDBOriginState::kMaxEarliestGlobalSweepFromNow);
364
365 EXPECT_TRUE(origin_state_handle.origin_state()->ShouldRunTombstoneSweeper());
366
367 // Move clock forward to trigger next sweep, but origin has longer
368 // sweep minimum, so no tasks should execute.
369 clock.Advance(IndexedDBOriginState::kMaxEarliestGlobalSweepFromNow);
370
371 EXPECT_FALSE(origin_state_handle.origin_state()->ShouldRunTombstoneSweeper());
372
373 // Finally, move the clock forward so the origin should allow a sweep.
374 clock.Advance(IndexedDBOriginState::kMaxEarliestOriginSweepFromNow);
375
376 EXPECT_TRUE(origin_state_handle.origin_state()->ShouldRunTombstoneSweeper());
377 }
378
379 // Remove this test when the kill switch is removed.
TEST_F(IndexedDBFactoryTest,CompactionKillSwitchWorks)380 TEST_F(IndexedDBFactoryTest, CompactionKillSwitchWorks) {
381 base::test::ScopedFeatureList feature_list;
382 feature_list.InitWithFeatures({}, {kCompactIDBOnClose});
383
384 SetupContext();
385
386 const Origin origin = Origin::Create(GURL("http://localhost:81"));
387 IndexedDBOriginStateHandle origin_state_handle;
388 leveldb::Status s;
389
390 // Open a connection & immediately release it to cause the closing sequence to
391 // start.
392 std::tie(origin_state_handle, s, std::ignore, std::ignore, std::ignore) =
393 factory()->GetOrOpenOriginFactory(origin, context()->data_path(),
394 /*create_if_missing=*/true);
395 EXPECT_TRUE(origin_state_handle.IsHeld()) << s.ToString();
396
397 EXPECT_FALSE(origin_state_handle.origin_state()->ShouldRunCompaction());
398 }
399
TEST_F(IndexedDBFactoryTest,InMemoryFactoriesStay)400 TEST_F(IndexedDBFactoryTest, InMemoryFactoriesStay) {
401 SetupInMemoryContext();
402 ASSERT_TRUE(context()->IsInMemoryContext());
403
404 const Origin origin = Origin::Create(GURL("http://localhost:81"));
405
406 IndexedDBOriginStateHandle origin_state_handle;
407 leveldb::Status s;
408
409 std::tie(origin_state_handle, s, std::ignore, std::ignore, std::ignore) =
410 factory()->GetOrOpenOriginFactory(origin, context()->data_path(),
411 /*create_if_missing=*/true);
412 EXPECT_TRUE(origin_state_handle.IsHeld()) << s.ToString();
413 EXPECT_TRUE(OriginStateFromHandle(origin_state_handle)
414 ->backing_store()
415 ->is_incognito());
416 origin_state_handle.Release();
417
418 EXPECT_TRUE(factory()->GetOriginFactory(origin));
419 EXPECT_FALSE(factory()->GetOriginFactory(origin)->IsClosing());
420
421 factory()->ForceClose(origin, false);
422 EXPECT_TRUE(factory()->GetOriginFactory(origin));
423
424 factory()->ForceClose(origin, true);
425 EXPECT_FALSE(factory()->GetOriginFactory(origin));
426 }
427
TEST_F(IndexedDBFactoryTest,TooLongOrigin)428 TEST_F(IndexedDBFactoryTest, TooLongOrigin) {
429 SetupContext();
430
431 base::FilePath temp_dir = context()->data_path().DirName();
432 int limit = base::GetMaximumPathComponentLength(temp_dir);
433 EXPECT_GT(limit, 0);
434
435 std::string origin(limit + 1, 'x');
436 Origin too_long_origin = Origin::Create(GURL("http://" + origin + ":81/"));
437
438 IndexedDBOriginStateHandle origin_state_handle;
439 leveldb::Status s;
440
441 std::tie(origin_state_handle, s, std::ignore, std::ignore, std::ignore) =
442 factory()->GetOrOpenOriginFactory(too_long_origin, context()->data_path(),
443 /*create_if_missing=*/true);
444 EXPECT_FALSE(origin_state_handle.IsHeld());
445 EXPECT_TRUE(s.IsIOError());
446 }
447
TEST_F(IndexedDBFactoryTest,ContextDestructionClosesConnections)448 TEST_F(IndexedDBFactoryTest, ContextDestructionClosesConnections) {
449 SetupContext();
450 const Origin origin = Origin::Create(GURL("http://localhost:81"));
451
452 auto callbacks = base::MakeRefCounted<MockIndexedDBCallbacks>();
453 auto db_callbacks = base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>();
454
455 const int64_t transaction_id = 1;
456 auto create_transaction_callback =
457 base::BindOnce(&CreateAndBindTransactionPlaceholder);
458 auto connection = std::make_unique<IndexedDBPendingConnection>(
459 callbacks, db_callbacks,
460 transaction_id, IndexedDBDatabaseMetadata::DEFAULT_VERSION,
461 std::move(create_transaction_callback));
462 factory()->Open(ASCIIToUTF16("db"), std::move(connection), origin,
463 context()->data_path());
464 RunPostedTasks();
465
466 // Now simulate shutdown, which should clear all factories.
467 factory()->ContextDestroyed();
468 EXPECT_TRUE(db_callbacks->forced_close_called());
469 }
470
TEST_F(IndexedDBFactoryTest,ContextDestructionClosesHandles)471 TEST_F(IndexedDBFactoryTest, ContextDestructionClosesHandles) {
472 SetupContext();
473 const Origin origin = Origin::Create(GURL("http://localhost:81"));
474
475 IndexedDBOriginStateHandle origin_state_handle;
476 leveldb::Status s;
477
478 std::tie(origin_state_handle, s, std::ignore, std::ignore, std::ignore) =
479 factory()->GetOrOpenOriginFactory(origin, context()->data_path(),
480 /*create_if_missing=*/true);
481 EXPECT_TRUE(origin_state_handle.IsHeld()) << s.ToString();
482
483 // Now simulate shutdown, which should clear all factories.
484 factory()->ContextDestroyed();
485 EXPECT_FALSE(OriginStateFromHandle(origin_state_handle));
486 EXPECT_FALSE(factory()->GetOriginFactory(origin));
487 }
488
TEST_F(IndexedDBFactoryTest,FactoryForceClose)489 TEST_F(IndexedDBFactoryTest, FactoryForceClose) {
490 SetupContext();
491 const Origin origin = Origin::Create(GURL("http://localhost:81"));
492
493 IndexedDBOriginStateHandle origin_state_handle;
494 leveldb::Status s;
495
496 std::tie(origin_state_handle, s, std::ignore, std::ignore, std::ignore) =
497 factory()->GetOrOpenOriginFactory(origin, context()->data_path(),
498 /*create_if_missing=*/true);
499 EXPECT_TRUE(origin_state_handle.IsHeld()) << s.ToString();
500
501 OriginStateFromHandle(origin_state_handle)->ForceClose();
502 origin_state_handle.Release();
503
504 EXPECT_TRUE(factory()->GetOriginFactory(origin));
505 RunPostedTasks();
506 EXPECT_FALSE(factory()->GetOriginFactory(origin));
507 }
508
TEST_F(IndexedDBFactoryTest,ConnectionForceClose)509 TEST_F(IndexedDBFactoryTest, ConnectionForceClose) {
510 SetupContext();
511 const Origin origin = Origin::Create(GURL("http://localhost:81"));
512
513 auto callbacks = base::MakeRefCounted<MockIndexedDBCallbacks>();
514 auto db_callbacks = base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>();
515
516 const int64_t transaction_id = 1;
517 auto create_transaction_callback =
518 base::BindOnce(&CreateAndBindTransactionPlaceholder);
519 auto connection = std::make_unique<IndexedDBPendingConnection>(
520 callbacks, db_callbacks,
521 transaction_id, IndexedDBDatabaseMetadata::DEFAULT_VERSION,
522 std::move(create_transaction_callback));
523 factory()->Open(ASCIIToUTF16("db"), std::move(connection), origin,
524 context()->data_path());
525 EXPECT_FALSE(callbacks->connection());
526 RunPostedTasks();
527 EXPECT_TRUE(callbacks->connection());
528
529 EXPECT_TRUE(factory()->GetOriginFactory(origin));
530 EXPECT_FALSE(factory()->GetOriginFactory(origin)->IsClosing());
531
532 callbacks->connection()->CloseAndReportForceClose();
533
534 EXPECT_TRUE(factory()->GetOriginFactory(origin));
535 EXPECT_TRUE(factory()->GetOriginFactory(origin)->IsClosing());
536
537 EXPECT_TRUE(db_callbacks->forced_close_called());
538 }
539
TEST_F(IndexedDBFactoryTest,DatabaseForceCloseDuringUpgrade)540 TEST_F(IndexedDBFactoryTest, DatabaseForceCloseDuringUpgrade) {
541 SetupContext();
542 const Origin origin = Origin::Create(GURL("http://localhost:81"));
543
544 auto callbacks = base::MakeRefCounted<MockIndexedDBCallbacks>();
545 auto db_callbacks = base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>();
546
547 const int64_t transaction_id = 1;
548 auto create_transaction_callback =
549 base::BindOnce(&CreateAndBindTransactionPlaceholder);
550 auto connection = std::make_unique<IndexedDBPendingConnection>(
551 callbacks, db_callbacks,
552 transaction_id, IndexedDBDatabaseMetadata::NO_VERSION,
553 std::move(create_transaction_callback));
554
555 // Do the first half of the upgrade, and request the upgrade from renderer.
556 {
557 base::RunLoop loop;
558 callbacks->CallOnUpgradeNeeded(
559 base::BindLambdaForTesting([&]() { loop.Quit(); }));
560 factory()->Open(ASCIIToUTF16("db"), std::move(connection), origin,
561 context()->data_path());
562 loop.Run();
563 }
564
565 EXPECT_TRUE(callbacks->upgrade_called());
566 ASSERT_TRUE(callbacks->connection());
567 ASSERT_TRUE(callbacks->connection()->database());
568
569 callbacks->connection()->database()->ForceCloseAndRunTasks();
570
571 EXPECT_TRUE(db_callbacks->forced_close_called());
572 // Since there are no more references the factory should be closing.
573 EXPECT_TRUE(factory()->GetOriginFactory(origin));
574 EXPECT_TRUE(factory()->GetOriginFactory(origin)->IsClosing());
575 }
576
TEST_F(IndexedDBFactoryTest,ConnectionCloseDuringUpgrade)577 TEST_F(IndexedDBFactoryTest, ConnectionCloseDuringUpgrade) {
578 SetupContext();
579 const Origin origin = Origin::Create(GURL("http://localhost:81"));
580
581 auto callbacks = base::MakeRefCounted<MockIndexedDBCallbacks>();
582 auto db_callbacks = base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>();
583
584 const int64_t transaction_id = 1;
585 auto create_transaction_callback =
586 base::BindOnce(&CreateAndBindTransactionPlaceholder);
587 auto connection = std::make_unique<IndexedDBPendingConnection>(
588 callbacks, db_callbacks,
589 transaction_id, IndexedDBDatabaseMetadata::NO_VERSION,
590 std::move(create_transaction_callback));
591
592 // Do the first half of the upgrade, and request the upgrade from renderer.
593 {
594 base::RunLoop loop;
595 callbacks->CallOnUpgradeNeeded(
596 base::BindLambdaForTesting([&]() { loop.Quit(); }));
597 factory()->Open(ASCIIToUTF16("db"), std::move(connection), origin,
598 context()->data_path());
599 loop.Run();
600 }
601
602 EXPECT_TRUE(callbacks->upgrade_called());
603 ASSERT_TRUE(callbacks->connection());
604
605 // Close the connection.
606 callbacks->connection()->AbortTransactionsAndClose(
607 IndexedDBConnection::CloseErrorHandling::kAbortAllReturnLastError);
608
609 // Since there are no more references the factory should be closing.
610 EXPECT_TRUE(factory()->GetOriginFactory(origin));
611 EXPECT_TRUE(factory()->GetOriginFactory(origin)->IsClosing());
612 }
613
TEST_F(IndexedDBFactoryTest,DatabaseForceCloseWithFullConnection)614 TEST_F(IndexedDBFactoryTest, DatabaseForceCloseWithFullConnection) {
615 SetupContext();
616 const Origin origin = Origin::Create(GURL("http://localhost:81"));
617
618 std::unique_ptr<IndexedDBConnection> connection;
619 scoped_refptr<MockIndexedDBDatabaseCallbacks> db_callbacks;
620 std::tie(connection, db_callbacks) =
621 CreateConnectionForDatatabase(origin, ASCIIToUTF16("db"));
622
623 // Force close the database.
624 connection->database()->ForceCloseAndRunTasks();
625
626 EXPECT_TRUE(db_callbacks->forced_close_called());
627 // Since there are no more references the factory should be closing.
628 EXPECT_TRUE(factory()->GetOriginFactory(origin));
629 EXPECT_TRUE(factory()->GetOriginFactory(origin)->IsClosing());
630 }
631
TEST_F(IndexedDBFactoryTest,DeleteDatabase)632 TEST_F(IndexedDBFactoryTest, DeleteDatabase) {
633 SetupContext();
634
635 auto callbacks = base::MakeRefCounted<MockIndexedDBCallbacks>(
636 /*expect_connection=*/false);
637
638 const Origin origin = Origin::Create(GURL("http://localhost:81"));
639
640 factory()->DeleteDatabase(ASCIIToUTF16("db"), callbacks, origin,
641 context()->data_path(),
642 /*force_close=*/false);
643
644 // Since there are no more references the factory should be closing.
645 EXPECT_TRUE(factory()->GetOriginFactory(origin));
646 EXPECT_TRUE(factory()->GetOriginFactory(origin)->IsClosing());
647 }
648
TEST_F(IndexedDBFactoryTest,DeleteDatabaseWithForceClose)649 TEST_F(IndexedDBFactoryTest, DeleteDatabaseWithForceClose) {
650 SetupContext();
651
652 const Origin origin = Origin::Create(GURL("http://localhost:81"));
653 const base::string16 name = ASCIIToUTF16("db");
654
655 std::unique_ptr<IndexedDBConnection> connection;
656 scoped_refptr<MockIndexedDBDatabaseCallbacks> db_callbacks;
657 std::tie(connection, db_callbacks) =
658 CreateConnectionForDatatabase(origin, name);
659
660 auto callbacks = base::MakeRefCounted<MockIndexedDBCallbacks>(
661 /*expect_connection=*/false);
662
663 factory()->DeleteDatabase(name, callbacks, origin, context()->data_path(),
664 /*force_close=*/true);
665
666 // Force close means the connection has been force closed, but the factory
667 // isn't force closed, and instead is going through it's shutdown sequence.
668 EXPECT_FALSE(connection->IsConnected());
669 EXPECT_TRUE(db_callbacks->forced_close_called());
670 EXPECT_TRUE(factory()->GetOriginFactory(origin));
671 EXPECT_TRUE(factory()->GetOriginFactory(origin)->IsClosing());
672 }
673
TEST_F(IndexedDBFactoryTest,GetDatabaseNames)674 TEST_F(IndexedDBFactoryTest, GetDatabaseNames) {
675 SetupContext();
676
677 auto callbacks = base::MakeRefCounted<MockIndexedDBCallbacks>(
678 /*expect_connection=*/false);
679
680 const Origin origin = Origin::Create(GURL("http://localhost:81"));
681
682 factory()->GetDatabaseInfo(callbacks, origin, context()->data_path());
683
684 EXPECT_TRUE(callbacks->info_called());
685 // Since there are no more references the factory should be closing.
686 EXPECT_TRUE(factory()->GetOriginFactory(origin));
687 EXPECT_TRUE(factory()->GetOriginFactory(origin)->IsClosing());
688 }
689
690 class LookingForQuotaErrorMockCallbacks : public IndexedDBCallbacks {
691 public:
LookingForQuotaErrorMockCallbacks()692 LookingForQuotaErrorMockCallbacks()
693 : IndexedDBCallbacks(nullptr,
694 url::Origin(),
695 mojo::NullAssociatedRemote(),
696 base::SequencedTaskRunnerHandle::Get()) {}
OnError(const IndexedDBDatabaseError & error)697 void OnError(const IndexedDBDatabaseError& error) override {
698 error_called_ = true;
699 EXPECT_EQ(blink::mojom::IDBException::kQuotaError, error.code());
700 }
error_called() const701 bool error_called() const { return error_called_; }
702
703 private:
704 ~LookingForQuotaErrorMockCallbacks() override = default;
705 bool error_called_ = false;
706
707 DISALLOW_COPY_AND_ASSIGN(LookingForQuotaErrorMockCallbacks);
708 };
709
TEST_F(IndexedDBFactoryTest,QuotaErrorOnDiskFull)710 TEST_F(IndexedDBFactoryTest, QuotaErrorOnDiskFull) {
711 FakeLevelDBFactory fake_ldb_factory(
712 IndexedDBClassFactory::GetLevelDBOptions(), "indexed-db");
713 fake_ldb_factory.EnqueueNextOpenLevelDBStateResult(
714 nullptr, leveldb::Status::IOError("Disk is full."), true);
715 SetupContextWithFactories(&fake_ldb_factory,
716 base::DefaultClock::GetInstance());
717
718 auto callbacks = base::MakeRefCounted<LookingForQuotaErrorMockCallbacks>();
719 auto dummy_database_callbacks =
720 base::MakeRefCounted<IndexedDBDatabaseCallbacks>(
721 nullptr, mojo::NullAssociatedRemote(), context()->IDBTaskRunner());
722 const Origin origin = Origin::Create(GURL("http://localhost:81"));
723 const base::string16 name(ASCIIToUTF16("name"));
724 auto create_transaction_callback =
725 base::BindOnce(&CreateAndBindTransactionPlaceholder);
726 auto connection = std::make_unique<IndexedDBPendingConnection>(
727 callbacks, dummy_database_callbacks,
728 /*transaction_id=*/1, /*version=*/1,
729 std::move(create_transaction_callback));
730 factory()->Open(name, std::move(connection), origin, context()->data_path());
731 EXPECT_TRUE(callbacks->error_called());
732 base::RunLoop().RunUntilIdle();
733
734 ASSERT_EQ(1U, quota_manager()->write_error_tracker().size());
735 EXPECT_EQ(origin, quota_manager()->write_error_tracker().begin()->first);
736 EXPECT_EQ(1, quota_manager()->write_error_tracker().begin()->second);
737 }
738
TEST_F(IndexedDBFactoryTest,NotifyQuotaOnDatabaseError)739 TEST_F(IndexedDBFactoryTest, NotifyQuotaOnDatabaseError) {
740 SetupContext();
741 const Origin origin = Origin::Create(GURL("www.example.com"));
742 factory()->OnDatabaseError(origin,
743 leveldb::Status::Corruption("Corrupted stuff."),
744 "Corrupted stuff.");
745 base::RunLoop().RunUntilIdle();
746 // Quota should not be notified unless the status is IOError.
747 ASSERT_EQ(0U, quota_manager()->write_error_tracker().size());
748
749 factory()->OnDatabaseError(origin, leveldb::Status::IOError("Disk is full."),
750 "Disk is full.");
751 base::RunLoop().RunUntilIdle();
752 ASSERT_EQ(1U, quota_manager()->write_error_tracker().size());
753 EXPECT_EQ(origin, quota_manager()->write_error_tracker().begin()->first);
754 EXPECT_EQ(1, quota_manager()->write_error_tracker().begin()->second);
755 }
756
757 class ErrorCallbacks : public MockIndexedDBCallbacks {
758 public:
ErrorCallbacks()759 ErrorCallbacks() : MockIndexedDBCallbacks(false) {}
760
OnError(const IndexedDBDatabaseError & error)761 void OnError(const IndexedDBDatabaseError& error) override {
762 saw_error_ = true;
763 }
saw_error() const764 bool saw_error() const { return saw_error_; }
765
766 private:
767 ~ErrorCallbacks() override = default;
768 bool saw_error_ = false;
769
770 DISALLOW_COPY_AND_ASSIGN(ErrorCallbacks);
771 };
772
TEST_F(IndexedDBFactoryTest,DatabaseFailedOpen)773 TEST_F(IndexedDBFactoryTest, DatabaseFailedOpen) {
774 SetupContext();
775 const Origin origin = Origin::Create(GURL("http://localhost:81"));
776 const base::string16 db_name(ASCIIToUTF16("db"));
777 const int64_t transaction_id = 1;
778
779 auto callbacks = base::MakeRefCounted<MockIndexedDBCallbacks>();
780 auto db_callbacks = base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>();
781 auto failed_open_callbacks = base::MakeRefCounted<ErrorCallbacks>();
782 auto db_callbacks2 = base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>();
783
784 // Open at version 2.
785 const int64_t db_version = 2;
786 auto create_transaction_callback =
787 base::BindOnce(&CreateAndBindTransactionPlaceholder);
788
789 auto connection = std::make_unique<IndexedDBPendingConnection>(
790 callbacks, db_callbacks,
791 transaction_id, db_version, std::move(create_transaction_callback));
792 {
793 base::RunLoop loop;
794 callbacks->CallOnUpgradeNeeded(
795 base::BindLambdaForTesting([&]() { loop.Quit(); }));
796 factory()->Open(db_name, std::move(connection), origin,
797 context()->data_path());
798 loop.Run();
799 }
800 EXPECT_TRUE(callbacks->upgrade_called());
801 EXPECT_TRUE(factory()->IsDatabaseOpen(origin, db_name));
802
803 // Finish connecting, then close the connection.
804 {
805 base::RunLoop loop;
806 callbacks->CallOnDBSuccess(
807 base::BindLambdaForTesting([&]() { loop.Quit(); }));
808 EXPECT_TRUE(callbacks->connection());
809 callbacks->connection()->database()->Commit(
810 callbacks->connection()->GetTransaction(transaction_id));
811 loop.Run();
812 callbacks->connection()->AbortTransactionsAndClose(
813 IndexedDBConnection::CloseErrorHandling::kAbortAllReturnLastError);
814 RunPostedTasks();
815 EXPECT_FALSE(factory()->IsDatabaseOpen(origin, db_name));
816 }
817
818 // Open at version < 2, which will fail.
819 {
820 const int64_t db_version = 1;
821 auto create_transaction_callback =
822 base::BindOnce(&CreateAndBindTransactionPlaceholder);
823 auto connection = std::make_unique<IndexedDBPendingConnection>(
824 failed_open_callbacks, db_callbacks2,
825 transaction_id, db_version, std::move(create_transaction_callback));
826 factory()->Open(db_name, std::move(connection), origin,
827 context()->data_path());
828 EXPECT_TRUE(factory()->IsDatabaseOpen(origin, db_name));
829 RunPostedTasks();
830 EXPECT_TRUE(failed_open_callbacks->saw_error());
831 EXPECT_FALSE(factory()->IsDatabaseOpen(origin, db_name));
832 }
833 }
834
835 namespace {
836
837 class DataLossCallbacks final : public MockIndexedDBCallbacks {
838 public:
data_loss() const839 blink::mojom::IDBDataLoss data_loss() const { return data_loss_; }
840
OnError(const IndexedDBDatabaseError & error)841 void OnError(const IndexedDBDatabaseError& error) final {
842 ADD_FAILURE() << "Unexpected IDB error: " << error.message();
843 }
OnUpgradeNeeded(int64_t old_version,std::unique_ptr<IndexedDBConnection> connection,const IndexedDBDatabaseMetadata & metadata,const IndexedDBDataLossInfo & data_loss)844 void OnUpgradeNeeded(int64_t old_version,
845 std::unique_ptr<IndexedDBConnection> connection,
846 const IndexedDBDatabaseMetadata& metadata,
847 const IndexedDBDataLossInfo& data_loss) final {
848 data_loss_ = data_loss.status;
849 MockIndexedDBCallbacks::OnUpgradeNeeded(old_version, std::move(connection),
850 metadata, data_loss);
851 }
852
853 private:
854 ~DataLossCallbacks() final = default;
855 blink::mojom::IDBDataLoss data_loss_ = blink::mojom::IDBDataLoss::None;
856 };
857
TEST_F(IndexedDBFactoryTest,DataFormatVersion)858 TEST_F(IndexedDBFactoryTest, DataFormatVersion) {
859 SetupContext();
860 auto try_open = [this](const Origin& origin,
861 const IndexedDBDataFormatVersion& version) {
862 base::AutoReset<IndexedDBDataFormatVersion> override_version(
863 &IndexedDBDataFormatVersion::GetMutableCurrentForTesting(), version);
864
865 const int64_t transaction_id = 1;
866 auto callbacks = base::MakeRefCounted<DataLossCallbacks>();
867 auto db_callbacks = base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>();
868 auto create_transaction_callback =
869 base::BindOnce(&CreateAndBindTransactionPlaceholder);
870 auto pending_connection = std::make_unique<IndexedDBPendingConnection>(
871 callbacks, db_callbacks,
872 transaction_id,
873 /*version=*/1, std::move(create_transaction_callback));
874
875 {
876 base::RunLoop loop;
877 bool upgraded = false;
878 // The database might already exist. Wait until either a success or an
879 // ugprade request.
880 callbacks->CallOnUpgradeNeeded(base::BindLambdaForTesting([&]() {
881 upgraded = true;
882 loop.Quit();
883 }));
884 callbacks->CallOnDBSuccess(
885 base::BindLambdaForTesting([&]() { loop.Quit(); }));
886
887 this->factory()->Open(ASCIIToUTF16("test_db"),
888 std::move(pending_connection), origin,
889 context()->data_path());
890 loop.Run();
891
892 // If an upgrade was requested, then commit the upgrade transaction.
893 if (upgraded) {
894 EXPECT_TRUE(callbacks->upgrade_called());
895 EXPECT_TRUE(callbacks->connection());
896 EXPECT_TRUE(callbacks->connection()->database());
897 // Finish the upgrade by committing the transaction.
898 auto* connection = callbacks->connection();
899 {
900 base::RunLoop inner_loop;
901 callbacks->CallOnDBSuccess(
902 base::BindLambdaForTesting([&]() { inner_loop.Quit(); }));
903 connection->database()->Commit(
904 connection->GetTransaction(transaction_id));
905 inner_loop.Run();
906 }
907 }
908 }
909 RunPostedTasks();
910 factory()->ForceClose(origin, false);
911 RunPostedTasks();
912 return callbacks->data_loss();
913 };
914
915 static const struct {
916 const char* origin;
917 IndexedDBDataFormatVersion open_version_1;
918 IndexedDBDataFormatVersion open_version_2;
919 blink::mojom::IDBDataLoss expected_data_loss;
920 } kTestCases[] = {{"http://blink-downgrade.com/",
921 {3, 4},
922 {3, 3},
923 blink::mojom::IDBDataLoss::Total}};
924 for (const auto& test : kTestCases) {
925 SCOPED_TRACE(test.origin);
926 const Origin origin = Origin::Create(GURL(test.origin));
927 ASSERT_EQ(blink::mojom::IDBDataLoss::None,
928 try_open(origin, test.open_version_1));
929 EXPECT_EQ(test.expected_data_loss, try_open(origin, test.open_version_2));
930 }
931 }
932
933 } // namespace
934 } // namespace content
935