1 // Copyright (c) 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 "content/browser/indexed_db/indexed_db_backing_store.h"
6 
7 #include <stddef.h>
8 #include <stdint.h>
9 
10 #include <string>
11 #include <utility>
12 
13 #include "base/barrier_closure.h"
14 #include "base/bind.h"
15 #include "base/callback.h"
16 #include "base/containers/span.h"
17 #include "base/files/file_util.h"
18 #include "base/files/scoped_temp_dir.h"
19 #include "base/guid.h"
20 #include "base/logging.h"
21 #include "base/sequenced_task_runner.h"
22 #include "base/stl_util.h"
23 #include "base/strings/string16.h"
24 #include "base/strings/string_number_conversions.h"
25 #include "base/strings/utf_string_conversions.h"
26 #include "base/synchronization/waitable_event.h"
27 #include "base/synchronization/waitable_event_watcher.h"
28 #include "base/task/post_task.h"
29 #include "base/task/thread_pool.h"
30 #include "base/test/bind_test_util.h"
31 #include "base/test/task_environment.h"
32 #include "base/time/default_clock.h"
33 #include "components/services/storage/indexed_db/scopes/disjoint_range_lock_manager.h"
34 #include "components/services/storage/indexed_db/scopes/varint_coding.h"
35 #include "components/services/storage/indexed_db/transactional_leveldb/leveldb_write_batch.h"
36 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_database.h"
37 #include "components/services/storage/public/mojom/indexed_db_control.mojom-test-utils.h"
38 #include "content/browser/indexed_db/indexed_db_class_factory.h"
39 #include "content/browser/indexed_db/indexed_db_context_impl.h"
40 #include "content/browser/indexed_db/indexed_db_factory_impl.h"
41 #include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
42 #include "content/browser/indexed_db/indexed_db_leveldb_operations.h"
43 #include "content/browser/indexed_db/indexed_db_metadata_coding.h"
44 #include "content/browser/indexed_db/indexed_db_origin_state.h"
45 #include "content/browser/indexed_db/indexed_db_value.h"
46 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
47 #include "storage/browser/quota/special_storage_policy.h"
48 #include "storage/browser/test/fake_blob.h"
49 #include "storage/browser/test/mock_quota_manager_proxy.h"
50 #include "storage/browser/test/mock_special_storage_policy.h"
51 #include "testing/gtest/include/gtest/gtest.h"
52 #include "third_party/blink/public/common/indexeddb/web_idb_types.h"
53 #include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
54 
55 using base::ASCIIToUTF16;
56 using blink::IndexedDBDatabaseMetadata;
57 using blink::IndexedDBIndexMetadata;
58 using blink::IndexedDBKey;
59 using blink::IndexedDBKeyPath;
60 using blink::IndexedDBKeyRange;
61 using blink::IndexedDBObjectStoreMetadata;
62 using url::Origin;
63 
64 namespace content {
65 namespace indexed_db_backing_store_unittest {
66 
67 // Write |content| to |file|. Returns true on success.
WriteFile(const base::FilePath & file,base::StringPiece content)68 bool WriteFile(const base::FilePath& file, base::StringPiece content) {
69   int write_size = base::WriteFile(file, content.data(), content.length());
70   return write_size >= 0 && write_size == static_cast<int>(content.length());
71 }
72 
73 class TestableIndexedDBBackingStore : public IndexedDBBackingStore {
74  public:
TestableIndexedDBBackingStore(IndexedDBBackingStore::Mode backing_store_mode,TransactionalLevelDBFactory * leveldb_factory,const url::Origin & origin,const base::FilePath & blob_path,std::unique_ptr<TransactionalLevelDBDatabase> db,storage::mojom::BlobStorageContext * blob_storage_context,storage::mojom::NativeFileSystemContext * native_file_system_context,BlobFilesCleanedCallback blob_files_cleaned,ReportOutstandingBlobsCallback report_outstanding_blobs,scoped_refptr<base::SequencedTaskRunner> idb_task_runner,scoped_refptr<base::SequencedTaskRunner> io_task_runner)75   TestableIndexedDBBackingStore(
76       IndexedDBBackingStore::Mode backing_store_mode,
77       TransactionalLevelDBFactory* leveldb_factory,
78       const url::Origin& origin,
79       const base::FilePath& blob_path,
80       std::unique_ptr<TransactionalLevelDBDatabase> db,
81       storage::mojom::BlobStorageContext* blob_storage_context,
82       storage::mojom::NativeFileSystemContext* native_file_system_context,
83       BlobFilesCleanedCallback blob_files_cleaned,
84       ReportOutstandingBlobsCallback report_outstanding_blobs,
85       scoped_refptr<base::SequencedTaskRunner> idb_task_runner,
86       scoped_refptr<base::SequencedTaskRunner> io_task_runner)
87       : IndexedDBBackingStore(backing_store_mode,
88                               leveldb_factory,
89                               origin,
90                               blob_path,
91                               std::move(db),
92                               blob_storage_context,
93                               native_file_system_context,
94                               std::move(blob_files_cleaned),
95                               std::move(report_outstanding_blobs),
96                               std::move(idb_task_runner),
97                               std::move(io_task_runner)) {}
98   ~TestableIndexedDBBackingStore() override = default;
99 
removals() const100   const std::vector<base::FilePath>& removals() const { return removals_; }
ClearRemovals()101   void ClearRemovals() { removals_.clear(); }
102 
StartJournalCleaningTimer()103   void StartJournalCleaningTimer() override {
104     IndexedDBBackingStore::StartJournalCleaningTimer();
105   }
106 
107  protected:
RemoveBlobFile(int64_t database_id,int64_t blob_number) const108   bool RemoveBlobFile(int64_t database_id, int64_t blob_number) const override {
109     removals_.push_back(GetBlobFileName(database_id, blob_number));
110     return IndexedDBBackingStore::RemoveBlobFile(database_id, blob_number);
111   }
112 
113  private:
114   // This is modified in an overridden virtual function that is properly const
115   // in the real implementation, therefore must be mutable here.
116   mutable std::vector<base::FilePath> removals_;
117 
118   DISALLOW_COPY_AND_ASSIGN(TestableIndexedDBBackingStore);
119 };
120 
121 // Factory subclass to allow the test to use the
122 // TestableIndexedDBBackingStore subclass.
123 class TestIDBFactory : public IndexedDBFactoryImpl {
124  public:
TestIDBFactory(IndexedDBContextImpl * idb_context,storage::mojom::BlobStorageContext * blob_storage_context,storage::mojom::NativeFileSystemContext * native_file_system_context)125   explicit TestIDBFactory(
126       IndexedDBContextImpl* idb_context,
127       storage::mojom::BlobStorageContext* blob_storage_context,
128       storage::mojom::NativeFileSystemContext* native_file_system_context)
129       : IndexedDBFactoryImpl(idb_context,
130                              IndexedDBClassFactory::Get(),
131                              base::DefaultClock::GetInstance()),
132         blob_storage_context_(blob_storage_context),
133         native_file_system_context_(native_file_system_context) {}
134   ~TestIDBFactory() override = default;
135 
136  protected:
CreateBackingStore(IndexedDBBackingStore::Mode backing_store_mode,TransactionalLevelDBFactory * leveldb_factory,const url::Origin & origin,const base::FilePath & blob_path,std::unique_ptr<TransactionalLevelDBDatabase> db,storage::mojom::BlobStorageContext *,storage::mojom::NativeFileSystemContext *,IndexedDBBackingStore::BlobFilesCleanedCallback blob_files_cleaned,IndexedDBBackingStore::ReportOutstandingBlobsCallback report_outstanding_blobs,scoped_refptr<base::SequencedTaskRunner> idb_task_runner,scoped_refptr<base::SequencedTaskRunner> io_task_runner)137   std::unique_ptr<IndexedDBBackingStore> CreateBackingStore(
138       IndexedDBBackingStore::Mode backing_store_mode,
139       TransactionalLevelDBFactory* leveldb_factory,
140       const url::Origin& origin,
141       const base::FilePath& blob_path,
142       std::unique_ptr<TransactionalLevelDBDatabase> db,
143       storage::mojom::BlobStorageContext*,
144       storage::mojom::NativeFileSystemContext*,
145       IndexedDBBackingStore::BlobFilesCleanedCallback blob_files_cleaned,
146       IndexedDBBackingStore::ReportOutstandingBlobsCallback
147           report_outstanding_blobs,
148       scoped_refptr<base::SequencedTaskRunner> idb_task_runner,
149       scoped_refptr<base::SequencedTaskRunner> io_task_runner) override {
150     // Use the overridden blob storage and native file system contexts rather
151     // than the versions that were passed in to this method. This way tests can
152     // use a different context from what is stored in the IndexedDBContext.
153     return std::make_unique<TestableIndexedDBBackingStore>(
154         backing_store_mode, leveldb_factory, origin, blob_path, std::move(db),
155         blob_storage_context_, native_file_system_context_,
156         std::move(blob_files_cleaned), std::move(report_outstanding_blobs),
157         std::move(idb_task_runner), std::move(io_task_runner));
158   }
159 
160  private:
161   storage::mojom::BlobStorageContext* blob_storage_context_;
162   storage::mojom::NativeFileSystemContext* native_file_system_context_;
163 
164   DISALLOW_COPY_AND_ASSIGN(TestIDBFactory);
165 };
166 
167 struct BlobWrite {
168   BlobWrite() = default;
BlobWritecontent::indexed_db_backing_store_unittest::BlobWrite169   BlobWrite(BlobWrite&& other) {
170     blob = std::move(other.blob);
171     path = std::move(other.path);
172   }
BlobWritecontent::indexed_db_backing_store_unittest::BlobWrite173   BlobWrite(mojo::PendingRemote<::blink::mojom::Blob> blob, base::FilePath path)
174       : blob(std::move(blob)), path(path) {}
175   ~BlobWrite() = default;
176 
GetBlobNumbercontent::indexed_db_backing_store_unittest::BlobWrite177   int64_t GetBlobNumber() const {
178     int64_t result;
179     EXPECT_TRUE(base::StringToInt64(path.BaseName().AsUTF8Unsafe(), &result));
180     return result;
181   }
182 
183   mojo::Remote<::blink::mojom::Blob> blob;
184   base::FilePath path;
185 };
186 
187 class MockBlobStorageContext : public ::storage::mojom::BlobStorageContext {
188  public:
189   ~MockBlobStorageContext() override = default;
190 
RegisterFromDataItem(mojo::PendingReceiver<::blink::mojom::Blob> blob,const std::string & uuid,storage::mojom::BlobDataItemPtr item)191   void RegisterFromDataItem(mojo::PendingReceiver<::blink::mojom::Blob> blob,
192                             const std::string& uuid,
193                             storage::mojom::BlobDataItemPtr item) override {
194     NOTREACHED();
195   }
RegisterFromMemory(mojo::PendingReceiver<::blink::mojom::Blob> blob,const std::string & uuid,::mojo_base::BigBuffer data)196   void RegisterFromMemory(mojo::PendingReceiver<::blink::mojom::Blob> blob,
197                           const std::string& uuid,
198                           ::mojo_base::BigBuffer data) override {
199     NOTREACHED();
200   }
WriteBlobToFile(mojo::PendingRemote<::blink::mojom::Blob> blob,const base::FilePath & path,bool flush_on_write,base::Optional<base::Time> last_modified,WriteBlobToFileCallback callback)201   void WriteBlobToFile(mojo::PendingRemote<::blink::mojom::Blob> blob,
202                        const base::FilePath& path,
203                        bool flush_on_write,
204                        base::Optional<base::Time> last_modified,
205                        WriteBlobToFileCallback callback) override {
206     writes_.emplace_back(std::move(blob), path);
207     base::SequencedTaskRunnerHandle::Get()->PostTask(
208         FROM_HERE,
209         base::BindOnce(std::move(callback),
210                        storage::mojom::WriteBlobToFileResult::kSuccess));
211   }
212 
writes()213   const std::vector<BlobWrite>& writes() { return writes_; }
ClearWrites()214   void ClearWrites() { writes_.clear(); }
215 
216  private:
217   std::vector<BlobWrite> writes_;
218 };
219 
220 class FakeNativeFileSystemTransferToken
221     : public ::blink::mojom::NativeFileSystemTransferToken {
222  public:
FakeNativeFileSystemTransferToken(const base::UnguessableToken & id)223   explicit FakeNativeFileSystemTransferToken(const base::UnguessableToken& id)
224       : id_(id) {}
225 
GetInternalID(GetInternalIDCallback callback)226   void GetInternalID(GetInternalIDCallback callback) override {
227     std::move(callback).Run(id_);
228   }
229 
Clone(mojo::PendingReceiver<blink::mojom::NativeFileSystemTransferToken> clone_receiver)230   void Clone(mojo::PendingReceiver<blink::mojom::NativeFileSystemTransferToken>
231                  clone_receiver) override {
232     mojo::MakeSelfOwnedReceiver(
233         std::make_unique<FakeNativeFileSystemTransferToken>(id_),
234         std::move(clone_receiver));
235   }
236 
237  private:
238   base::UnguessableToken id_;
239 };
240 
241 class MockNativeFileSystemContext
242     : public ::storage::mojom::NativeFileSystemContext {
243  public:
244   ~MockNativeFileSystemContext() override = default;
245 
SerializeHandle(mojo::PendingRemote<::blink::mojom::NativeFileSystemTransferToken> pending_token,SerializeHandleCallback callback)246   void SerializeHandle(
247       mojo::PendingRemote<::blink::mojom::NativeFileSystemTransferToken>
248           pending_token,
249       SerializeHandleCallback callback) override {
250     writes_.emplace_back(std::move(pending_token));
251     base::SequencedTaskRunnerHandle::Get()->PostTask(
252         FROM_HERE, base::BindOnce(std::move(callback),
253                                   std::vector<uint8_t>{writes_.size() - 1}));
254   }
255 
DeserializeHandle(const url::Origin & origin,const std::vector<uint8_t> & bits,mojo::PendingReceiver<::blink::mojom::NativeFileSystemTransferToken> token)256   void DeserializeHandle(
257       const url::Origin& origin,
258       const std::vector<uint8_t>& bits,
259       mojo::PendingReceiver<::blink::mojom::NativeFileSystemTransferToken>
260           token) override {
261     NOTREACHED();
262   }
263 
264   const std::vector<
265       mojo::Remote<::blink::mojom::NativeFileSystemTransferToken>>&
writes()266   writes() {
267     return writes_;
268   }
ClearWrites()269   void ClearWrites() { writes_.clear(); }
270 
271  private:
272   std::vector<mojo::Remote<::blink::mojom::NativeFileSystemTransferToken>>
273       writes_;
274 };
275 
276 class IndexedDBBackingStoreTest : public testing::Test {
277  public:
IndexedDBBackingStoreTest()278   IndexedDBBackingStoreTest()
279       : special_storage_policy_(
280             base::MakeRefCounted<storage::MockSpecialStoragePolicy>()),
281         quota_manager_proxy_(
282             base::MakeRefCounted<storage::MockQuotaManagerProxy>(nullptr,
283                                                                  nullptr)) {}
284 
SetUp()285   void SetUp() override {
286     special_storage_policy_->SetAllUnlimited(true);
287     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
288 
289     blob_context_ = std::make_unique<MockBlobStorageContext>();
290     native_file_system_context_ =
291         std::make_unique<MockNativeFileSystemContext>();
292 
293     idb_context_ = base::MakeRefCounted<IndexedDBContextImpl>(
294         temp_dir_.GetPath(), special_storage_policy_, quota_manager_proxy_,
295         base::DefaultClock::GetInstance(),
296         /*blob_storage_context=*/mojo::NullRemote(),
297         /*native_file_system_context=*/mojo::NullRemote(),
298         base::SequencedTaskRunnerHandle::Get(),
299         base::SequencedTaskRunnerHandle::Get());
300 
301     CreateFactoryAndBackingStore();
302 
303     // useful keys and values during tests
304     value1_ = IndexedDBValue("value1", {});
305     value2_ = IndexedDBValue("value2", {});
306 
307     key1_ = IndexedDBKey(99, blink::mojom::IDBKeyType::Number);
308     key2_ = IndexedDBKey(ASCIIToUTF16("key2"));
309   }
310 
CreateFactoryAndBackingStore()311   void CreateFactoryAndBackingStore() {
312     const Origin origin = Origin::Create(GURL("http://localhost:81"));
313     idb_factory_ = std::make_unique<TestIDBFactory>(
314         idb_context_.get(), blob_context_.get(),
315         native_file_system_context_.get());
316 
317     leveldb::Status s;
318     std::tie(origin_state_handle_, s, std::ignore, data_loss_info_,
319              std::ignore) =
320         idb_factory_->GetOrOpenOriginFactory(origin, idb_context_->data_path(),
321                                              /*create_if_missing=*/true);
322     if (!origin_state_handle_.IsHeld()) {
323       backing_store_ = nullptr;
324       return;
325     }
326     backing_store_ = static_cast<TestableIndexedDBBackingStore*>(
327         origin_state_handle_.origin_state()->backing_store());
328     lock_manager_ = origin_state_handle_.origin_state()->lock_manager();
329   }
330 
CreateDummyLock()331   std::vector<ScopeLock> CreateDummyLock() {
332     base::RunLoop loop;
333     ScopesLocksHolder locks_receiver;
334     bool success = lock_manager_->AcquireLocks(
335         {{0, {"01", "11"}, ScopesLockManager::LockType::kShared}},
336         locks_receiver.AsWeakPtr(),
337         base::BindLambdaForTesting([&loop]() { loop.Quit(); }));
338     EXPECT_TRUE(success);
339     if (success)
340       loop.Run();
341     return std::move(locks_receiver.locks);
342   }
343 
DestroyFactoryAndBackingStore()344   void DestroyFactoryAndBackingStore() {
345     origin_state_handle_.Release();
346     idb_factory_.reset();
347     backing_store_ = nullptr;
348   }
349 
TearDown()350   void TearDown() override {
351     DestroyFactoryAndBackingStore();
352     quota_manager_proxy_->SimulateQuotaManagerDestroyed();
353 
354     if (idb_context_ && !idb_context_->IsInMemoryContext()) {
355       IndexedDBFactoryImpl* factory = idb_context_->GetIDBFactory();
356 
357       // Loop through all open origins, and force close them, and request the
358       // deletion of the leveldb state. Once the states are no longer around,
359       // delete all of the databases on disk.
360       auto open_factory_origins = factory->GetOpenOrigins();
361 
362       for (auto origin : open_factory_origins) {
363         base::RunLoop loop;
364         IndexedDBOriginState* per_origin_factory =
365             factory->GetOriginFactory(origin);
366 
367         auto* leveldb_state =
368             per_origin_factory->backing_store()->db()->leveldb_state();
369 
370         base::WaitableEvent leveldb_close_event;
371         base::WaitableEventWatcher event_watcher;
372         leveldb_state->RequestDestruction(&leveldb_close_event);
373         event_watcher.StartWatching(
374             &leveldb_close_event,
375             base::BindLambdaForTesting(
376                 [&](base::WaitableEvent*) { loop.Quit(); }),
377             base::SequencedTaskRunnerHandle::Get());
378 
379         idb_context_->ForceCloseSync(
380             origin,
381             storage::mojom::ForceCloseReason::FORCE_CLOSE_DELETE_ORIGIN);
382         loop.Run();
383         // There is a possible race in |leveldb_close_event| where the signaling
384         // thread is still in the WaitableEvent::Signal() method. To ensure that
385         // the other thread exits their Signal method, any method on the
386         // WaitableEvent can be called to acquire the internal lock (which will
387         // subsequently wait for the other thread to exit the Signal method).
388         EXPECT_TRUE(leveldb_close_event.IsSignaled());
389       }
390       // All leveldb databases are closed, and they can be deleted.
391       for (auto origin : idb_context_->GetAllOrigins()) {
392         bool success = false;
393         storage::mojom::IndexedDBControlAsyncWaiter waiter(idb_context_.get());
394         waiter.DeleteForOrigin(origin, &success);
395         EXPECT_TRUE(success);
396       }
397     }
398     if (temp_dir_.IsValid())
399       ASSERT_TRUE(temp_dir_.Delete());
400 
401     // Wait until the context has fully destroyed.
402     scoped_refptr<base::SequencedTaskRunner> task_runner =
403         idb_context_->IDBTaskRunner();
404     idb_context_.reset();
405     {
406       base::RunLoop loop;
407       task_runner->PostTask(FROM_HERE, loop.QuitClosure());
408       loop.Run();
409     }
410   }
411 
backing_store()412   TestableIndexedDBBackingStore* backing_store() { return backing_store_; }
413 
414   // Cycle the idb runner to help clean up tasks, which allows for a clean
415   // shutdown of the leveldb database. This ensures that all file handles are
416   // released and the folder can be deleted on windows (which doesn't allow
417   // folders to be deleted when inside files are in use/exist).
CycleIDBTaskRunner()418   void CycleIDBTaskRunner() {
419     base::RunLoop cycle_loop;
420     idb_context_->IDBTaskRunner()->PostTask(FROM_HERE,
421                                             cycle_loop.QuitClosure());
422     cycle_loop.Run();
423   }
424 
425  protected:
426   base::test::TaskEnvironment task_environment_;
427 
428   base::ScopedTempDir temp_dir_;
429   std::unique_ptr<MockBlobStorageContext> blob_context_;
430   std::unique_ptr<MockNativeFileSystemContext> native_file_system_context_;
431   scoped_refptr<storage::MockSpecialStoragePolicy> special_storage_policy_;
432   scoped_refptr<storage::MockQuotaManagerProxy> quota_manager_proxy_;
433   scoped_refptr<IndexedDBContextImpl> idb_context_;
434   std::unique_ptr<TestIDBFactory> idb_factory_;
435   DisjointRangeLockManager* lock_manager_;
436 
437   IndexedDBOriginStateHandle origin_state_handle_;
438   TestableIndexedDBBackingStore* backing_store_ = nullptr;
439   IndexedDBDataLossInfo data_loss_info_;
440 
441   // Sample keys and values that are consistent.
442   IndexedDBKey key1_;
443   IndexedDBKey key2_;
444   IndexedDBValue value1_;
445   IndexedDBValue value2_;
446 
447  private:
448   DISALLOW_COPY_AND_ASSIGN(IndexedDBBackingStoreTest);
449 };
450 
451 enum class ExternalObjectTestType {
452   kOnlyBlobs,
453   kOnlyNativeFileSystemHandles,
454   kBlobsAndNativeFileSystemHandles
455 };
456 
457 class IndexedDBBackingStoreTestWithExternalObjects
458     : public testing::WithParamInterface<ExternalObjectTestType>,
459       public IndexedDBBackingStoreTest {
460  public:
461   IndexedDBBackingStoreTestWithExternalObjects() = default;
462 
TestType()463   virtual ExternalObjectTestType TestType() { return GetParam(); }
464 
IncludesBlobs()465   bool IncludesBlobs() {
466     return TestType() != ExternalObjectTestType::kOnlyNativeFileSystemHandles;
467   }
468 
IncludesNativeFileSystemHandles()469   bool IncludesNativeFileSystemHandles() {
470     return TestType() != ExternalObjectTestType::kOnlyBlobs;
471   }
472 
SetUp()473   void SetUp() override {
474     IndexedDBBackingStoreTest::SetUp();
475 
476     const int64_t kTime1 = 13255919133000000ll;
477     const int64_t kTime2 = 13287455133000000ll;
478     // useful keys and values during tests
479     if (IncludesBlobs()) {
480       external_objects_.push_back(
481           CreateBlobInfo(base::UTF8ToUTF16("blob type"), 1));
482       external_objects_.push_back(CreateBlobInfo(
483           base::UTF8ToUTF16("file name"), base::UTF8ToUTF16("file type"),
484           base::Time::FromDeltaSinceWindowsEpoch(
485               base::TimeDelta::FromMicroseconds(kTime1)),
486           kBlobFileData1.size()));
487       external_objects_.push_back(CreateBlobInfo(
488           base::UTF8ToUTF16("file name"), base::UTF8ToUTF16("file type"),
489           base::Time::FromDeltaSinceWindowsEpoch(
490               base::TimeDelta::FromMicroseconds(kTime2)),
491           kBlobFileData2.size()));
492     }
493     if (IncludesNativeFileSystemHandles()) {
494       external_objects_.push_back(CreateNativeFileSystemHandle());
495       external_objects_.push_back(CreateNativeFileSystemHandle());
496     }
497     value3_ = IndexedDBValue("value3", external_objects_);
498     key3_ = IndexedDBKey(ASCIIToUTF16("key3"));
499   }
500 
CreateBlobInfo(const base::string16 & file_name,const base::string16 & type,base::Time last_modified,int64_t size)501   IndexedDBExternalObject CreateBlobInfo(const base::string16& file_name,
502                                          const base::string16& type,
503                                          base::Time last_modified,
504                                          int64_t size) {
505     auto uuid = base::GenerateGUID();
506     mojo::PendingRemote<blink::mojom::Blob> remote;
507     base::ThreadPool::CreateSequencedTaskRunner({})->PostTask(
508         FROM_HERE,
509         base::BindOnce(
510             [](std::string uuid,
511                mojo::PendingReceiver<blink::mojom::Blob> pending_receiver) {
512               mojo::MakeSelfOwnedReceiver(
513                   std::make_unique<storage::FakeBlob>(uuid),
514                   std::move(pending_receiver));
515             },
516             uuid, remote.InitWithNewPipeAndPassReceiver()));
517     IndexedDBExternalObject info(std::move(remote), uuid, file_name, type,
518                                  last_modified, size);
519     return info;
520   }
521 
CreateBlobInfo(const base::string16 & type,int64_t size)522   IndexedDBExternalObject CreateBlobInfo(const base::string16& type,
523                                          int64_t size) {
524     auto uuid = base::GenerateGUID();
525     mojo::PendingRemote<blink::mojom::Blob> remote;
526     base::ThreadPool::CreateSequencedTaskRunner({})->PostTask(
527         FROM_HERE,
528         base::BindOnce(
529             [](std::string uuid,
530                mojo::PendingReceiver<blink::mojom::Blob> pending_receiver) {
531               mojo::MakeSelfOwnedReceiver(
532                   std::make_unique<storage::FakeBlob>(uuid),
533                   std::move(pending_receiver));
534             },
535             uuid, remote.InitWithNewPipeAndPassReceiver()));
536     IndexedDBExternalObject info(std::move(remote), uuid, type, size);
537     return info;
538   }
539 
CreateNativeFileSystemHandle()540   IndexedDBExternalObject CreateNativeFileSystemHandle() {
541     auto id = base::UnguessableToken::Create();
542     mojo::PendingRemote<blink::mojom::NativeFileSystemTransferToken> remote;
543     base::ThreadPool::CreateSequencedTaskRunner({})->PostTask(
544         FROM_HERE,
545         base::BindOnce(
546             [](base::UnguessableToken id,
547                mojo::PendingReceiver<
548                    blink::mojom::NativeFileSystemTransferToken>
549                    pending_receiver) {
550               mojo::MakeSelfOwnedReceiver(
551                   std::make_unique<FakeNativeFileSystemTransferToken>(id),
552                   std::move(pending_receiver));
553             },
554             id, remote.InitWithNewPipeAndPassReceiver()));
555     IndexedDBExternalObject info(std::move(remote));
556     return info;
557   }
558 
559   // This just checks the data that survive getting stored and recalled, e.g.
560   // the file path and UUID will change and thus aren't verified.
CheckBlobInfoMatches(const std::vector<IndexedDBExternalObject> & reads) const561   bool CheckBlobInfoMatches(
562       const std::vector<IndexedDBExternalObject>& reads) const {
563     DCHECK(idb_context_->IDBTaskRunner()->RunsTasksInCurrentSequence());
564 
565     if (external_objects_.size() != reads.size()) {
566       EXPECT_EQ(external_objects_.size(), reads.size());
567       return false;
568     }
569     for (size_t i = 0; i < external_objects_.size(); ++i) {
570       const IndexedDBExternalObject& a = external_objects_[i];
571       const IndexedDBExternalObject& b = reads[i];
572       if (a.object_type() != b.object_type()) {
573         EXPECT_EQ(a.object_type(), b.object_type());
574         return false;
575       }
576       switch (a.object_type()) {
577         case IndexedDBExternalObject::ObjectType::kFile:
578           if (a.file_name() != b.file_name()) {
579             EXPECT_EQ(a.file_name(), b.file_name());
580             return false;
581           }
582           if (a.last_modified() != b.last_modified()) {
583             EXPECT_EQ(a.last_modified(), b.last_modified());
584             return false;
585           }
586           FALLTHROUGH;
587         case IndexedDBExternalObject::ObjectType::kBlob:
588           if (a.type() != b.type()) {
589             EXPECT_EQ(a.type(), b.type());
590             return false;
591           }
592           if (a.size() != b.size()) {
593             EXPECT_EQ(a.size(), b.size());
594             return false;
595           }
596           break;
597         case IndexedDBExternalObject::ObjectType::kNativeFileSystemHandle:
598           if (b.native_file_system_token().empty()) {
599             EXPECT_FALSE(b.native_file_system_token().empty());
600             return false;
601           }
602           break;
603       }
604     }
605     return true;
606   }
607 
CheckBlobReadsMatchWrites(const std::vector<IndexedDBExternalObject> & reads) const608   bool CheckBlobReadsMatchWrites(
609       const std::vector<IndexedDBExternalObject>& reads) const {
610     DCHECK(idb_context_->IDBTaskRunner()->RunsTasksInCurrentSequence());
611 
612     if (blob_context_->writes().size() +
613             native_file_system_context_->writes().size() !=
614         reads.size()) {
615       return false;
616     }
617     std::set<base::FilePath> ids;
618     for (const auto& write : blob_context_->writes())
619       ids.insert(write.path);
620     if (ids.size() != blob_context_->writes().size())
621       return false;
622     for (const auto& read : reads) {
623       switch (read.object_type()) {
624         case IndexedDBExternalObject::ObjectType::kBlob:
625         case IndexedDBExternalObject::ObjectType::kFile:
626           if (ids.count(read.indexed_db_file_path()) != 1)
627             return false;
628           break;
629         case IndexedDBExternalObject::ObjectType::kNativeFileSystemHandle:
630           if (read.native_file_system_token().size() != 1 ||
631               read.native_file_system_token()[0] >
632                   native_file_system_context_->writes().size()) {
633             return false;
634           }
635           break;
636       }
637     }
638     return true;
639   }
640 
CheckBlobWrites()641   bool CheckBlobWrites() {
642     DCHECK(idb_context_->IDBTaskRunner()->RunsTasksInCurrentSequence());
643 
644     if (blob_context_->writes().size() +
645             native_file_system_context_->writes().size() !=
646         external_objects_.size()) {
647       return false;
648     }
649     for (size_t i = 0; i < blob_context_->writes().size(); ++i) {
650       const BlobWrite& desc = blob_context_->writes()[i];
651       const IndexedDBExternalObject& info = external_objects_[i];
652       base::RunLoop uuid_loop;
653       std::string uuid_out;
654       DCHECK(desc.blob.is_bound());
655       DCHECK(desc.blob.is_connected());
656       desc.blob->GetInternalUUID(
657           base::BindLambdaForTesting([&](const std::string& uuid) {
658             uuid_out = uuid;
659             uuid_loop.Quit();
660           }));
661       uuid_loop.Run();
662       if (uuid_out != info.uuid())
663         return false;
664     }
665     for (size_t i = 0; i < native_file_system_context_->writes().size(); ++i) {
666       const IndexedDBExternalObject& info =
667           external_objects_[blob_context_->writes().size() + i];
668       base::UnguessableToken info_token;
669       {
670         base::RunLoop loop;
671         info.native_file_system_token_remote()->GetInternalID(
672             base::BindLambdaForTesting(
673                 [&](const base::UnguessableToken& token) {
674                   info_token = token;
675                   loop.Quit();
676                 }));
677         loop.Run();
678       }
679       base::UnguessableToken written_token;
680       {
681         base::RunLoop loop;
682         native_file_system_context_->writes()[i]->GetInternalID(
683             base::BindLambdaForTesting(
684                 [&](const base::UnguessableToken& token) {
685                   written_token = token;
686                   loop.Quit();
687                 }));
688         loop.Run();
689       }
690       if (info_token != written_token) {
691         EXPECT_EQ(info_token, written_token);
692         return false;
693       }
694     }
695     return true;
696   }
697 
CheckBlobRemovals() const698   bool CheckBlobRemovals() const {
699     DCHECK(idb_context_->IDBTaskRunner()->RunsTasksInCurrentSequence());
700 
701     if (backing_store_->removals().size() != blob_context_->writes().size())
702       return false;
703     for (size_t i = 0; i < blob_context_->writes().size(); ++i) {
704       if (blob_context_->writes()[i].path != backing_store_->removals()[i]) {
705         return false;
706       }
707     }
708     return true;
709   }
710 
external_objects()711   std::vector<IndexedDBExternalObject>& external_objects() {
712     return external_objects_;
713   }
714 
715   // Sample keys and values that are consistent. Public so that posted
716   // lambdas passed |this| can access them.
717   IndexedDBKey key3_;
718   IndexedDBValue value3_;
719 
720  protected:
721   const std::string kBlobFileData1 = "asdfgasdf";
722   const std::string kBlobFileData2 = "aaaaaa";
723 
724  private:
725   // Blob details referenced by |value3_|. The various CheckBlob*() methods
726   // can be used to verify the state as a test progresses.
727   std::vector<IndexedDBExternalObject> external_objects_;
728 
729   std::vector<std::string> blob_remote_uuids_;
730 
731   DISALLOW_COPY_AND_ASSIGN(IndexedDBBackingStoreTestWithExternalObjects);
732 };
733 
734 INSTANTIATE_TEST_SUITE_P(
735     /* no prefix */,
736     IndexedDBBackingStoreTestWithExternalObjects,
737     ::testing::Values(
738         ExternalObjectTestType::kOnlyBlobs,
739         ExternalObjectTestType::kOnlyNativeFileSystemHandles,
740         ExternalObjectTestType::kBlobsAndNativeFileSystemHandles));
741 
742 class IndexedDBBackingStoreTestWithBlobs
743     : public IndexedDBBackingStoreTestWithExternalObjects {
744  public:
TestType()745   ExternalObjectTestType TestType() override {
746     return ExternalObjectTestType::kOnlyBlobs;
747   }
748 };
749 
CreateBlobWriteCallback(bool * succeeded,base::OnceClosure on_done=base::OnceClosure ())750 BlobWriteCallback CreateBlobWriteCallback(
751     bool* succeeded,
752     base::OnceClosure on_done = base::OnceClosure()) {
753   *succeeded = false;
754   return base::BindOnce(
755       [](bool* succeeded, base::OnceClosure on_done, BlobWriteResult result) {
756         switch (result) {
757           case BlobWriteResult::kFailure:
758             NOTREACHED();
759             break;
760           case BlobWriteResult::kRunPhaseTwoAsync:
761           case BlobWriteResult::kRunPhaseTwoAndReturnResult:
762             *succeeded = true;
763             break;
764         }
765         if (!on_done.is_null())
766           std::move(on_done).Run();
767         return leveldb::Status::OK();
768       },
769       succeeded, std::move(on_done));
770 }
771 
TEST_F(IndexedDBBackingStoreTest,PutGetConsistency)772 TEST_F(IndexedDBBackingStoreTest, PutGetConsistency) {
773   base::RunLoop loop;
774   idb_context_->IDBTaskRunner()->PostTask(
775       FROM_HERE, base::BindLambdaForTesting([&]() {
776         const IndexedDBKey key = key1_;
777         IndexedDBValue value = value1_;
778         {
779           IndexedDBBackingStore::Transaction transaction1(
780               backing_store()->AsWeakPtr(),
781               blink::mojom::IDBTransactionDurability::Relaxed,
782               blink::mojom::IDBTransactionMode::ReadWrite);
783           transaction1.Begin(CreateDummyLock());
784           IndexedDBBackingStore::RecordIdentifier record;
785           leveldb::Status s = backing_store()->PutRecord(&transaction1, 1, 1,
786                                                          key, &value, &record);
787           EXPECT_TRUE(s.ok());
788           bool succeeded = false;
789           EXPECT_TRUE(
790               transaction1.CommitPhaseOne(CreateBlobWriteCallback(&succeeded))
791                   .ok());
792           EXPECT_TRUE(succeeded);
793           EXPECT_TRUE(transaction1.CommitPhaseTwo().ok());
794         }
795 
796         {
797           IndexedDBBackingStore::Transaction transaction2(
798               backing_store()->AsWeakPtr(),
799               blink::mojom::IDBTransactionDurability::Relaxed,
800               blink::mojom::IDBTransactionMode::ReadWrite);
801           transaction2.Begin(CreateDummyLock());
802           IndexedDBValue result_value;
803           EXPECT_TRUE(backing_store()
804                           ->GetRecord(&transaction2, 1, 1, key, &result_value)
805                           .ok());
806           bool succeeded = false;
807           EXPECT_TRUE(
808               transaction2.CommitPhaseOne(CreateBlobWriteCallback(&succeeded))
809                   .ok());
810           EXPECT_TRUE(succeeded);
811           EXPECT_TRUE(transaction2.CommitPhaseTwo().ok());
812           EXPECT_EQ(value.bits, result_value.bits);
813         }
814         loop.Quit();
815       }));
816   loop.Run();
817 
818   CycleIDBTaskRunner();
819 }
820 
TEST_P(IndexedDBBackingStoreTestWithExternalObjects,PutGetConsistency)821 TEST_P(IndexedDBBackingStoreTestWithExternalObjects, PutGetConsistency) {
822   // Initiate transaction1 - writing blobs.
823   std::unique_ptr<IndexedDBBackingStore::Transaction> transaction1 =
824       std::make_unique<IndexedDBBackingStore::Transaction>(
825           backing_store()->AsWeakPtr(),
826           blink::mojom::IDBTransactionDurability::Relaxed,
827           blink::mojom::IDBTransactionMode::ReadWrite);
828   transaction1->Begin(CreateDummyLock());
829   IndexedDBBackingStore::RecordIdentifier record;
830   EXPECT_TRUE(
831       backing_store()
832           ->PutRecord(transaction1.get(), 1, 1, key3_, &value3_, &record)
833           .ok());
834   bool succeeded = false;
835   base::RunLoop phase_one_wait;
836   EXPECT_TRUE(transaction1
837                   ->CommitPhaseOne(CreateBlobWriteCallback(
838                       &succeeded, phase_one_wait.QuitClosure()))
839                   .ok());
840   EXPECT_FALSE(succeeded);
841   task_environment_.RunUntilIdle();
842   phase_one_wait.Run();
843 
844   // Finish up transaction1, verifying blob writes.
845 
846   EXPECT_TRUE(succeeded);
847   EXPECT_TRUE(CheckBlobWrites());
848   EXPECT_TRUE(transaction1->CommitPhaseTwo().ok());
849 
850   // Initiate transaction2, reading blobs.
851   IndexedDBBackingStore::Transaction transaction2(
852       backing_store()->AsWeakPtr(),
853       blink::mojom::IDBTransactionDurability::Relaxed,
854       blink::mojom::IDBTransactionMode::ReadWrite);
855   transaction2.Begin(CreateDummyLock());
856   IndexedDBValue result_value;
857   EXPECT_TRUE(backing_store()
858                   ->GetRecord(&transaction2, 1, 1, key3_, &result_value)
859                   .ok());
860 
861   // Finish up transaction2, verifying blob reads.
862   succeeded = false;
863   EXPECT_TRUE(
864       transaction2.CommitPhaseOne(CreateBlobWriteCallback(&succeeded)).ok());
865   EXPECT_TRUE(succeeded);
866   EXPECT_TRUE(transaction2.CommitPhaseTwo().ok());
867   EXPECT_EQ(value3_.bits, result_value.bits);
868 
869   task_environment_.RunUntilIdle();
870   EXPECT_TRUE(CheckBlobInfoMatches(result_value.external_objects));
871   EXPECT_TRUE(CheckBlobReadsMatchWrites(result_value.external_objects));
872 
873   // Initiate transaction3, deleting blobs.
874   std::unique_ptr<IndexedDBBackingStore::Transaction> transaction3 =
875       std::make_unique<IndexedDBBackingStore::Transaction>(
876           backing_store()->AsWeakPtr(),
877           blink::mojom::IDBTransactionDurability::Relaxed,
878           blink::mojom::IDBTransactionMode::ReadWrite);
879   transaction3->Begin(CreateDummyLock());
880   EXPECT_TRUE(
881       backing_store()
882           ->DeleteRange(transaction3.get(), 1, 1, IndexedDBKeyRange(key3_))
883           .ok());
884   succeeded = false;
885   EXPECT_TRUE(
886       transaction3->CommitPhaseOne(CreateBlobWriteCallback(&succeeded)).ok());
887   EXPECT_TRUE(succeeded);
888   task_environment_.RunUntilIdle();
889 
890   EXPECT_TRUE(succeeded);
891 
892   // Finish up transaction 3, verifying blob deletes.
893   EXPECT_TRUE(transaction3->CommitPhaseTwo().ok());
894   EXPECT_TRUE(CheckBlobRemovals());
895 
896   // Clean up on the IDB sequence.
897   transaction1.reset();
898   transaction3.reset();
899   task_environment_.RunUntilIdle();
900 }
901 
TEST_P(IndexedDBBackingStoreTestWithExternalObjects,DeleteRange)902 TEST_P(IndexedDBBackingStoreTestWithExternalObjects, DeleteRange) {
903   const std::vector<IndexedDBKey> keys = {
904       IndexedDBKey(ASCIIToUTF16("key0")), IndexedDBKey(ASCIIToUTF16("key1")),
905       IndexedDBKey(ASCIIToUTF16("key2")), IndexedDBKey(ASCIIToUTF16("key3"))};
906   const IndexedDBKeyRange ranges[] = {
907       IndexedDBKeyRange(keys[1], keys[2], false, false),
908       IndexedDBKeyRange(keys[1], keys[2], false, false),
909       IndexedDBKeyRange(keys[0], keys[2], true, false),
910       IndexedDBKeyRange(keys[1], keys[3], false, true),
911       IndexedDBKeyRange(keys[0], keys[3], true, true)};
912 
913   for (size_t i = 0; i < base::size(ranges); ++i) {
914     const int64_t database_id = 1;
915     const int64_t object_store_id = i + 1;
916     const IndexedDBKeyRange& range = ranges[i];
917 
918     std::vector<IndexedDBExternalObject> external_objects;
919     for (size_t j = 0; j < 4; ++j) {
920       std::string type = "type " + base::NumberToString(j);
921       external_objects.push_back(CreateBlobInfo(base::UTF8ToUTF16(type), 1));
922     }
923 
924     // Reset from previous iteration.
925     blob_context_->ClearWrites();
926     native_file_system_context_->ClearWrites();
927     backing_store()->ClearRemovals();
928 
929     std::vector<IndexedDBValue> values = {
930         IndexedDBValue("value0", {external_objects[0]}),
931         IndexedDBValue("value1", {external_objects[1]}),
932         IndexedDBValue("value2", {external_objects[2]}),
933         IndexedDBValue("value3", {external_objects[3]}),
934     };
935     ASSERT_GE(keys.size(), values.size());
936 
937     // Initiate transaction1 - write records.
938     std::unique_ptr<IndexedDBBackingStore::Transaction> transaction1 =
939         std::make_unique<IndexedDBBackingStore::Transaction>(
940             backing_store()->AsWeakPtr(),
941             blink::mojom::IDBTransactionDurability::Relaxed,
942             blink::mojom::IDBTransactionMode::ReadWrite);
943     transaction1->Begin(CreateDummyLock());
944     IndexedDBBackingStore::RecordIdentifier record;
945     for (size_t i = 0; i < values.size(); ++i) {
946       EXPECT_TRUE(backing_store()
947                       ->PutRecord(transaction1.get(), database_id,
948                                   object_store_id, keys[i], &values[i], &record)
949                       .ok());
950     }
951 
952     // Start committing transaction1.
953     bool succeeded = false;
954     EXPECT_TRUE(
955         transaction1->CommitPhaseOne(CreateBlobWriteCallback(&succeeded)).ok());
956     task_environment_.RunUntilIdle();
957 
958     // Finish committing transaction1.
959 
960     EXPECT_TRUE(succeeded);
961     EXPECT_TRUE(transaction1->CommitPhaseTwo().ok());
962 
963     // Initiate transaction 2 - delete range.
964     std::unique_ptr<IndexedDBBackingStore::Transaction> transaction2 =
965         std::make_unique<IndexedDBBackingStore::Transaction>(
966             backing_store()->AsWeakPtr(),
967             blink::mojom::IDBTransactionDurability::Relaxed,
968             blink::mojom::IDBTransactionMode::ReadWrite);
969     transaction2->Begin(CreateDummyLock());
970     IndexedDBValue result_value;
971     EXPECT_TRUE(backing_store()
972                     ->DeleteRange(transaction2.get(), database_id,
973                                   object_store_id, range)
974                     .ok());
975 
976     // Start committing transaction2.
977     succeeded = false;
978     EXPECT_TRUE(
979         transaction2->CommitPhaseOne(CreateBlobWriteCallback(&succeeded)).ok());
980     task_environment_.RunUntilIdle();
981 
982     // Finish committing transaction2.
983 
984     EXPECT_TRUE(succeeded);
985     EXPECT_TRUE(transaction2->CommitPhaseTwo().ok());
986 
987     // Verify blob removals.
988     ASSERT_EQ(2UL, backing_store()->removals().size());
989     EXPECT_EQ(blob_context_->writes()[1].path, backing_store()->removals()[0]);
990     EXPECT_EQ(blob_context_->writes()[2].path, backing_store()->removals()[1]);
991 
992     // Clean up on the IDB sequence.
993     transaction1.reset();
994     transaction2.reset();
995     task_environment_.RunUntilIdle();
996   }
997 }
998 
TEST_P(IndexedDBBackingStoreTestWithExternalObjects,DeleteRangeEmptyRange)999 TEST_P(IndexedDBBackingStoreTestWithExternalObjects, DeleteRangeEmptyRange) {
1000   const std::vector<IndexedDBKey> keys = {
1001       IndexedDBKey(ASCIIToUTF16("key0")), IndexedDBKey(ASCIIToUTF16("key1")),
1002       IndexedDBKey(ASCIIToUTF16("key2")), IndexedDBKey(ASCIIToUTF16("key3")),
1003       IndexedDBKey(ASCIIToUTF16("key4"))};
1004   const IndexedDBKeyRange ranges[] = {
1005       IndexedDBKeyRange(keys[3], keys[4], true, false),
1006       IndexedDBKeyRange(keys[2], keys[1], false, false),
1007       IndexedDBKeyRange(keys[2], keys[1], true, true)};
1008 
1009   for (size_t i = 0; i < base::size(ranges); ++i) {
1010     const int64_t database_id = 1;
1011     const int64_t object_store_id = i + 1;
1012     const IndexedDBKeyRange& range = ranges[i];
1013 
1014     std::vector<IndexedDBExternalObject> external_objects;
1015     for (size_t j = 0; j < 4; ++j) {
1016       std::string type = "type " + base::NumberToString(j);
1017       external_objects.push_back(CreateBlobInfo(base::UTF8ToUTF16(type), 1));
1018     }
1019 
1020     // Reset from previous iteration.
1021     blob_context_->ClearWrites();
1022     native_file_system_context_->ClearWrites();
1023     backing_store()->ClearRemovals();
1024 
1025     std::vector<IndexedDBValue> values = {
1026         IndexedDBValue("value0", {external_objects[0]}),
1027         IndexedDBValue("value1", {external_objects[1]}),
1028         IndexedDBValue("value2", {external_objects[2]}),
1029         IndexedDBValue("value3", {external_objects[3]}),
1030     };
1031     ASSERT_GE(keys.size(), values.size());
1032 
1033     // Initiate transaction1 - write records.
1034     std::unique_ptr<IndexedDBBackingStore::Transaction> transaction1 =
1035         std::make_unique<IndexedDBBackingStore::Transaction>(
1036             backing_store()->AsWeakPtr(),
1037             blink::mojom::IDBTransactionDurability::Relaxed,
1038             blink::mojom::IDBTransactionMode::ReadWrite);
1039     transaction1->Begin(CreateDummyLock());
1040 
1041     IndexedDBBackingStore::RecordIdentifier record;
1042     for (size_t i = 0; i < values.size(); ++i) {
1043       EXPECT_TRUE(backing_store()
1044                       ->PutRecord(transaction1.get(), database_id,
1045                                   object_store_id, keys[i], &values[i], &record)
1046                       .ok());
1047     }
1048     // Start committing transaction1.
1049     bool succeeded = false;
1050     EXPECT_TRUE(
1051         transaction1->CommitPhaseOne(CreateBlobWriteCallback(&succeeded)).ok());
1052     task_environment_.RunUntilIdle();
1053 
1054     // Finish committing transaction1.
1055     EXPECT_TRUE(succeeded);
1056     EXPECT_TRUE(transaction1->CommitPhaseTwo().ok());
1057 
1058     // Initiate transaction 2 - delete range.
1059     std::unique_ptr<IndexedDBBackingStore::Transaction> transaction2 =
1060         std::make_unique<IndexedDBBackingStore::Transaction>(
1061             backing_store()->AsWeakPtr(),
1062             blink::mojom::IDBTransactionDurability::Relaxed,
1063             blink::mojom::IDBTransactionMode::ReadWrite);
1064     transaction2->Begin(CreateDummyLock());
1065     IndexedDBValue result_value;
1066     EXPECT_TRUE(backing_store()
1067                     ->DeleteRange(transaction2.get(), database_id,
1068                                   object_store_id, range)
1069                     .ok());
1070 
1071     // Start committing transaction2.
1072     succeeded = false;
1073     EXPECT_TRUE(
1074         transaction2->CommitPhaseOne(CreateBlobWriteCallback(&succeeded)).ok());
1075     task_environment_.RunUntilIdle();
1076 
1077     // Finish committing transaction2.
1078     EXPECT_TRUE(succeeded);
1079     EXPECT_TRUE(transaction2->CommitPhaseTwo().ok());
1080 
1081     // Verify blob removals.
1082     EXPECT_EQ(0UL, backing_store()->removals().size());
1083 
1084     // Clean on the IDB sequence.
1085     transaction1.reset();
1086     transaction2.reset();
1087     task_environment_.RunUntilIdle();
1088   }
1089 }
1090 
TEST_P(IndexedDBBackingStoreTestWithExternalObjects,BlobJournalInterleavedTransactions)1091 TEST_P(IndexedDBBackingStoreTestWithExternalObjects,
1092        BlobJournalInterleavedTransactions) {
1093   // Initiate transaction1.
1094   std::unique_ptr<IndexedDBBackingStore::Transaction> transaction1 =
1095       std::make_unique<IndexedDBBackingStore::Transaction>(
1096           backing_store()->AsWeakPtr(),
1097           blink::mojom::IDBTransactionDurability::Relaxed,
1098           blink::mojom::IDBTransactionMode::ReadWrite);
1099   transaction1->Begin(CreateDummyLock());
1100   IndexedDBBackingStore::RecordIdentifier record1;
1101   EXPECT_TRUE(
1102       backing_store()
1103           ->PutRecord(transaction1.get(), 1, 1, key3_, &value3_, &record1)
1104           .ok());
1105   bool succeeded = false;
1106   EXPECT_TRUE(
1107       transaction1->CommitPhaseOne(CreateBlobWriteCallback(&succeeded)).ok());
1108   task_environment_.RunUntilIdle();
1109 
1110   // Verify transaction1 phase one completed.
1111 
1112   EXPECT_TRUE(succeeded);
1113   EXPECT_TRUE(CheckBlobWrites());
1114   EXPECT_EQ(0U, backing_store()->removals().size());
1115 
1116   // Initiate transaction2.
1117   std::unique_ptr<IndexedDBBackingStore::Transaction> transaction2 =
1118       std::make_unique<IndexedDBBackingStore::Transaction>(
1119           backing_store()->AsWeakPtr(),
1120           blink::mojom::IDBTransactionDurability::Relaxed,
1121           blink::mojom::IDBTransactionMode::ReadWrite);
1122   transaction2->Begin(CreateDummyLock());
1123   IndexedDBBackingStore::RecordIdentifier record2;
1124   EXPECT_TRUE(
1125       backing_store()
1126           ->PutRecord(transaction2.get(), 1, 1, key1_, &value1_, &record2)
1127           .ok());
1128   succeeded = false;
1129   EXPECT_TRUE(
1130       transaction2->CommitPhaseOne(CreateBlobWriteCallback(&succeeded)).ok());
1131   task_environment_.RunUntilIdle();
1132 
1133   // Verify transaction2 phase one completed.
1134   EXPECT_TRUE(succeeded);
1135   EXPECT_TRUE(CheckBlobWrites());
1136   EXPECT_EQ(0U, backing_store()->removals().size());
1137 
1138   // Finalize both transactions.
1139   EXPECT_TRUE(transaction1->CommitPhaseTwo().ok());
1140   EXPECT_EQ(0U, backing_store()->removals().size());
1141 
1142   EXPECT_TRUE(transaction2->CommitPhaseTwo().ok());
1143   EXPECT_EQ(0U, backing_store()->removals().size());
1144 
1145   // Clean up on the IDB sequence.
1146   transaction1.reset();
1147   transaction2.reset();
1148   task_environment_.RunUntilIdle();
1149 }
1150 
TEST_P(IndexedDBBackingStoreTestWithExternalObjects,ActiveBlobJournal)1151 TEST_P(IndexedDBBackingStoreTestWithExternalObjects, ActiveBlobJournal) {
1152   std::unique_ptr<IndexedDBBackingStore::Transaction> transaction1 =
1153       std::make_unique<IndexedDBBackingStore::Transaction>(
1154           backing_store()->AsWeakPtr(),
1155           blink::mojom::IDBTransactionDurability::Relaxed,
1156           blink::mojom::IDBTransactionMode::ReadWrite);
1157   transaction1->Begin(CreateDummyLock());
1158   IndexedDBBackingStore::RecordIdentifier record;
1159   EXPECT_TRUE(
1160       backing_store()
1161           ->PutRecord(transaction1.get(), 1, 1, key3_, &value3_, &record)
1162           .ok());
1163   bool succeeded = false;
1164   EXPECT_TRUE(
1165       transaction1->CommitPhaseOne(CreateBlobWriteCallback(&succeeded)).ok());
1166 
1167   task_environment_.RunUntilIdle();
1168 
1169   EXPECT_TRUE(succeeded);
1170   EXPECT_TRUE(CheckBlobWrites());
1171   EXPECT_TRUE(transaction1->CommitPhaseTwo().ok());
1172 
1173   IndexedDBBackingStore::Transaction transaction2(
1174       backing_store()->AsWeakPtr(),
1175       blink::mojom::IDBTransactionDurability::Relaxed,
1176       blink::mojom::IDBTransactionMode::ReadWrite);
1177   transaction2.Begin(CreateDummyLock());
1178   IndexedDBValue read_result_value;
1179   EXPECT_TRUE(backing_store()
1180                   ->GetRecord(&transaction2, 1, 1, key3_, &read_result_value)
1181                   .ok());
1182   succeeded = false;
1183 
1184   EXPECT_TRUE(
1185       transaction2.CommitPhaseOne(CreateBlobWriteCallback(&succeeded)).ok());
1186 
1187   EXPECT_TRUE(succeeded);
1188   EXPECT_TRUE(transaction2.CommitPhaseTwo().ok());
1189   EXPECT_EQ(value3_.bits, read_result_value.bits);
1190   EXPECT_TRUE(CheckBlobInfoMatches(read_result_value.external_objects));
1191   EXPECT_TRUE(CheckBlobReadsMatchWrites(read_result_value.external_objects));
1192   for (size_t i = 0; i < read_result_value.external_objects.size(); ++i) {
1193     if (read_result_value.external_objects[i].mark_used_callback())
1194       read_result_value.external_objects[i].mark_used_callback().Run();
1195   }
1196 
1197   std::unique_ptr<IndexedDBBackingStore::Transaction> transaction3 =
1198       std::make_unique<IndexedDBBackingStore::Transaction>(
1199           backing_store()->AsWeakPtr(),
1200           blink::mojom::IDBTransactionDurability::Relaxed,
1201           blink::mojom::IDBTransactionMode::ReadWrite);
1202   transaction3->Begin(CreateDummyLock());
1203   EXPECT_TRUE(
1204       backing_store()
1205           ->DeleteRange(transaction3.get(), 1, 1, IndexedDBKeyRange(key3_))
1206           .ok());
1207   succeeded = false;
1208   EXPECT_TRUE(
1209       transaction3->CommitPhaseOne(CreateBlobWriteCallback(&succeeded)).ok());
1210   task_environment_.RunUntilIdle();
1211 
1212   EXPECT_TRUE(succeeded);
1213   EXPECT_TRUE(transaction3->CommitPhaseTwo().ok());
1214   EXPECT_EQ(0U, backing_store()->removals().size());
1215   for (size_t i = 0; i < read_result_value.external_objects.size(); ++i) {
1216     if (read_result_value.external_objects[i].release_callback())
1217       read_result_value.external_objects[i].release_callback().Run();
1218   }
1219   task_environment_.RunUntilIdle();
1220 
1221   if (TestType() != ExternalObjectTestType::kOnlyNativeFileSystemHandles) {
1222     EXPECT_TRUE(backing_store()->IsBlobCleanupPending());
1223 #if DCHECK_IS_ON()
1224   EXPECT_EQ(3,
1225             backing_store()->NumAggregatedJournalCleaningRequestsForTesting());
1226 #endif
1227   for (int i = 3; i < IndexedDBBackingStore::kMaxJournalCleanRequests; ++i) {
1228     backing_store()->StartJournalCleaningTimer();
1229   }
1230   EXPECT_NE(0U, backing_store()->removals().size());
1231   EXPECT_TRUE(CheckBlobRemovals());
1232 #if DCHECK_IS_ON()
1233   EXPECT_EQ(3, backing_store()->NumBlobFilesDeletedForTesting());
1234 #endif
1235   }
1236 
1237   EXPECT_FALSE(backing_store()->IsBlobCleanupPending());
1238 
1239   // Clean on the IDB sequence.
1240   transaction1.reset();
1241   transaction3.reset();
1242   task_environment_.RunUntilIdle();
1243 }
1244 
1245 // Make sure that using very high ( more than 32 bit ) values for
1246 // database_id and object_store_id still work.
TEST_F(IndexedDBBackingStoreTest,HighIds)1247 TEST_F(IndexedDBBackingStoreTest, HighIds) {
1248   IndexedDBKey key1 = key1_;
1249   IndexedDBKey key2 = key2_;
1250   IndexedDBValue value1 = value1_;
1251 
1252   const int64_t high_database_id = 1ULL << 35;
1253   const int64_t high_object_store_id = 1ULL << 39;
1254   // index_ids are capped at 32 bits for storage purposes.
1255   const int64_t high_index_id = 1ULL << 29;
1256 
1257   const int64_t invalid_high_index_id = 1ULL << 37;
1258 
1259   const IndexedDBKey& index_key = key2;
1260   std::string index_key_raw;
1261   EncodeIDBKey(index_key, &index_key_raw);
1262   {
1263     IndexedDBBackingStore::Transaction transaction1(
1264         backing_store()->AsWeakPtr(),
1265         blink::mojom::IDBTransactionDurability::Relaxed,
1266         blink::mojom::IDBTransactionMode::ReadWrite);
1267     transaction1.Begin(CreateDummyLock());
1268     IndexedDBBackingStore::RecordIdentifier record;
1269     leveldb::Status s = backing_store()->PutRecord(
1270         &transaction1, high_database_id, high_object_store_id, key1, &value1,
1271         &record);
1272     EXPECT_TRUE(s.ok());
1273 
1274     s = backing_store()->PutIndexDataForRecord(
1275         &transaction1, high_database_id, high_object_store_id,
1276         invalid_high_index_id, index_key, record);
1277     EXPECT_FALSE(s.ok());
1278 
1279     s = backing_store()->PutIndexDataForRecord(
1280         &transaction1, high_database_id, high_object_store_id, high_index_id,
1281         index_key, record);
1282     EXPECT_TRUE(s.ok());
1283 
1284     bool succeeded = false;
1285     EXPECT_TRUE(
1286         transaction1.CommitPhaseOne(CreateBlobWriteCallback(&succeeded)).ok());
1287     EXPECT_TRUE(succeeded);
1288     EXPECT_TRUE(transaction1.CommitPhaseTwo().ok());
1289   }
1290 
1291   {
1292     IndexedDBBackingStore::Transaction transaction2(
1293         backing_store()->AsWeakPtr(),
1294         blink::mojom::IDBTransactionDurability::Relaxed,
1295         blink::mojom::IDBTransactionMode::ReadWrite);
1296     transaction2.Begin(CreateDummyLock());
1297     IndexedDBValue result_value;
1298     leveldb::Status s =
1299         backing_store()->GetRecord(&transaction2, high_database_id,
1300                                    high_object_store_id, key1, &result_value);
1301     EXPECT_TRUE(s.ok());
1302     EXPECT_EQ(value1.bits, result_value.bits);
1303 
1304     std::unique_ptr<IndexedDBKey> new_primary_key;
1305     s = backing_store()->GetPrimaryKeyViaIndex(
1306         &transaction2, high_database_id, high_object_store_id,
1307         invalid_high_index_id, index_key, &new_primary_key);
1308     EXPECT_FALSE(s.ok());
1309 
1310     s = backing_store()->GetPrimaryKeyViaIndex(
1311         &transaction2, high_database_id, high_object_store_id, high_index_id,
1312         index_key, &new_primary_key);
1313     EXPECT_TRUE(s.ok());
1314     EXPECT_TRUE(new_primary_key->Equals(key1));
1315 
1316     bool succeeded = false;
1317     EXPECT_TRUE(
1318         transaction2.CommitPhaseOne(CreateBlobWriteCallback(&succeeded)).ok());
1319     EXPECT_TRUE(succeeded);
1320     EXPECT_TRUE(transaction2.CommitPhaseTwo().ok());
1321   }
1322 
1323   CycleIDBTaskRunner();
1324 }
1325 
1326 // Make sure that other invalid ids do not crash.
TEST_F(IndexedDBBackingStoreTest,InvalidIds)1327 TEST_F(IndexedDBBackingStoreTest, InvalidIds) {
1328   base::RunLoop loop;
1329   idb_context_->IDBTaskRunner()->PostTask(
1330       FROM_HERE, base::BindLambdaForTesting([&]() {
1331         const IndexedDBKey key = key1_;
1332         IndexedDBValue value = value1_;
1333 
1334         // valid ids for use when testing invalid ids
1335         const int64_t database_id = 1;
1336         const int64_t object_store_id = 1;
1337         const int64_t index_id = kMinimumIndexId;
1338         // index_ids must be > kMinimumIndexId
1339         const int64_t invalid_low_index_id = 19;
1340         IndexedDBValue result_value;
1341 
1342         IndexedDBBackingStore::Transaction transaction1(
1343             backing_store()->AsWeakPtr(),
1344             blink::mojom::IDBTransactionDurability::Relaxed,
1345             blink::mojom::IDBTransactionMode::ReadWrite);
1346         transaction1.Begin(CreateDummyLock());
1347 
1348         IndexedDBBackingStore::RecordIdentifier record;
1349         leveldb::Status s = backing_store()->PutRecord(
1350             &transaction1, database_id, KeyPrefix::kInvalidId, key, &value,
1351             &record);
1352         EXPECT_FALSE(s.ok());
1353         s = backing_store()->PutRecord(&transaction1, database_id, 0, key,
1354                                        &value, &record);
1355         EXPECT_FALSE(s.ok());
1356         s = backing_store()->PutRecord(&transaction1, KeyPrefix::kInvalidId,
1357                                        object_store_id, key, &value, &record);
1358         EXPECT_FALSE(s.ok());
1359         s = backing_store()->PutRecord(&transaction1, 0, object_store_id, key,
1360                                        &value, &record);
1361         EXPECT_FALSE(s.ok());
1362 
1363         s = backing_store()->GetRecord(&transaction1, database_id,
1364                                        KeyPrefix::kInvalidId, key,
1365                                        &result_value);
1366         EXPECT_FALSE(s.ok());
1367         s = backing_store()->GetRecord(&transaction1, database_id, 0, key,
1368                                        &result_value);
1369         EXPECT_FALSE(s.ok());
1370         s = backing_store()->GetRecord(&transaction1, KeyPrefix::kInvalidId,
1371                                        object_store_id, key, &result_value);
1372         EXPECT_FALSE(s.ok());
1373         s = backing_store()->GetRecord(&transaction1, 0, object_store_id, key,
1374                                        &result_value);
1375         EXPECT_FALSE(s.ok());
1376 
1377         std::unique_ptr<IndexedDBKey> new_primary_key;
1378         s = backing_store()->GetPrimaryKeyViaIndex(
1379             &transaction1, database_id, object_store_id, KeyPrefix::kInvalidId,
1380             key, &new_primary_key);
1381         EXPECT_FALSE(s.ok());
1382         s = backing_store()->GetPrimaryKeyViaIndex(
1383             &transaction1, database_id, object_store_id, invalid_low_index_id,
1384             key, &new_primary_key);
1385         EXPECT_FALSE(s.ok());
1386         s = backing_store()->GetPrimaryKeyViaIndex(&transaction1, database_id,
1387                                                    object_store_id, 0, key,
1388                                                    &new_primary_key);
1389         EXPECT_FALSE(s.ok());
1390 
1391         s = backing_store()->GetPrimaryKeyViaIndex(
1392             &transaction1, KeyPrefix::kInvalidId, object_store_id, index_id,
1393             key, &new_primary_key);
1394         EXPECT_FALSE(s.ok());
1395         s = backing_store()->GetPrimaryKeyViaIndex(
1396             &transaction1, database_id, KeyPrefix::kInvalidId, index_id, key,
1397             &new_primary_key);
1398         EXPECT_FALSE(s.ok());
1399         loop.Quit();
1400       }));
1401   loop.Run();
1402 }
1403 
TEST_F(IndexedDBBackingStoreTest,CreateDatabase)1404 TEST_F(IndexedDBBackingStoreTest, CreateDatabase) {
1405   base::RunLoop loop;
1406   idb_context_->IDBTaskRunner()->PostTask(
1407       FROM_HERE, base::BindLambdaForTesting([&]() {
1408         const base::string16 database_name(ASCIIToUTF16("db1"));
1409         int64_t database_id;
1410         const int64_t version = 9;
1411 
1412         const int64_t object_store_id = 99;
1413         const base::string16 object_store_name(ASCIIToUTF16("object_store1"));
1414         const bool auto_increment = true;
1415         const IndexedDBKeyPath object_store_key_path(
1416             ASCIIToUTF16("object_store_key"));
1417 
1418         const int64_t index_id = 999;
1419         const base::string16 index_name(ASCIIToUTF16("index1"));
1420         const bool unique = true;
1421         const bool multi_entry = true;
1422         const IndexedDBKeyPath index_key_path(ASCIIToUTF16("index_key"));
1423 
1424         IndexedDBMetadataCoding metadata_coding;
1425 
1426         {
1427           IndexedDBDatabaseMetadata database;
1428           leveldb::Status s = metadata_coding.CreateDatabase(
1429               backing_store()->db(), backing_store()->origin_identifier(),
1430               database_name, version, &database);
1431           EXPECT_TRUE(s.ok());
1432           EXPECT_GT(database.id, 0);
1433           database_id = database.id;
1434 
1435           IndexedDBBackingStore::Transaction transaction(
1436               backing_store()->AsWeakPtr(),
1437               blink::mojom::IDBTransactionDurability::Relaxed,
1438               blink::mojom::IDBTransactionMode::ReadWrite);
1439           transaction.Begin(CreateDummyLock());
1440 
1441           IndexedDBObjectStoreMetadata object_store;
1442           s = metadata_coding.CreateObjectStore(
1443               transaction.transaction(), database.id, object_store_id,
1444               object_store_name, object_store_key_path, auto_increment,
1445               &object_store);
1446           EXPECT_TRUE(s.ok());
1447 
1448           IndexedDBIndexMetadata index;
1449           s = metadata_coding.CreateIndex(
1450               transaction.transaction(), database.id, object_store.id, index_id,
1451               index_name, index_key_path, unique, multi_entry, &index);
1452           EXPECT_TRUE(s.ok());
1453 
1454           bool succeeded = false;
1455           EXPECT_TRUE(
1456               transaction.CommitPhaseOne(CreateBlobWriteCallback(&succeeded))
1457                   .ok());
1458           EXPECT_TRUE(succeeded);
1459           EXPECT_TRUE(transaction.CommitPhaseTwo().ok());
1460         }
1461 
1462         {
1463           IndexedDBDatabaseMetadata database;
1464           bool found;
1465           leveldb::Status s = metadata_coding.ReadMetadataForDatabaseName(
1466               backing_store()->db(), backing_store()->origin_identifier(),
1467               database_name, &database, &found);
1468           EXPECT_TRUE(s.ok());
1469           EXPECT_TRUE(found);
1470 
1471           // database.name is not filled in by the implementation.
1472           EXPECT_EQ(version, database.version);
1473           EXPECT_EQ(database_id, database.id);
1474 
1475           EXPECT_EQ(1UL, database.object_stores.size());
1476           IndexedDBObjectStoreMetadata object_store =
1477               database.object_stores[object_store_id];
1478           EXPECT_EQ(object_store_name, object_store.name);
1479           EXPECT_EQ(object_store_key_path, object_store.key_path);
1480           EXPECT_EQ(auto_increment, object_store.auto_increment);
1481 
1482           EXPECT_EQ(1UL, object_store.indexes.size());
1483           IndexedDBIndexMetadata index = object_store.indexes[index_id];
1484           EXPECT_EQ(index_name, index.name);
1485           EXPECT_EQ(index_key_path, index.key_path);
1486           EXPECT_EQ(unique, index.unique);
1487           EXPECT_EQ(multi_entry, index.multi_entry);
1488         }
1489         loop.Quit();
1490       }));
1491   loop.Run();
1492 
1493   {
1494     // Cycle the idb runner to help clean up tasks for the Windows tests.
1495     base::RunLoop cycle_loop;
1496     idb_context_->IDBTaskRunner()->PostTask(FROM_HERE,
1497                                             cycle_loop.QuitClosure());
1498     cycle_loop.Run();
1499   }
1500 }
1501 
TEST_F(IndexedDBBackingStoreTest,GetDatabaseNames)1502 TEST_F(IndexedDBBackingStoreTest, GetDatabaseNames) {
1503   const base::string16 db1_name(ASCIIToUTF16("db1"));
1504   const int64_t db1_version = 1LL;
1505 
1506   // Database records with DEFAULT_VERSION represent
1507   // stale data, and should not be enumerated.
1508   const base::string16 db2_name(ASCIIToUTF16("db2"));
1509   const int64_t db2_version = IndexedDBDatabaseMetadata::DEFAULT_VERSION;
1510   IndexedDBMetadataCoding metadata_coding;
1511 
1512   IndexedDBDatabaseMetadata db1;
1513   leveldb::Status s = metadata_coding.CreateDatabase(
1514       backing_store()->db(), backing_store()->origin_identifier(), db1_name,
1515       db1_version, &db1);
1516   EXPECT_TRUE(s.ok());
1517   EXPECT_GT(db1.id, 0LL);
1518 
1519   IndexedDBDatabaseMetadata db2;
1520   s = metadata_coding.CreateDatabase(backing_store()->db(),
1521                                      backing_store()->origin_identifier(),
1522                                      db2_name, db2_version, &db2);
1523   EXPECT_TRUE(s.ok());
1524   EXPECT_GT(db2.id, db1.id);
1525 
1526   std::vector<base::string16> names;
1527   s = metadata_coding.ReadDatabaseNames(
1528       backing_store()->db(), backing_store()->origin_identifier(), &names);
1529   EXPECT_TRUE(s.ok());
1530   ASSERT_EQ(1U, names.size());
1531   EXPECT_EQ(db1_name, names[0]);
1532 }
1533 
TEST_F(IndexedDBBackingStoreTest,ReadCorruptionInfo)1534 TEST_F(IndexedDBBackingStoreTest, ReadCorruptionInfo) {
1535   // No |path_base|.
1536   EXPECT_TRUE(
1537       indexed_db::ReadCorruptionInfo(base::FilePath(), Origin()).empty());
1538 
1539   const base::FilePath path_base = temp_dir_.GetPath();
1540   const Origin origin = Origin::Create(GURL("http://www.google.com/"));
1541   ASSERT_FALSE(path_base.empty());
1542   ASSERT_TRUE(PathIsWritable(path_base));
1543 
1544   // File not found.
1545   EXPECT_TRUE(indexed_db::ReadCorruptionInfo(path_base, origin).empty());
1546 
1547   const base::FilePath info_path =
1548       path_base.AppendASCII("http_www.google.com_0.indexeddb.leveldb")
1549           .AppendASCII("corruption_info.json");
1550   ASSERT_TRUE(CreateDirectory(info_path.DirName()));
1551 
1552   // Empty file.
1553   std::string dummy_data;
1554   ASSERT_TRUE(WriteFile(info_path, dummy_data));
1555   EXPECT_TRUE(indexed_db::ReadCorruptionInfo(path_base, origin).empty());
1556   EXPECT_FALSE(PathExists(info_path));
1557 
1558   // File size > 4 KB.
1559   dummy_data.resize(5000, 'c');
1560   ASSERT_TRUE(WriteFile(info_path, dummy_data));
1561   EXPECT_TRUE(indexed_db::ReadCorruptionInfo(path_base, origin).empty());
1562   EXPECT_FALSE(PathExists(info_path));
1563 
1564   // Random string.
1565   ASSERT_TRUE(WriteFile(info_path, "foo bar"));
1566   EXPECT_TRUE(indexed_db::ReadCorruptionInfo(path_base, origin).empty());
1567   EXPECT_FALSE(PathExists(info_path));
1568 
1569   // Not a dictionary.
1570   ASSERT_TRUE(WriteFile(info_path, "[]"));
1571   EXPECT_TRUE(indexed_db::ReadCorruptionInfo(path_base, origin).empty());
1572   EXPECT_FALSE(PathExists(info_path));
1573 
1574   // Empty dictionary.
1575   ASSERT_TRUE(WriteFile(info_path, "{}"));
1576   EXPECT_TRUE(indexed_db::ReadCorruptionInfo(path_base, origin).empty());
1577   EXPECT_FALSE(PathExists(info_path));
1578 
1579   // Dictionary, no message key.
1580   ASSERT_TRUE(WriteFile(info_path, "{\"foo\":\"bar\"}"));
1581   EXPECT_TRUE(indexed_db::ReadCorruptionInfo(path_base, origin).empty());
1582   EXPECT_FALSE(PathExists(info_path));
1583 
1584   // Dictionary, message key.
1585   ASSERT_TRUE(WriteFile(info_path, "{\"message\":\"bar\"}"));
1586   std::string message = indexed_db::ReadCorruptionInfo(path_base, origin);
1587   EXPECT_FALSE(message.empty());
1588   EXPECT_FALSE(PathExists(info_path));
1589   EXPECT_EQ("bar", message);
1590 
1591   // Dictionary, message key and more.
1592   ASSERT_TRUE(WriteFile(info_path, "{\"message\":\"foo\",\"bar\":5}"));
1593   message = indexed_db::ReadCorruptionInfo(path_base, origin);
1594   EXPECT_FALSE(message.empty());
1595   EXPECT_FALSE(PathExists(info_path));
1596   EXPECT_EQ("foo", message);
1597 }
1598 
1599 // There was a wrong migration from schema 2 to 3, which always delete IDB
1600 // blobs and doesn't actually write the new schema version. This tests the
1601 // upgrade path where the database doesn't have blob entries, so it' safe to
1602 // keep the database.
1603 // https://crbug.com/756447, https://crbug.com/829125, https://crbug.com/829141
TEST_F(IndexedDBBackingStoreTest,SchemaUpgradeWithoutBlobsSurvives)1604 TEST_F(IndexedDBBackingStoreTest, SchemaUpgradeWithoutBlobsSurvives) {
1605   int64_t database_id;
1606   const int64_t object_store_id = 99;
1607 
1608   // The database metadata needs to be written so we can verify the blob entry
1609   // keys are not detected.
1610   const base::string16 database_name(ASCIIToUTF16("db1"));
1611   const int64_t version = 9;
1612 
1613   const base::string16 object_store_name(ASCIIToUTF16("object_store1"));
1614   const bool auto_increment = true;
1615   const IndexedDBKeyPath object_store_key_path(
1616       ASCIIToUTF16("object_store_key"));
1617 
1618   IndexedDBMetadataCoding metadata_coding;
1619 
1620   {
1621     IndexedDBDatabaseMetadata database;
1622     leveldb::Status s = metadata_coding.CreateDatabase(
1623         backing_store()->db(), backing_store()->origin_identifier(),
1624         database_name, version, &database);
1625     EXPECT_TRUE(s.ok());
1626     EXPECT_GT(database.id, 0);
1627     database_id = database.id;
1628 
1629     IndexedDBBackingStore::Transaction transaction(
1630         backing_store()->AsWeakPtr(),
1631         blink::mojom::IDBTransactionDurability::Relaxed,
1632         blink::mojom::IDBTransactionMode::ReadWrite);
1633     transaction.Begin(CreateDummyLock());
1634 
1635     IndexedDBObjectStoreMetadata object_store;
1636     s = metadata_coding.CreateObjectStore(
1637         transaction.transaction(), database.id, object_store_id,
1638         object_store_name, object_store_key_path, auto_increment,
1639         &object_store);
1640     EXPECT_TRUE(s.ok());
1641 
1642     bool succeeded = false;
1643     EXPECT_TRUE(
1644         transaction.CommitPhaseOne(CreateBlobWriteCallback(&succeeded)).ok());
1645     EXPECT_TRUE(succeeded);
1646     EXPECT_TRUE(transaction.CommitPhaseTwo().ok());
1647   }
1648   task_environment_.RunUntilIdle();
1649 
1650   // Save a value.
1651   IndexedDBBackingStore::Transaction transaction1(
1652       backing_store()->AsWeakPtr(),
1653       blink::mojom::IDBTransactionDurability::Relaxed,
1654       blink::mojom::IDBTransactionMode::ReadWrite);
1655   transaction1.Begin(CreateDummyLock());
1656   IndexedDBBackingStore::RecordIdentifier record;
1657   leveldb::Status s = backing_store()->PutRecord(
1658       &transaction1, database_id, object_store_id, key1_, &value1_, &record);
1659   EXPECT_TRUE(s.ok());
1660   bool succeeded = false;
1661   EXPECT_TRUE(
1662       transaction1.CommitPhaseOne(CreateBlobWriteCallback(&succeeded)).ok());
1663   EXPECT_TRUE(succeeded);
1664   EXPECT_TRUE(transaction1.CommitPhaseTwo().ok());
1665 
1666   // Set the schema to 2, which was before blob support.
1667   std::unique_ptr<LevelDBWriteBatch> write_batch = LevelDBWriteBatch::Create();
1668   const std::string schema_version_key = SchemaVersionKey::Encode();
1669   ignore_result(indexed_db::PutInt(write_batch.get(), schema_version_key, 2));
1670   ASSERT_TRUE(backing_store()->db()->Write(write_batch.get()).ok());
1671   task_environment_.RunUntilIdle();
1672 
1673   DestroyFactoryAndBackingStore();
1674   CreateFactoryAndBackingStore();
1675 
1676   IndexedDBBackingStore::Transaction transaction2(
1677       backing_store()->AsWeakPtr(),
1678       blink::mojom::IDBTransactionDurability::Relaxed,
1679       blink::mojom::IDBTransactionMode::ReadWrite);
1680   transaction2.Begin(CreateDummyLock());
1681   IndexedDBValue result_value;
1682   EXPECT_TRUE(backing_store()
1683                   ->GetRecord(&transaction2, database_id, object_store_id,
1684                               key1_, &result_value)
1685                   .ok());
1686   succeeded = false;
1687   EXPECT_TRUE(
1688       transaction2.CommitPhaseOne(CreateBlobWriteCallback(&succeeded)).ok());
1689   EXPECT_TRUE(succeeded);
1690   EXPECT_TRUE(transaction2.CommitPhaseTwo().ok());
1691   EXPECT_EQ(value1_.bits, result_value.bits);
1692 
1693   // Test that we upgraded.
1694   int64_t found_int = 0;
1695   bool found = false;
1696   bool success = indexed_db::GetInt(backing_store()->db(), schema_version_key,
1697                                     &found_int, &found)
1698                      .ok();
1699   ASSERT_TRUE(success);
1700 
1701   EXPECT_TRUE(found);
1702   EXPECT_EQ(4, found_int);
1703   task_environment_.RunUntilIdle();
1704 }
1705 
1706 // Our v2->v3 schema migration code forgot to bump the on-disk version number.
1707 // This test covers migrating a v3 database mislabeled as v2 to a properly
1708 // labeled v3 database. When the mislabeled database has blob entries, we must
1709 // treat it as corrupt and delete it.
1710 // https://crbug.com/756447, https://crbug.com/829125, https://crbug.com/829141
TEST_F(IndexedDBBackingStoreTestWithBlobs,SchemaUpgradeWithBlobsCorrupt)1711 TEST_F(IndexedDBBackingStoreTestWithBlobs, SchemaUpgradeWithBlobsCorrupt) {
1712   int64_t database_id;
1713   const int64_t object_store_id = 99;
1714 
1715   // The database metadata needs to be written so the blob entry keys can
1716   // be detected.
1717   const base::string16 database_name(ASCIIToUTF16("db1"));
1718   const int64_t version = 9;
1719 
1720   const base::string16 object_store_name(ASCIIToUTF16("object_store1"));
1721   const bool auto_increment = true;
1722   const IndexedDBKeyPath object_store_key_path(
1723       ASCIIToUTF16("object_store_key"));
1724 
1725   IndexedDBMetadataCoding metadata_coding;
1726 
1727   {
1728     IndexedDBDatabaseMetadata database;
1729     leveldb::Status s = metadata_coding.CreateDatabase(
1730         backing_store()->db(), backing_store()->origin_identifier(),
1731         database_name, version, &database);
1732     EXPECT_TRUE(s.ok());
1733     EXPECT_GT(database.id, 0);
1734     database_id = database.id;
1735 
1736     IndexedDBBackingStore::Transaction transaction(
1737         backing_store()->AsWeakPtr(),
1738         blink::mojom::IDBTransactionDurability::Relaxed,
1739         blink::mojom::IDBTransactionMode::ReadWrite);
1740     transaction.Begin(CreateDummyLock());
1741 
1742     IndexedDBObjectStoreMetadata object_store;
1743     s = metadata_coding.CreateObjectStore(
1744         transaction.transaction(), database.id, object_store_id,
1745         object_store_name, object_store_key_path, auto_increment,
1746         &object_store);
1747     EXPECT_TRUE(s.ok());
1748 
1749     bool succeeded = false;
1750     EXPECT_TRUE(
1751         transaction.CommitPhaseOne(CreateBlobWriteCallback(&succeeded)).ok());
1752     EXPECT_TRUE(succeeded);
1753     EXPECT_TRUE(transaction.CommitPhaseTwo().ok());
1754   }
1755   task_environment_.RunUntilIdle();
1756 
1757   base::RunLoop write_blobs_loop;
1758   // Initiate transaction1 - writing blobs.
1759   std::unique_ptr<IndexedDBBackingStore::Transaction> transaction1 =
1760       std::make_unique<IndexedDBBackingStore::Transaction>(
1761           backing_store()->AsWeakPtr(),
1762           blink::mojom::IDBTransactionDurability::Relaxed,
1763           blink::mojom::IDBTransactionMode::ReadWrite);
1764   transaction1->Begin(CreateDummyLock());
1765   IndexedDBBackingStore::RecordIdentifier record;
1766   EXPECT_TRUE(backing_store()
1767                   ->PutRecord(transaction1.get(), database_id, object_store_id,
1768                               key3_, &value3_, &record)
1769                   .ok());
1770   bool succeeded = false;
1771   EXPECT_TRUE(transaction1
1772                   ->CommitPhaseOne(CreateBlobWriteCallback(
1773                       &succeeded, write_blobs_loop.QuitClosure()))
1774                   .ok());
1775   task_environment_.RunUntilIdle();
1776   write_blobs_loop.Run();
1777 
1778   // Finish up transaction1, verifying blob writes.
1779   EXPECT_TRUE(succeeded);
1780   EXPECT_TRUE(CheckBlobWrites());
1781   EXPECT_TRUE(transaction1->CommitPhaseTwo().ok());
1782 
1783   // Set the schema to 2, which was before blob support.
1784   std::unique_ptr<LevelDBWriteBatch> write_batch = LevelDBWriteBatch::Create();
1785   const std::string schema_version_key = SchemaVersionKey::Encode();
1786   ignore_result(indexed_db::PutInt(write_batch.get(), schema_version_key, 2));
1787   ASSERT_TRUE(backing_store()->db()->Write(write_batch.get()).ok());
1788 
1789   // Clean up on the IDB sequence.
1790   transaction1.reset();
1791   task_environment_.RunUntilIdle();
1792 
1793   DestroyFactoryAndBackingStore();
1794   CreateFactoryAndBackingStore();
1795 
1796   // The factory returns a null backing store pointer when there is a corrupt
1797   // database.
1798   EXPECT_TRUE(data_loss_info_.status == blink::mojom::IDBDataLoss::Total);
1799 }
1800 
1801 namespace {
1802 
1803 // v3 Blob Data is encoded as a series of:
1804 //   { is_file [bool], blob_number [int64_t as varInt],
1805 //     type [string-with-length, may be empty],
1806 //     (for Blobs only) size [int64_t as varInt]
1807 //     (for Files only) fileName [string-with-length]
1808 //   }
1809 // There is no length field; just read until you run out of data.
EncodeV3BlobInfos(const std::vector<IndexedDBExternalObject> & blob_info)1810 std::string EncodeV3BlobInfos(
1811     const std::vector<IndexedDBExternalObject>& blob_info) {
1812   std::string ret;
1813   for (const auto& info : blob_info) {
1814     DCHECK(info.object_type() == IndexedDBExternalObject::ObjectType::kFile ||
1815            info.object_type() == IndexedDBExternalObject::ObjectType::kBlob);
1816     bool is_file =
1817         info.object_type() == IndexedDBExternalObject::ObjectType::kFile;
1818     EncodeBool(is_file, &ret);
1819     EncodeVarInt(info.blob_number(), &ret);
1820     EncodeStringWithLength(info.type(), &ret);
1821     if (is_file)
1822       EncodeStringWithLength(info.file_name(), &ret);
1823     else
1824       EncodeVarInt(info.size(), &ret);
1825   }
1826   return ret;
1827 }
1828 
1829 }  // namespace
1830 
TEST_F(IndexedDBBackingStoreTestWithBlobs,SchemaUpgradeV3ToV4)1831 TEST_F(IndexedDBBackingStoreTestWithBlobs, SchemaUpgradeV3ToV4) {
1832   int64_t database_id;
1833   const int64_t object_store_id = 99;
1834 
1835   const base::string16 database_name(ASCIIToUTF16("db1"));
1836   const int64_t version = 9;
1837 
1838   const base::string16 object_store_name(ASCIIToUTF16("object_store1"));
1839   const bool auto_increment = true;
1840   const IndexedDBKeyPath object_store_key_path(
1841       ASCIIToUTF16("object_store_key"));
1842 
1843   IndexedDBMetadataCoding metadata_coding;
1844 
1845   {
1846     IndexedDBDatabaseMetadata database;
1847     leveldb::Status s = metadata_coding.CreateDatabase(
1848         backing_store()->db(), backing_store()->origin_identifier(),
1849         database_name, version, &database);
1850     EXPECT_TRUE(s.ok());
1851     EXPECT_GT(database.id, 0);
1852     database_id = database.id;
1853 
1854     IndexedDBBackingStore::Transaction transaction(
1855         backing_store()->AsWeakPtr(),
1856         blink::mojom::IDBTransactionDurability::Relaxed,
1857         blink::mojom::IDBTransactionMode::ReadWrite);
1858     transaction.Begin(CreateDummyLock());
1859 
1860     IndexedDBObjectStoreMetadata object_store;
1861     s = metadata_coding.CreateObjectStore(
1862         transaction.transaction(), database.id, object_store_id,
1863         object_store_name, object_store_key_path, auto_increment,
1864         &object_store);
1865     EXPECT_TRUE(s.ok());
1866 
1867     bool succeeded = false;
1868     EXPECT_TRUE(
1869         transaction.CommitPhaseOne(CreateBlobWriteCallback(&succeeded)).ok());
1870     EXPECT_TRUE(succeeded);
1871     EXPECT_TRUE(transaction.CommitPhaseTwo().ok());
1872   }
1873   task_environment_.RunUntilIdle();
1874 
1875   // Initiate transaction1 - writing blobs.
1876   std::unique_ptr<IndexedDBBackingStore::Transaction> transaction1 =
1877       std::make_unique<IndexedDBBackingStore::Transaction>(
1878           backing_store()->AsWeakPtr(),
1879           blink::mojom::IDBTransactionDurability::Relaxed,
1880           blink::mojom::IDBTransactionMode::ReadWrite);
1881   transaction1->Begin(CreateDummyLock());
1882   IndexedDBBackingStore::RecordIdentifier record;
1883   EXPECT_TRUE(backing_store()
1884                   ->PutRecord(transaction1.get(), database_id, object_store_id,
1885                               key3_, &value3_, &record)
1886                   .ok());
1887   bool succeeded = false;
1888   base::RunLoop write_blobs_loop;
1889   EXPECT_TRUE(transaction1
1890                   ->CommitPhaseOne(CreateBlobWriteCallback(
1891                       &succeeded, write_blobs_loop.QuitClosure()))
1892                   .ok());
1893   write_blobs_loop.Run();
1894   task_environment_.RunUntilIdle();
1895 
1896   // Finish up transaction1, verifying blob writes.
1897   EXPECT_TRUE(succeeded);
1898   EXPECT_TRUE(CheckBlobWrites());
1899   ASSERT_TRUE(transaction1->CommitPhaseTwo().ok());
1900   transaction1.reset();
1901 
1902   task_environment_.RunUntilIdle();
1903 
1904   // Change entries to be v3, and change the schema to be v3.
1905   std::unique_ptr<LevelDBWriteBatch> write_batch = LevelDBWriteBatch::Create();
1906   const std::string schema_version_key = SchemaVersionKey::Encode();
1907   ASSERT_TRUE(
1908       indexed_db::PutInt(write_batch.get(), schema_version_key, 3).ok());
1909   const std::string object_store_data_key =
1910       ObjectStoreDataKey::Encode(database_id, object_store_id, key3_);
1911   base::StringPiece leveldb_key_piece(object_store_data_key);
1912   BlobEntryKey blob_entry_key;
1913   ASSERT_TRUE(BlobEntryKey::FromObjectStoreDataKey(&leveldb_key_piece,
1914                                                    &blob_entry_key));
1915   ASSERT_EQ(blob_context_->writes().size(), 3u);
1916   auto& writes = blob_context_->writes();
1917   external_objects()[0].set_blob_number(writes[0].GetBlobNumber());
1918   external_objects()[1].set_blob_number(writes[1].GetBlobNumber());
1919   external_objects()[2].set_blob_number(writes[2].GetBlobNumber());
1920   std::string v3_blob_data = EncodeV3BlobInfos(external_objects());
1921   write_batch->Put(base::StringPiece(blob_entry_key.Encode()),
1922                    base::StringPiece(v3_blob_data));
1923   ASSERT_TRUE(backing_store()->db()->Write(write_batch.get()).ok());
1924 
1925   // The migration code uses the physical files on disk, so those need to be
1926   // written with the correct size & timestamp.
1927   base::FilePath file1_path = writes[1].path;
1928   base::FilePath file2_path = writes[2].path;
1929   ASSERT_TRUE(CreateDirectory(file1_path.DirName()));
1930   ASSERT_TRUE(CreateDirectory(file2_path.DirName()));
1931   base::File file1(file1_path,
1932                    base::File::FLAG_WRITE | base::File::FLAG_CREATE_ALWAYS);
1933   ASSERT_TRUE(file1.IsValid());
1934   ASSERT_TRUE(file1.WriteAtCurrentPosAndCheck(
1935       base::as_bytes(base::make_span(kBlobFileData1))));
1936   ASSERT_TRUE(file1.SetTimes(external_objects()[1].last_modified(),
1937                              external_objects()[1].last_modified()));
1938   file1.Close();
1939 
1940   base::File file2(file2_path,
1941                    base::File::FLAG_WRITE | base::File::FLAG_CREATE_ALWAYS);
1942   ASSERT_TRUE(file2.IsValid());
1943   ASSERT_TRUE(file2.WriteAtCurrentPosAndCheck(
1944       base::as_bytes(base::make_span(kBlobFileData2))));
1945   ASSERT_TRUE(file2.SetTimes(external_objects()[2].last_modified(),
1946                              external_objects()[2].last_modified()));
1947   file2.Close();
1948 
1949   DestroyFactoryAndBackingStore();
1950   CreateFactoryAndBackingStore();
1951 
1952   // There should be no corruption.
1953   ASSERT_TRUE(data_loss_info_.status == blink::mojom::IDBDataLoss::None);
1954 
1955   // Initiate transaction2, reading blobs.
1956   IndexedDBBackingStore::Transaction transaction2(
1957       backing_store()->AsWeakPtr(),
1958       blink::mojom::IDBTransactionDurability::Relaxed,
1959       blink::mojom::IDBTransactionMode::ReadWrite);
1960   transaction2.Begin(CreateDummyLock());
1961   IndexedDBValue result_value;
1962   EXPECT_TRUE(backing_store()
1963                   ->GetRecord(&transaction2, database_id, object_store_id,
1964                               key3_, &result_value)
1965                   .ok());
1966 
1967   // Finish up transaction2, verifying blob reads.
1968   succeeded = false;
1969   EXPECT_TRUE(
1970       transaction2.CommitPhaseOne(CreateBlobWriteCallback(&succeeded)).ok());
1971   EXPECT_TRUE(succeeded);
1972   EXPECT_TRUE(transaction2.CommitPhaseTwo().ok());
1973   EXPECT_EQ(value3_.bits, result_value.bits);
1974   EXPECT_TRUE(CheckBlobInfoMatches(result_value.external_objects));
1975 }
1976 
1977 }  // namespace indexed_db_backing_store_unittest
1978 }  // namespace content
1979