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