[code-health] Add ParamTraits for base::Value::{Dict,List}
* Use the new types internally, rather than the deprecated types.
* Add conversion unit tests for both deprecated and new types.
* Behavior change: a base::Value can no longer be read as a
base::Value::List or base::Value::Dict or vice-versa. The type
inferrence should be enough for the caller to use the right type.
Bug: 1301889
Bug: 1301890
Change-Id: I3279e30e586d7443603042b6b9a31f01e8ff41c6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3558072
Reviewed-by: Daniel Cheng <[email protected]>
Reviewed-by: Ken Rockot <[email protected]>
Commit-Queue: Fabrice de Gans <[email protected]>
Cr-Commit-Position: refs/heads/main@{#988178}
diff --git a/ipc/ipc_message_utils.cc b/ipc/ipc_message_utils.cc
index b80fe75..def0eee6 100644
--- a/ipc/ipc_message_utils.cc
+++ b/ipc/ipc_message_utils.cc
@@ -17,7 +17,6 @@
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "base/unguessable_token.h"
-#include "base/values.h"
#include "build/build_config.h"
#include "ipc/ipc_channel_handle.h"
#include "ipc/ipc_message.h"
@@ -80,125 +79,135 @@
#endif
}
-bool ReadValue(const base::Pickle* m,
- base::PickleIterator* iter,
- base::Value* value,
- int recursion);
+void WriteValue(const base::Value& value, int recursion, base::Pickle* pickle);
-void WriteValue(base::Pickle* m, const base::Value* value, int recursion) {
+void WriteDictValue(const base::Value::Dict& value,
+ int recursion,
+ base::Pickle* pickle) {
+ WriteParam(pickle, base::checked_cast<int>(value.size()));
+ for (const auto entry : value) {
+ WriteParam(pickle, entry.first);
+ WriteValue(entry.second, recursion + 1, pickle);
+ }
+}
+
+void WriteListValue(const base::Value::List& value,
+ int recursion,
+ base::Pickle* pickle) {
+ WriteParam(pickle, base::checked_cast<int>(value.size()));
+ for (const auto& entry : value) {
+ WriteValue(entry, recursion + 1, pickle);
+ }
+}
+
+void WriteValue(const base::Value& value, int recursion, base::Pickle* pickle) {
bool result;
if (recursion > kMaxRecursionDepth) {
LOG(ERROR) << "Max recursion depth hit in WriteValue.";
return;
}
- m->WriteInt(static_cast<int>(value->type()));
+ pickle->WriteInt(static_cast<int>(value.type()));
- switch (value->type()) {
+ switch (value.type()) {
case base::Value::Type::NONE:
break;
case base::Value::Type::BOOLEAN: {
- WriteParam(m, value->GetBool());
+ WriteParam(pickle, value.GetBool());
break;
}
case base::Value::Type::INTEGER: {
- DCHECK(value->is_int());
- WriteParam(m, value->GetInt());
+ DCHECK(value.is_int());
+ WriteParam(pickle, value.GetInt());
break;
}
case base::Value::Type::DOUBLE: {
- DCHECK(value->is_int() || value->is_double());
- WriteParam(m, value->GetDouble());
+ DCHECK(value.is_int() || value.is_double());
+ WriteParam(pickle, value.GetDouble());
break;
}
case base::Value::Type::STRING: {
- const std::string* val = value->GetIfString();
+ const std::string* val = value.GetIfString();
result = !!val;
DCHECK(result);
- WriteParam(m, *val);
+ WriteParam(pickle, *val);
break;
}
case base::Value::Type::BINARY: {
- m->WriteData(reinterpret_cast<const char*>(value->GetBlob().data()),
- base::checked_cast<int>(value->GetBlob().size()));
+ pickle->WriteData(reinterpret_cast<const char*>(value.GetBlob().data()),
+ base::checked_cast<int>(value.GetBlob().size()));
break;
}
- case base::Value::Type::DICTIONARY: {
- DCHECK(value->is_dict());
- WriteParam(m, base::checked_cast<int>(value->DictSize()));
-
- for (auto it : value->DictItems()) {
- WriteParam(m, it.first);
- WriteValue(m, &it.second, recursion + 1);
- }
+ case base::Value::Type::DICT: {
+ DCHECK(value.is_dict());
+ WriteDictValue(value.GetDict(), recursion, pickle);
break;
}
case base::Value::Type::LIST: {
- const base::ListValue* list = static_cast<const base::ListValue*>(value);
- WriteParam(m, base::checked_cast<int>(list->GetListDeprecated().size()));
- for (const auto& entry : list->GetListDeprecated()) {
- WriteValue(m, &entry, recursion + 1);
- }
+ DCHECK(value.is_list());
+ WriteListValue(value.GetList(), recursion, pickle);
break;
}
}
}
-// Helper for ReadValue that reads a DictionaryValue into a pre-allocated
-// object.
-bool ReadDictionaryValue(const base::Pickle* m,
- base::PickleIterator* iter,
- base::DictionaryValue* value,
- int recursion) {
- int size;
- if (!ReadParam(m, iter, &size))
- return false;
-
- std::vector<base::Value::LegacyDictStorage::value_type> entries;
- entries.resize(size);
- for (auto& entry : entries) {
- entry.second = std::make_unique<base::Value>();
- if (!ReadParam(m, iter, &entry.first) ||
- !ReadValue(m, iter, entry.second.get(), recursion + 1))
- return false;
- }
-
- *value =
- base::DictionaryValue(base::Value::LegacyDictStorage(std::move(entries)));
- return true;
-}
-
-// Helper for ReadValue that reads a ReadListValue into a pre-allocated
-// object.
-bool ReadListValue(const base::Pickle* m,
- base::PickleIterator* iter,
- base::ListValue* value,
- int recursion) {
- int size;
- if (!ReadParam(m, iter, &size))
- return false;
-
- base::Value::ListStorage list_storage;
- list_storage.resize(size);
- for (base::Value& subval : list_storage) {
- if (!ReadValue(m, iter, &subval, recursion + 1))
- return false;
- }
- *value = base::ListValue(std::move(list_storage));
- return true;
-}
-
-bool ReadValue(const base::Pickle* m,
+bool ReadValue(const base::Pickle* pickle,
base::PickleIterator* iter,
- base::Value* value,
- int recursion) {
+ int recursion,
+ base::Value* value);
+
+// Helper for ReadValue that reads a Value::Dict into a pre-allocated object.
+bool ReadDictValue(const base::Pickle* pickle,
+ base::PickleIterator* iter,
+ int recursion,
+ base::Value::Dict* value) {
+ int size;
+ if (!ReadParam(pickle, iter, &size))
+ return false;
+
+ for (int i = 0; i < size; i++) {
+ std::string key;
+ base::Value subvalue;
+ if (!ReadParam(pickle, iter, &key) ||
+ !ReadValue(pickle, iter, recursion + 1, &subvalue)) {
+ return false;
+ }
+ value->Set(key, std::move(subvalue));
+ }
+
+ return true;
+}
+
+// Helper for ReadValue that reads a Value::List into a pre-allocated object.
+bool ReadListValue(const base::Pickle* pickle,
+ base::PickleIterator* iter,
+ int recursion,
+ base::Value::List* value) {
+ int size;
+ if (!ReadParam(pickle, iter, &size))
+ return false;
+
+ value->reserve(size);
+ for (int i = 0; i < size; i++) {
+ base::Value subval;
+ if (!ReadValue(pickle, iter, recursion + 1, &subval))
+ return false;
+ value->Append(std::move(subval));
+ }
+ return true;
+}
+
+bool ReadValue(const base::Pickle* pickle,
+ base::PickleIterator* iter,
+ int recursion,
+ base::Value* value) {
if (recursion > kMaxRecursionDepth) {
LOG(ERROR) << "Max recursion depth hit in ReadValue.";
return false;
}
int type;
- if (!ReadParam(m, iter, &type))
+ if (!ReadParam(pickle, iter, &type))
return false;
constexpr int kMinValueType = static_cast<int>(base::Value::Type::NONE);
@@ -212,28 +221,28 @@
break;
case base::Value::Type::BOOLEAN: {
bool val;
- if (!ReadParam(m, iter, &val))
+ if (!ReadParam(pickle, iter, &val))
return false;
*value = base::Value(val);
break;
}
case base::Value::Type::INTEGER: {
int val;
- if (!ReadParam(m, iter, &val))
+ if (!ReadParam(pickle, iter, &val))
return false;
*value = base::Value(val);
break;
}
case base::Value::Type::DOUBLE: {
double val;
- if (!ReadParam(m, iter, &val))
+ if (!ReadParam(pickle, iter, &val))
return false;
*value = base::Value(val);
break;
}
case base::Value::Type::STRING: {
std::string val;
- if (!ReadParam(m, iter, &val))
+ if (!ReadParam(pickle, iter, &val))
return false;
*value = base::Value(std::move(val));
break;
@@ -246,17 +255,17 @@
break;
}
case base::Value::Type::DICTIONARY: {
- base::DictionaryValue val;
- if (!ReadDictionaryValue(m, iter, &val, recursion))
+ base::Value::Dict val;
+ if (!ReadDictValue(pickle, iter, recursion, &val))
return false;
- *value = std::move(val);
+ *value = base::Value(std::move(val));
break;
}
case base::Value::Type::LIST: {
- base::ListValue val;
- if (!ReadListValue(m, iter, &val, recursion))
+ base::Value::List val;
+ if (!ReadListValue(pickle, iter, recursion, &val))
return false;
- *value = std::move(val);
+ *value = base::Value(std::move(val));
break;
}
default:
@@ -510,18 +519,13 @@
void ParamTraits<base::DictionaryValue>::Write(base::Pickle* m,
const param_type& p) {
- WriteValue(m, &p, 0);
+ WriteDictValue(p.GetDict(), 0, m);
}
bool ParamTraits<base::DictionaryValue>::Read(const base::Pickle* m,
base::PickleIterator* iter,
param_type* r) {
- int type;
- if (!ReadParam(m, iter, &type) ||
- type != static_cast<int>(base::Value::Type::DICTIONARY))
- return false;
-
- return ReadDictionaryValue(m, iter, r, 0);
+ return ReadDictValue(m, iter, 0, &(r->GetDict()));
}
void ParamTraits<base::DictionaryValue>::Log(const param_type& p,
@@ -531,6 +535,23 @@
l->append(json);
}
+void ParamTraits<base::Value::Dict>::Write(base::Pickle* m,
+ const param_type& p) {
+ WriteDictValue(p, 0, m);
+}
+
+bool ParamTraits<base::Value::Dict>::Read(const base::Pickle* m,
+ base::PickleIterator* iter,
+ param_type* r) {
+ return ReadDictValue(m, iter, 0, r);
+}
+
+void ParamTraits<base::Value::Dict>::Log(const param_type& p, std::string* l) {
+ std::string json;
+ base::JSONWriter::Write(p, &json);
+ l->append(json);
+}
+
#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
void ParamTraits<base::FileDescriptor>::Write(base::Pickle* m,
const param_type& p) {
@@ -1157,18 +1178,13 @@
}
void ParamTraits<base::ListValue>::Write(base::Pickle* m, const param_type& p) {
- WriteValue(m, &p, 0);
+ WriteListValue(p.GetList(), 0, m);
}
bool ParamTraits<base::ListValue>::Read(const base::Pickle* m,
base::PickleIterator* iter,
param_type* r) {
- int type;
- if (!ReadParam(m, iter, &type) ||
- type != static_cast<int>(base::Value::Type::LIST))
- return false;
-
- return ReadListValue(m, iter, r, 0);
+ return ReadListValue(m, iter, 0, &(r->GetList()));
}
void ParamTraits<base::ListValue>::Log(const param_type& p, std::string* l) {
@@ -1177,14 +1193,31 @@
l->append(json);
}
+void ParamTraits<base::Value::List>::Write(base::Pickle* m,
+ const param_type& p) {
+ WriteListValue(p, 0, m);
+}
+
+bool ParamTraits<base::Value::List>::Read(const base::Pickle* m,
+ base::PickleIterator* iter,
+ param_type* r) {
+ return ReadListValue(m, iter, 0, r);
+}
+
+void ParamTraits<base::Value::List>::Log(const param_type& p, std::string* l) {
+ std::string json;
+ base::JSONWriter::Write(p, &json);
+ l->append(json);
+}
+
void ParamTraits<base::Value>::Write(base::Pickle* m, const param_type& p) {
- WriteValue(m, &p, 0);
+ WriteValue(p, 0, m);
}
bool ParamTraits<base::Value>::Read(const base::Pickle* m,
base::PickleIterator* iter,
param_type* r) {
- return ReadValue(m, iter, r, 0);
+ return ReadValue(m, iter, 0, r);
}
void ParamTraits<base::Value>::Log(const param_type& p, std::string* l) {
diff --git a/ipc/ipc_message_utils.h b/ipc/ipc_message_utils.h
index baafeea..a2746359 100644
--- a/ipc/ipc_message_utils.h
+++ b/ipc/ipc_message_utils.h
@@ -30,6 +30,7 @@
#include "base/numerics/safe_conversions.h"
#include "base/pickle.h"
#include "base/types/id_type.h"
+#include "base/values.h"
#include "build/build_config.h"
#include "ipc/ipc_buildflags.h"
#include "ipc/ipc_param_traits.h"
@@ -53,16 +54,13 @@
#endif
namespace base {
-class DictionaryValue;
class FilePath;
-class ListValue;
class Time;
class TimeDelta;
class TimeTicks;
class UnguessableToken;
-class Value;
struct FileDescriptor;
-}
+} // namespace base
namespace IPC {
@@ -552,6 +550,16 @@
static void Log(const param_type& p, std::string* l);
};
+template <>
+struct COMPONENT_EXPORT(IPC) ParamTraits<base::Value::Dict> {
+ typedef base::Value::Dict param_type;
+ static void Write(base::Pickle* m, const param_type& p);
+ static bool Read(const base::Pickle* m,
+ base::PickleIterator* iter,
+ param_type* r);
+ static void Log(const param_type& p, std::string* l);
+};
+
#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
// FileDescriptors may be serialised over IPC channels on POSIX. On the
// receiving side, the FileDescriptor is a valid duplicate of the file
@@ -722,6 +730,16 @@
};
template <>
+struct COMPONENT_EXPORT(IPC) ParamTraits<base::Value::List> {
+ typedef base::Value::List param_type;
+ static void Write(base::Pickle* m, const param_type& p);
+ static bool Read(const base::Pickle* m,
+ base::PickleIterator* iter,
+ param_type* r);
+ static void Log(const param_type& p, std::string* l);
+};
+
+template <>
struct COMPONENT_EXPORT(IPC) ParamTraits<base::Value> {
typedef base::Value param_type;
static void Write(base::Pickle* m, const param_type& p);
diff --git a/ipc/ipc_message_utils_unittest.cc b/ipc/ipc_message_utils_unittest.cc
index 34e279e..83bf3db 100644
--- a/ipc/ipc_message_utils_unittest.cc
+++ b/ipc/ipc_message_utils_unittest.cc
@@ -233,6 +233,73 @@
EXPECT_EQ(input, output);
}
+TEST(IPCMessageUtilsTest, LegacyDictValueConversion) {
+ base::DictionaryValue dict_value;
+ dict_value.SetInteger("path1", 42);
+ dict_value.SetInteger("path2", 84);
+ base::ListValue subvalue;
+ subvalue.Append(1234);
+ subvalue.Append(5678);
+ dict_value.SetKey("path3", std::move(subvalue));
+
+ IPC::Message message;
+ ParamTraits<base::DictionaryValue>::Write(&message, dict_value);
+
+ base::PickleIterator iter(message);
+ base::DictionaryValue read_value;
+ ASSERT_TRUE(
+ ParamTraits<base::DictionaryValue>::Read(&message, &iter, &read_value));
+ EXPECT_EQ(dict_value, read_value);
+}
+
+TEST(IPCMessageUtilsTest, DictValueConversion) {
+ base::Value::Dict dict_value;
+ dict_value.Set("path1", 42);
+ dict_value.Set("path2", 84);
+ base::Value::List subvalue;
+ subvalue.Append(1234);
+ subvalue.Append(5678);
+ dict_value.Set("path3", std::move(subvalue));
+
+ IPC::Message message;
+ ParamTraits<base::Value::Dict>::Write(&message, dict_value);
+
+ base::PickleIterator iter(message);
+ base::Value::Dict read_value;
+ ASSERT_TRUE(
+ ParamTraits<base::Value::Dict>::Read(&message, &iter, &read_value));
+ EXPECT_EQ(dict_value, read_value);
+}
+
+TEST(IPCMessageUtilsTest, LegacyListValueConversion) {
+ base::ListValue list_value;
+ list_value.Append(42);
+ list_value.Append(84);
+
+ IPC::Message message;
+ ParamTraits<base::ListValue>::Write(&message, list_value);
+
+ base::PickleIterator iter(message);
+ base::ListValue read_value;
+ ASSERT_TRUE(ParamTraits<base::ListValue>::Read(&message, &iter, &read_value));
+ EXPECT_EQ(list_value, read_value);
+}
+
+TEST(IPCMessageUtilsTest, ListValueConversion) {
+ base::Value::List list_value;
+ list_value.Append(42);
+ list_value.Append(84);
+
+ IPC::Message message;
+ ParamTraits<base::Value::List>::Write(&message, list_value);
+
+ base::PickleIterator iter(message);
+ base::Value::List read_value;
+ ASSERT_TRUE(
+ ParamTraits<base::Value::List>::Read(&message, &iter, &read_value));
+ EXPECT_EQ(list_value, read_value);
+}
+
#if BUILDFLAG(IS_WIN)
TEST(IPCMessageUtilsTest, ScopedHandle) {
HANDLE raw_dupe_handle;