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