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