LevelDBWrapper: Support sending old values to leveldb wrapper when changing items
Old value parameter to Put() and Delete() calls. The parameter will be used
in next CLs to send notifications when the cache does not store values.
BUG=764127
Change-Id: I26320985265ccc66c66907770447818b94192537
Reviewed-on: https://chromium-review.googlesource.com/661417
Reviewed-by: Marijn Kruisselbrink <[email protected]>
Reviewed-by: Greg Kerr <[email protected]>
Commit-Queue: Siddhartha S <[email protected]>
Cr-Commit-Position: refs/heads/master@{#508493}
diff --git a/content/browser/dom_storage/local_storage_context_mojo_unittest.cc b/content/browser/dom_storage/local_storage_context_mojo_unittest.cc
index c481f52..012fd36 100644
--- a/content/browser/dom_storage/local_storage_context_mojo_unittest.cc
+++ b/content/browser/dom_storage/local_storage_context_mojo_unittest.cc
@@ -110,6 +110,7 @@
void AllDeleted(const std::string& source) override {
observations_.push_back({Observation::kDeleteAll, "", "", "", source});
}
+ void ShouldSendOldValueOnMutations(bool value) override {}
std::vector<Observation> observations_;
mojo::AssociatedBinding<mojom::LevelDBObserver> binding_;
@@ -230,7 +231,8 @@
mojom::LevelDBWrapperPtr wrapper;
context()->OpenLocalStorage(url::Origin(GURL("http://foobar.com")),
MakeRequest(&wrapper));
- wrapper->Put(key, value, "source", base::BindOnce(&NoOpSuccess));
+ wrapper->Put(key, value, base::nullopt, "source",
+ base::BindOnce(&NoOpSuccess));
wrapper.reset();
base::RunLoop().RunUntilIdle();
@@ -249,11 +251,13 @@
mojom::LevelDBWrapperPtr wrapper;
context()->OpenLocalStorage(origin1, MakeRequest(&wrapper));
- wrapper->Put(key1, value, "source", base::BindOnce(&NoOpSuccess));
+ wrapper->Put(key1, value, base::nullopt, "source",
+ base::BindOnce(&NoOpSuccess));
wrapper.reset();
context()->OpenLocalStorage(origin2, MakeRequest(&wrapper));
- wrapper->Put(key2, value, "source", base::BindOnce(&NoOpSuccess));
+ wrapper->Put(key2, value, base::nullopt, "source",
+ base::BindOnce(&NoOpSuccess));
wrapper.reset();
base::RunLoop().RunUntilIdle();
@@ -268,7 +272,8 @@
mojom::LevelDBWrapperPtr wrapper;
context()->OpenLocalStorage(url::Origin(GURL("http://foobar.com")),
MakeRequest(&wrapper));
- wrapper->Put(key, value, "source", base::BindOnce(&NoOpSuccess));
+ wrapper->Put(key, value, base::nullopt, "source",
+ base::BindOnce(&NoOpSuccess));
wrapper.reset();
base::RunLoop().RunUntilIdle();
@@ -318,7 +323,8 @@
mojom::LevelDBWrapperPtr wrapper;
context()->OpenLocalStorage(url::Origin(GURL("http://foobar.com")),
MakeRequest(&wrapper));
- wrapper->Put(key, value, "source", base::BindOnce(&NoOpSuccess));
+ wrapper->Put(key, value, base::nullopt, "source",
+ base::BindOnce(&NoOpSuccess));
wrapper.reset();
base::RunLoop().RunUntilIdle();
@@ -419,12 +425,15 @@
mojom::LevelDBWrapperPtr wrapper;
context()->OpenLocalStorage(origin1, MakeRequest(&wrapper));
- wrapper->Put(key1, value, "source", base::BindOnce(&NoOpSuccess));
- wrapper->Put(key2, value, "source", base::BindOnce(&NoOpSuccess));
+ wrapper->Put(key1, value, base::nullopt, "source",
+ base::BindOnce(&NoOpSuccess));
+ wrapper->Put(key2, value, base::nullopt, "source",
+ base::BindOnce(&NoOpSuccess));
wrapper.reset();
context()->OpenLocalStorage(origin2, MakeRequest(&wrapper));
- wrapper->Put(key2, value, "source", base::BindOnce(&NoOpSuccess));
+ wrapper->Put(key2, value, base::nullopt, "source",
+ base::BindOnce(&NoOpSuccess));
wrapper.reset();
// GetStorageUsage only includes committed data, but still returns all origins
@@ -466,13 +475,15 @@
mojom::LevelDBWrapperPtr wrapper;
context()->OpenLocalStorage(origin1, MakeRequest(&wrapper));
- wrapper->Put(key, value, "source", base::BindOnce(&NoOpSuccess));
+ wrapper->Put(key, value, base::nullopt, "source",
+ base::BindOnce(&NoOpSuccess));
wrapper.reset();
context()->OpenLocalStorage(origin2, MakeRequest(&wrapper));
- wrapper->Put(key, value, "source", base::BindOnce(&NoOpSuccess));
+ wrapper->Put(key, value, base::nullopt, "source",
+ base::BindOnce(&NoOpSuccess));
wrapper.reset();
context()->OpenLocalStorage(origin1, MakeRequest(&wrapper));
- wrapper->Delete(key, "source", base::BindOnce(&NoOpSuccess));
+ wrapper->Delete(key, value, "source", base::BindOnce(&NoOpSuccess));
wrapper.reset();
// Make sure all data gets committed to disk.
@@ -499,10 +510,12 @@
mojom::LevelDBWrapperPtr wrapper;
context()->OpenLocalStorage(origin1, MakeRequest(&wrapper));
- wrapper->Put(key, value, "source", base::BindOnce(&NoOpSuccess));
+ wrapper->Put(key, value, base::nullopt, "source",
+ base::BindOnce(&NoOpSuccess));
wrapper.reset();
context()->OpenLocalStorage(origin2, MakeRequest(&wrapper));
- wrapper->Put(key, value, "source", base::BindOnce(&NoOpSuccess));
+ wrapper->Put(key, value, base::nullopt, "source",
+ base::BindOnce(&NoOpSuccess));
wrapper.reset();
context()->OpenLocalStorage(origin1, MakeRequest(&wrapper));
@@ -542,11 +555,13 @@
mojom::LevelDBWrapperPtr wrapper;
context()->OpenLocalStorage(origin1, MakeRequest(&wrapper));
- wrapper->Put(key, value, "source", base::BindOnce(&NoOpSuccess));
+ wrapper->Put(key, value, base::nullopt, "source",
+ base::BindOnce(&NoOpSuccess));
wrapper.reset();
context()->OpenLocalStorage(origin2, MakeRequest(&wrapper));
- wrapper->Put(key, value, "source", base::BindOnce(&NoOpSuccess));
+ wrapper->Put(key, value, base::nullopt, "source",
+ base::BindOnce(&NoOpSuccess));
wrapper.reset();
// Make sure all data gets committed to disk.
@@ -577,11 +592,13 @@
mojom::LevelDBWrapperPtr wrapper;
context()->OpenLocalStorage(origin1, MakeRequest(&wrapper));
- wrapper->Put(key, value, "source", base::BindOnce(&NoOpSuccess));
+ wrapper->Put(key, value, base::nullopt, "source",
+ base::BindOnce(&NoOpSuccess));
wrapper.reset();
context()->OpenLocalStorage(origin2, MakeRequest(&wrapper));
- wrapper->Put(key, value, "source", base::BindOnce(&NoOpSuccess));
+ wrapper->Put(key, value, base::nullopt, "source",
+ base::BindOnce(&NoOpSuccess));
wrapper.reset();
// Make sure all data gets committed to disk.
@@ -621,11 +638,13 @@
mojom::LevelDBWrapperPtr wrapper;
context()->OpenLocalStorage(origin1, MakeRequest(&wrapper));
- wrapper->Put(key, value, "source", base::BindOnce(&NoOpSuccess));
+ wrapper->Put(key, value, base::nullopt, "source",
+ base::BindOnce(&NoOpSuccess));
wrapper.reset();
context()->OpenLocalStorage(origin2, MakeRequest(&wrapper));
- wrapper->Put(key, value, "source", base::BindOnce(&NoOpSuccess));
+ wrapper->Put(key, value, base::nullopt, "source",
+ base::BindOnce(&NoOpSuccess));
wrapper.reset();
// Make sure all data gets committed to disk.
@@ -635,7 +654,7 @@
TestLevelDBObserver observer;
context()->OpenLocalStorage(origin1, MakeRequest(&wrapper));
wrapper->AddObserver(observer.Bind());
- wrapper->Put(StdStringToUint8Vector("key2"), value, "source",
+ wrapper->Put(StdStringToUint8Vector("key2"), value, base::nullopt, "source",
base::BindOnce(&NoOpSuccess));
base::RunLoop().RunUntilIdle();
@@ -670,14 +689,17 @@
mojom::LevelDBWrapperPtr wrapper;
context()->OpenLocalStorage(origin1a, MakeRequest(&wrapper));
- wrapper->Put(key, value, "source", base::BindOnce(&NoOpSuccess));
+ wrapper->Put(key, value, base::nullopt, "source",
+ base::BindOnce(&NoOpSuccess));
wrapper.reset();
context()->OpenLocalStorage(origin1b, MakeRequest(&wrapper));
- wrapper->Put(key, value, "source", base::BindOnce(&NoOpSuccess));
+ wrapper->Put(key, value, base::nullopt, "source",
+ base::BindOnce(&NoOpSuccess));
wrapper.reset();
context()->OpenLocalStorage(origin2, MakeRequest(&wrapper));
- wrapper->Put(key, value, "source", base::BindOnce(&NoOpSuccess));
+ wrapper->Put(key, value, base::nullopt, "source",
+ base::BindOnce(&NoOpSuccess));
wrapper.reset();
// Make sure all data gets committed to disk.
@@ -836,12 +858,15 @@
mojom::LevelDBWrapperPtr wrapper;
context()->OpenLocalStorage(origin1, MakeRequest(&wrapper));
- wrapper->Put(key1, value, "source", base::BindOnce(&NoOpSuccess));
- wrapper->Put(key2, value, "source", base::BindOnce(&NoOpSuccess));
+ wrapper->Put(key1, value, base::nullopt, "source",
+ base::BindOnce(&NoOpSuccess));
+ wrapper->Put(key2, value, base::nullopt, "source",
+ base::BindOnce(&NoOpSuccess));
wrapper.reset();
context()->OpenLocalStorage(origin2, MakeRequest(&wrapper));
- wrapper->Put(key2, value, "source", base::BindOnce(&NoOpSuccess));
+ wrapper->Put(key2, value, base::nullopt, "source",
+ base::BindOnce(&NoOpSuccess));
wrapper.reset();
// Make sure all data gets committed to the DB.
@@ -944,7 +969,8 @@
mojom::LevelDBWrapperPtr wrapper;
context->OpenLocalStorage(url::Origin(GURL("http://foobar.com")),
MakeRequest(&wrapper));
- wrapper->Put(key, value, "source", base::BindOnce(&NoOpSuccess));
+ wrapper->Put(key, value, base::nullopt, "source",
+ base::BindOnce(&NoOpSuccess));
wrapper.reset();
base::RunLoop().RunUntilIdle();
}
@@ -1311,7 +1337,7 @@
// a lot of data on the first origin. This put operation should result in a
// pending commit that will get cancelled when the database connection is
// closed.
- wrapper3->Put(key, value, "source",
+ wrapper3->Put(key, value, base::nullopt, "source",
base::BindOnce([](bool success) { EXPECT_TRUE(success); }));
// Repeatedly write data to the database, to trigger enough commit errors.
@@ -1322,7 +1348,7 @@
// change to commit.
value[0]++;
wrapper1.set_connection_error_handler(put_loop.QuitClosure());
- wrapper1->Put(key, value, "source",
+ wrapper1->Put(key, value, base::nullopt, "source",
base::BindOnce(
[](base::Closure quit_closure, bool success) {
EXPECT_TRUE(success);
@@ -1447,7 +1473,7 @@
// change to commit.
value[0]++;
wrapper.set_connection_error_handler(put_loop.QuitClosure());
- wrapper->Put(key, value, "source",
+ wrapper->Put(key, value, base::nullopt, "source",
base::BindOnce(
[](base::Closure quit_closure, bool success) {
EXPECT_TRUE(success);
@@ -1494,7 +1520,7 @@
// change to commit.
value[0]++;
wrapper.set_connection_error_handler(put_loop.QuitClosure());
- wrapper->Put(key, value, "source",
+ wrapper->Put(key, value, base::nullopt, "source",
base::BindOnce(
[](base::Closure quit_closure, bool success) {
EXPECT_TRUE(success);
diff --git a/content/browser/leveldb_wrapper_impl.cc b/content/browser/leveldb_wrapper_impl.cc
index 1914d93..e20d2eee 100644
--- a/content/browser/leveldb_wrapper_impl.cc
+++ b/content/browser/leveldb_wrapper_impl.cc
@@ -146,13 +146,16 @@
observers_.AddPtr(std::move(observer_ptr));
}
-void LevelDBWrapperImpl::Put(const std::vector<uint8_t>& key,
- const std::vector<uint8_t>& value,
- const std::string& source,
- PutCallback callback) {
+void LevelDBWrapperImpl::Put(
+ const std::vector<uint8_t>& key,
+ const std::vector<uint8_t>& value,
+ const base::Optional<std::vector<uint8_t>>& client_old_value,
+ const std::string& source,
+ PutCallback callback) {
if (!map_) {
LoadMap(base::Bind(&LevelDBWrapperImpl::Put, base::Unretained(this), key,
- value, source, base::Passed(&callback)));
+ value, client_old_value, source,
+ base::Passed(&callback)));
return;
}
@@ -183,9 +186,8 @@
}
std::vector<uint8_t> old_value;
- if (has_old_item) {
+ if (has_old_item)
old_value.swap((*map_)[key]);
- }
(*map_)[key] = value;
bytes_used_ = new_bytes_used;
if (!has_old_item) {
@@ -204,12 +206,14 @@
std::move(callback).Run(true);
}
-void LevelDBWrapperImpl::Delete(const std::vector<uint8_t>& key,
- const std::string& source,
- DeleteCallback callback) {
+void LevelDBWrapperImpl::Delete(
+ const std::vector<uint8_t>& key,
+ const base::Optional<std::vector<uint8_t>>& client_old_value,
+ const std::string& source,
+ DeleteCallback callback) {
if (!map_) {
LoadMap(base::Bind(&LevelDBWrapperImpl::Delete, base::Unretained(this), key,
- source, base::Passed(&callback)));
+ client_old_value, source, base::Passed(&callback)));
return;
}
@@ -476,7 +480,7 @@
operations.push_back(std::move(item));
}
size_t data_size = 0;
- for (const auto& key: commit_batch_->changed_keys) {
+ for (const auto& key : commit_batch_->changed_keys) {
data_size += key.size();
leveldb::mojom::BatchedOperationPtr item =
leveldb::mojom::BatchedOperation::New();
diff --git a/content/browser/leveldb_wrapper_impl.h b/content/browser/leveldb_wrapper_impl.h
index 20d2425..876efd24 100644
--- a/content/browser/leveldb_wrapper_impl.h
+++ b/content/browser/leveldb_wrapper_impl.h
@@ -101,9 +101,11 @@
void AddObserver(mojom::LevelDBObserverAssociatedPtrInfo observer) override;
void Put(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& value,
+ const base::Optional<std::vector<uint8_t>>& client_old_value,
const std::string& source,
PutCallback callback) override;
void Delete(const std::vector<uint8_t>& key,
+ const base::Optional<std::vector<uint8_t>>& client_old_value,
const std::string& source,
DeleteCallback callback) override;
void DeleteAll(const std::string& source,
diff --git a/content/browser/leveldb_wrapper_impl_unittest.cc b/content/browser/leveldb_wrapper_impl_unittest.cc
index a0167f3..e76436d 100644
--- a/content/browser/leveldb_wrapper_impl_unittest.cc
+++ b/content/browser/leveldb_wrapper_impl_unittest.cc
@@ -161,21 +161,24 @@
bool PutSync(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& value,
+ const base::Optional<std::vector<uint8_t>>& client_old_value,
std::string source = kTestSource) {
base::RunLoop run_loop;
bool success = false;
wrapper()->Put(
- key, value, source,
+ key, value, client_old_value, source,
base::BindOnce(&SuccessCallback, run_loop.QuitClosure(), &success));
run_loop.Run();
return success;
}
- bool DeleteSync(const std::vector<uint8_t>& key) {
+ bool DeleteSync(
+ const std::vector<uint8_t>& key,
+ const base::Optional<std::vector<uint8_t>>& client_old_value) {
base::RunLoop run_loop;
bool success = false;
wrapper()->Delete(
- key, kTestSource,
+ key, client_old_value, kTestSource,
base::BindOnce(&SuccessCallback, run_loop.QuitClosure(), &success));
run_loop.Run();
return success;
@@ -222,6 +225,7 @@
void AllDeleted(const std::string& source) override {
observations_.push_back({Observation::kDeleteAll, "", "", "", source});
}
+ void ShouldSendOldValueOnMutations(bool value) override {}
TestBrowserThreadBundle thread_bundle_;
std::map<std::vector<uint8_t>, std::vector<uint8_t>> mock_data_;
@@ -245,7 +249,7 @@
std::vector<uint8_t> key = StdStringToUint8Vector("123");
std::vector<uint8_t> value = StdStringToUint8Vector("foo");
- EXPECT_TRUE(PutSync(key, value));
+ EXPECT_TRUE(PutSync(key, value, StdStringToUint8Vector("123data")));
std::vector<uint8_t> result;
EXPECT_TRUE(GetSync(key, &result));
@@ -256,7 +260,7 @@
std::vector<uint8_t> key = StdStringToUint8Vector("newkey");
std::vector<uint8_t> value = StdStringToUint8Vector("foo");
- EXPECT_TRUE(PutSync(key, value));
+ EXPECT_TRUE(PutSync(key, value, base::nullopt));
std::vector<uint8_t> result;
EXPECT_TRUE(GetSync(key, &result));
@@ -284,12 +288,14 @@
std::string key2 = "abc";
std::string value2 = "data abc";
- EXPECT_TRUE(
- PutSync(StdStringToUint8Vector(key1), StdStringToUint8Vector(value1)));
+ EXPECT_TRUE(PutSync(StdStringToUint8Vector(key1),
+ StdStringToUint8Vector(value1),
+ StdStringToUint8Vector("123data")));
EXPECT_TRUE(PutSync(StdStringToUint8Vector(key2),
+ StdStringToUint8Vector("old value"), base::nullopt));
+ EXPECT_TRUE(PutSync(StdStringToUint8Vector(key2),
+ StdStringToUint8Vector(value2),
StdStringToUint8Vector("old value")));
- EXPECT_TRUE(
- PutSync(StdStringToUint8Vector(key2), StdStringToUint8Vector(value2)));
EXPECT_FALSE(has_mock_data(kTestPrefix + key2));
@@ -308,7 +314,7 @@
std::string source2 = "source2";
EXPECT_TRUE(PutSync(StdStringToUint8Vector(key),
- StdStringToUint8Vector(value1), source1));
+ StdStringToUint8Vector(value1), base::nullopt, source1));
ASSERT_EQ(1u, observations().size());
EXPECT_EQ(Observation::kAdd, observations()[0].type);
EXPECT_EQ(key, observations()[0].key);
@@ -316,7 +322,8 @@
EXPECT_EQ(source1, observations()[0].source);
EXPECT_TRUE(PutSync(StdStringToUint8Vector(key),
- StdStringToUint8Vector(value2), source2));
+ StdStringToUint8Vector(value2),
+ StdStringToUint8Vector(value1), source2));
ASSERT_EQ(2u, observations().size());
EXPECT_EQ(Observation::kChange, observations()[1].type);
EXPECT_EQ(key, observations()[1].key);
@@ -326,12 +333,13 @@
// Same put should not cause another observation.
EXPECT_TRUE(PutSync(StdStringToUint8Vector(key),
- StdStringToUint8Vector(value2), source2));
+ StdStringToUint8Vector(value2), base::nullopt, source2));
ASSERT_EQ(2u, observations().size());
}
TEST_F(LevelDBWrapperImplTest, DeleteNonExistingKey) {
- EXPECT_TRUE(DeleteSync(StdStringToUint8Vector("doesn't exist")));
+ EXPECT_TRUE(DeleteSync(StdStringToUint8Vector("doesn't exist"),
+ std::vector<uint8_t>()));
EXPECT_EQ(0u, observations().size());
}
@@ -340,7 +348,8 @@
std::string value = "foo";
set_mock_data(kTestPrefix + key, value);
- EXPECT_TRUE(DeleteSync(StdStringToUint8Vector(key)));
+ EXPECT_TRUE(
+ DeleteSync(StdStringToUint8Vector(key), StdStringToUint8Vector(value)));
ASSERT_EQ(1u, observations().size());
EXPECT_EQ(Observation::kDelete, observations()[0].type);
EXPECT_EQ(key, observations()[0].key);
@@ -378,7 +387,7 @@
// And now we've deleted all, writing something the quota size should work.
EXPECT_TRUE(PutSync(std::vector<uint8_t>(kTestSizeLimit, 'b'),
- std::vector<uint8_t>()));
+ std::vector<uint8_t>(), base::nullopt));
}
TEST_F(LevelDBWrapperImplTest, DeleteAllWithLoadedMap) {
@@ -387,8 +396,8 @@
std::string dummy_key = "foobar";
set_mock_data(dummy_key, value);
- EXPECT_TRUE(
- PutSync(StdStringToUint8Vector(key), StdStringToUint8Vector(value)));
+ EXPECT_TRUE(PutSync(StdStringToUint8Vector(key),
+ StdStringToUint8Vector(value), base::nullopt));
EXPECT_TRUE(DeleteAllSync());
ASSERT_EQ(2u, observations().size());
@@ -409,7 +418,8 @@
set_mock_data(dummy_key, value);
wrapper()->Put(StdStringToUint8Vector(key), StdStringToUint8Vector(value),
- kTestSource, base::BindOnce(&NoOpSuccessCallback));
+ base::nullopt, kTestSource,
+ base::BindOnce(&NoOpSuccessCallback));
EXPECT_TRUE(DeleteAllSync());
ASSERT_EQ(2u, observations().size());
@@ -434,69 +444,77 @@
std::vector<uint8_t> key = StdStringToUint8Vector("newkey");
std::vector<uint8_t> value(kTestSizeLimit, 4);
- EXPECT_FALSE(PutSync(key, value));
+ EXPECT_FALSE(PutSync(key, value, base::nullopt));
value.resize(kTestSizeLimit / 2);
- EXPECT_TRUE(PutSync(key, value));
+ EXPECT_TRUE(PutSync(key, value, base::nullopt));
}
TEST_F(LevelDBWrapperImplTest, PutOverQuotaLargeKey) {
std::vector<uint8_t> key(kTestSizeLimit, 'a');
std::vector<uint8_t> value = StdStringToUint8Vector("newvalue");
- EXPECT_FALSE(PutSync(key, value));
+ EXPECT_FALSE(PutSync(key, value, base::nullopt));
key.resize(kTestSizeLimit / 2);
- EXPECT_TRUE(PutSync(key, value));
+ EXPECT_TRUE(PutSync(key, value, base::nullopt));
}
TEST_F(LevelDBWrapperImplTest, PutWhenAlreadyOverQuota) {
std::string key = "largedata";
std::vector<uint8_t> value(kTestSizeLimit, 4);
+ std::vector<uint8_t> old_value = value;
set_mock_data(kTestPrefix + key, Uint8VectorToStdString(value));
// Put with same data should succeed.
- EXPECT_TRUE(PutSync(StdStringToUint8Vector(key), value));
+ EXPECT_TRUE(PutSync(StdStringToUint8Vector(key), value, base::nullopt));
// Put with same data size should succeed.
value[1] = 13;
- EXPECT_TRUE(PutSync(StdStringToUint8Vector(key), value));
+ EXPECT_TRUE(PutSync(StdStringToUint8Vector(key), value, old_value));
// Adding a new key when already over quota should not succeed.
- EXPECT_FALSE(PutSync(StdStringToUint8Vector("newkey"), {1, 2, 3}));
+ EXPECT_FALSE(
+ PutSync(StdStringToUint8Vector("newkey"), {1, 2, 3}, base::nullopt));
// Reducing size should also succeed.
+ old_value = value;
value.resize(kTestSizeLimit / 2);
- EXPECT_TRUE(PutSync(StdStringToUint8Vector(key), value));
+ EXPECT_TRUE(PutSync(StdStringToUint8Vector(key), value, old_value));
// Increasing size again should succeed, as still under the limit.
+ old_value = value;
value.resize(value.size() + 1);
- EXPECT_TRUE(PutSync(StdStringToUint8Vector(key), value));
+ EXPECT_TRUE(PutSync(StdStringToUint8Vector(key), value, old_value));
// But increasing back to original size should fail.
+ old_value = value;
value.resize(kTestSizeLimit);
- EXPECT_FALSE(PutSync(StdStringToUint8Vector(key), value));
+ EXPECT_FALSE(PutSync(StdStringToUint8Vector(key), value, old_value));
}
TEST_F(LevelDBWrapperImplTest, PutWhenAlreadyOverQuotaBecauseOfLargeKey) {
std::vector<uint8_t> key(kTestSizeLimit, 'x');
std::vector<uint8_t> value = StdStringToUint8Vector("value");
+ std::vector<uint8_t> old_value = value;
set_mock_data(kTestPrefix + Uint8VectorToStdString(key),
Uint8VectorToStdString(value));
// Put with same data size should succeed.
value[0] = 'X';
- EXPECT_TRUE(PutSync(key, value));
+ EXPECT_TRUE(PutSync(key, value, old_value));
// Reducing size should also succeed.
+ old_value = value;
value.clear();
- EXPECT_TRUE(PutSync(key, value));
+ EXPECT_TRUE(PutSync(key, value, old_value));
// Increasing size should fail.
+ old_value = value;
value.resize(1, 'a');
- EXPECT_FALSE(PutSync(key, value));
+ EXPECT_FALSE(PutSync(key, value, old_value));
}
TEST_F(LevelDBWrapperImplTest, GetAfterPurgeMemory) {
@@ -521,7 +539,7 @@
TEST_F(LevelDBWrapperImplTest, PurgeMemoryWithPendingChanges) {
std::vector<uint8_t> key = StdStringToUint8Vector("123");
std::vector<uint8_t> value = StdStringToUint8Vector("foo");
- EXPECT_TRUE(PutSync(key, value));
+ EXPECT_TRUE(PutSync(key, value, StdStringToUint8Vector("123data")));
EXPECT_EQ(delegate()->map_load_count(), 1);
// Purge memory, and read. Should not actually have purged, so should not have
diff --git a/content/common/leveldb_wrapper.mojom b/content/common/leveldb_wrapper.mojom
index e099618..d900a88 100644
--- a/content/common/leveldb_wrapper.mojom
+++ b/content/common/leveldb_wrapper.mojom
@@ -15,6 +15,12 @@
string source);
KeyDeleted(array<uint8> key, array<uint8> old_value, string source);
AllDeleted(string source);
+
+ // Tells the client if it should send the old values for the key on Put() and
+ // Delete() calls for sending notifications. By default the clients are
+ // expected to send old values. It is set to true when the leveldb wrapper
+ // does not cache values in memory.
+ ShouldSendOldValueOnMutations(bool value);
};
struct KeyValue {
@@ -37,13 +43,31 @@
interface LevelDBWrapper {
AddObserver(associated LevelDBObserver observer);
- // Sets the database entry for |key| to |value|. Returns OK on success.
- Put(array<uint8> key, array<uint8> value, string source) => (bool success);
+ // Set the database entry for |key| to |value|.
+ // Takes an optional |client_old_value| (see ShouldSendOldValueOnMutations()):
+ // 1. If the client is notified to not send old value on mutations
+ // |client_old_value| is unused and can be nullopt.
+ // 2. If the client is notified to send old values or not notified at all,
+ // |client_old_value| must be filled in with old value of the |key|, or
+ // nullopt if |key| was not present in database. This value is used to send
+ // notifications to LevelDBObserver(s).
+ // Returns OK on success.
+ Put(array<uint8> key, array<uint8> value, array<uint8>? client_old_value,
+ string source) =>
+ (bool success);
- // Remove the database entry (if any) for |key|. Returns OK on success, and a
- // non-OK status on error. It is not an error if |key| did not exist in the
- // database.
- Delete(array<uint8> key, string source) => (bool success);
+ // Remove the database entry (if any) for |key|.
+ // Takes an optional |client_old_value| (see ShouldSendOldValueOnMutations()):
+ // 1. If the client is notified to not send old value on mutations,
+ // |client_old_value| is unused and can be nullopt.
+ // 2. If the client is notified to send old values or not notified at all,
+ // |client_old_value| must be filled in with old value of the |key|, or
+ // nullopt if |key| was not present in database. This value is used to send
+ // notifications to LevelDBObserver(s).
+ // Returns OK on success, and a non-OK status on error. It is not an error if
+ // |key| did not exist in the database.
+ Delete(array<uint8> key, array<uint8>? client_old_value, string source) =>
+ (bool success);
// Removes all the entries.
DeleteAll(string source) => (bool success);
diff --git a/content/renderer/dom_storage/local_storage_cached_area.cc b/content/renderer/dom_storage/local_storage_cached_area.cc
index 7297df6..9cb5057 100644
--- a/content/renderer/dom_storage/local_storage_cached_area.cc
+++ b/content/renderer/dom_storage/local_storage_cached_area.cc
@@ -110,13 +110,22 @@
return false;
EnsureLoaded();
- if (!map_->SetItem(key, value, nullptr))
+ bool result = false;
+ base::NullableString16 old_nullable_value;
+ if (should_send_old_value_on_mutations_)
+ result = map_->SetItem(key, value, &old_nullable_value);
+ else
+ result = map_->SetItem(key, value, nullptr);
+ if (!result)
return false;
// Ignore mutations to |key| until OnSetItemComplete.
ignore_key_mutations_[key]++;
+ base::Optional<std::vector<uint8_t>> optional_old_value;
+ if (!old_nullable_value.is_null())
+ optional_old_value = String16ToUint8Vector(old_nullable_value.string());
leveldb_->Put(String16ToUint8Vector(key), String16ToUint8Vector(value),
- PackSource(page_url, storage_area_id),
+ optional_old_value, PackSource(page_url, storage_area_id),
base::BindOnce(&LocalStorageCachedArea::OnSetItemComplete,
weak_factory_.GetWeakPtr(), key));
return true;
@@ -126,12 +135,21 @@
const GURL& page_url,
const std::string& storage_area_id) {
EnsureLoaded();
- if (!map_->RemoveItem(key, nullptr))
+ bool result = false;
+ base::string16 old_value;
+ if (should_send_old_value_on_mutations_)
+ result = map_->RemoveItem(key, &old_value);
+ else
+ result = map_->RemoveItem(key, nullptr);
+ if (!result)
return;
// Ignore mutations to |key| until OnRemoveItemComplete.
ignore_key_mutations_[key]++;
- leveldb_->Delete(String16ToUint8Vector(key),
+ base::Optional<std::vector<uint8_t>> optional_old_value;
+ if (should_send_old_value_on_mutations_)
+ optional_old_value = String16ToUint8Vector(old_value);
+ leveldb_->Delete(String16ToUint8Vector(key), optional_old_value,
PackSource(page_url, storage_area_id),
base::BindOnce(&LocalStorageCachedArea::OnRemoveItemComplete,
weak_factory_.GetWeakPtr(), key));
@@ -286,6 +304,10 @@
origin_.GetURL(), page_url, originating_area);
}
+void LocalStorageCachedArea::ShouldSendOldValueOnMutations(bool value) {
+ should_send_old_value_on_mutations_ = value;
+}
+
void LocalStorageCachedArea::KeyAddedOrChanged(
const std::vector<uint8_t>& key,
const std::vector<uint8_t>& new_value,
diff --git a/content/renderer/dom_storage/local_storage_cached_area.h b/content/renderer/dom_storage/local_storage_cached_area.h
index a04792ca..a28121d 100644
--- a/content/renderer/dom_storage/local_storage_cached_area.h
+++ b/content/renderer/dom_storage/local_storage_cached_area.h
@@ -85,6 +85,7 @@
const std::vector<uint8_t>& old_value,
const std::string& source) override;
void AllDeleted(const std::string& source) override;
+ void ShouldSendOldValueOnMutations(bool value) override;
// Common helper for KeyAdded() and KeyChanged()
void KeyAddedOrChanged(const std::vector<uint8_t>& key,
@@ -108,6 +109,8 @@
scoped_refptr<DOMStorageMap> map_;
std::map<base::string16, int> ignore_key_mutations_;
bool ignore_all_mutations_ = false;
+ // See ShouldSendOldValueOnMutations().
+ bool should_send_old_value_on_mutations_ = true;
mojom::LevelDBWrapperPtr leveldb_;
mojo::AssociatedBinding<mojom::LevelDBObserver> binding_;
LocalStorageCachedAreas* cached_areas_;
diff --git a/content/renderer/dom_storage/local_storage_cached_area_unittest.cc b/content/renderer/dom_storage/local_storage_cached_area_unittest.cc
index d9676ceb..d3b36a61 100644
--- a/content/renderer/dom_storage/local_storage_cached_area_unittest.cc
+++ b/content/renderer/dom_storage/local_storage_cached_area_unittest.cc
@@ -35,6 +35,7 @@
void Put(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& value,
+ const base::Optional<std::vector<uint8_t>>& client_old_value,
const std::string& source,
PutCallback callback) override {
observed_put_ = true;
@@ -45,6 +46,7 @@
}
void Delete(const std::vector<uint8_t>& key,
+ const base::Optional<std::vector<uint8_t>>& client_old_value,
const std::string& source,
DeleteCallback callback) override {
observed_delete_ = true;