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