summaryrefslogtreecommitdiffstats
path: root/components/policy
diff options
context:
space:
mode:
authordconnelly@chromium.org <dconnelly@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-26 13:53:37 +0000
committerdconnelly@chromium.org <dconnelly@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-26 13:53:37 +0000
commit23a8cf5cd9e002b27fdb038c44f34faf5ecb6751 (patch)
tree287c8db5d89332514ed83d61a59c6de4e94cf797 /components/policy
parent6b1445545b12abd150153c9573de92992acd855e (diff)
downloadchromium_src-23a8cf5cd9e002b27fdb038c44f34faf5ecb6751.zip
chromium_src-23a8cf5cd9e002b27fdb038c44f34faf5ecb6751.tar.gz
chromium_src-23a8cf5cd9e002b27fdb038c44f34faf5ecb6751.tar.bz2
Move RegistryDict to components/policy/.
This facilitates the refactoring of chrome/browser/policy into a layered component. BUG=271392 Review URL: https://codereview.chromium.org/79063003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@237330 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'components/policy')
-rw-r--r--components/policy/core/common/registry_dict_win.cc400
-rw-r--r--components/policy/core/common/registry_dict_win.h91
-rw-r--r--components/policy/core/common/registry_dict_win_unittest.cc241
3 files changed, 732 insertions, 0 deletions
diff --git a/components/policy/core/common/registry_dict_win.cc b/components/policy/core/common/registry_dict_win.cc
new file mode 100644
index 0000000..7673079
--- /dev/null
+++ b/components/policy/core/common/registry_dict_win.cc
@@ -0,0 +1,400 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/registry_dict_win.h"
+
+#include "base/json/json_reader.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/sys_byteorder.h"
+#include "base/values.h"
+#include "base/win/registry.h"
+#include "components/json_schema/json_schema_constants.h"
+
+namespace schema = json_schema_constants;
+
+using base::win::RegistryKeyIterator;
+using base::win::RegistryValueIterator;
+
+namespace policy {
+
+namespace {
+
+// Returns the entry with key |name| in |dictionary| (can be NULL), or NULL.
+const base::DictionaryValue* GetEntry(const base::DictionaryValue* dictionary,
+ const std::string& name) {
+ if (!dictionary)
+ return NULL;
+ const base::DictionaryValue* entry = NULL;
+ dictionary->GetDictionaryWithoutPathExpansion(name, &entry);
+ return entry;
+}
+
+// Returns the Value type described in |schema|, or |default_type| if not found.
+base::Value::Type GetValueTypeForSchema(const base::DictionaryValue* schema,
+ base::Value::Type default_type) {
+ // JSON-schema types to base::Value::Type mapping.
+ static const struct {
+ // JSON schema type.
+ const char* schema_type;
+ // Correspondent value type.
+ base::Value::Type value_type;
+ } kSchemaToValueTypeMap[] = {
+ { schema::kArray, base::Value::TYPE_LIST },
+ { schema::kBoolean, base::Value::TYPE_BOOLEAN },
+ { schema::kInteger, base::Value::TYPE_INTEGER },
+ { schema::kNull, base::Value::TYPE_NULL },
+ { schema::kNumber, base::Value::TYPE_DOUBLE },
+ { schema::kObject, base::Value::TYPE_DICTIONARY },
+ { schema::kString, base::Value::TYPE_STRING },
+ };
+
+ if (!schema)
+ return default_type;
+ std::string type;
+ if (!schema->GetStringWithoutPathExpansion(schema::kType, &type))
+ return default_type;
+ for (size_t i = 0; i < arraysize(kSchemaToValueTypeMap); ++i) {
+ if (type == kSchemaToValueTypeMap[i].schema_type)
+ return kSchemaToValueTypeMap[i].value_type;
+ }
+ return default_type;
+}
+
+// Returns the schema for property |name| given the |schema| of an object.
+// Returns the "additionalProperties" schema if no specific schema for
+// |name| is present. Returns NULL if no schema is found.
+const base::DictionaryValue* GetSchemaFor(const base::DictionaryValue* schema,
+ const std::string& name) {
+ const base::DictionaryValue* properties =
+ GetEntry(schema, schema::kProperties);
+ const base::DictionaryValue* sub_schema = GetEntry(properties, name);
+ if (sub_schema)
+ return sub_schema;
+ // "additionalProperties" can be a boolean, but that case is ignored.
+ return GetEntry(schema, schema::kAdditionalProperties);
+}
+
+// Converts a value (as read from the registry) to meet |schema|, converting
+// types as necessary. Unconvertible types will show up as NULL values in the
+// result.
+scoped_ptr<base::Value> ConvertValue(const base::Value& value,
+ const base::DictionaryValue* schema) {
+ // Figure out the type to convert to from the schema.
+ const base::Value::Type result_type(
+ GetValueTypeForSchema(schema, value.GetType()));
+
+ // If the type is good already, go with it.
+ if (value.IsType(result_type)) {
+ // Recurse for complex types if there is a schema.
+ if (schema) {
+ const base::DictionaryValue* dict = NULL;
+ const base::ListValue* list = NULL;
+ if (value.GetAsDictionary(&dict)) {
+ scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue());
+ for (base::DictionaryValue::Iterator entry(*dict); !entry.IsAtEnd();
+ entry.Advance()) {
+ scoped_ptr<base::Value> converted_value(
+ ConvertValue(entry.value(), GetSchemaFor(schema, entry.key())));
+ result->SetWithoutPathExpansion(entry.key(),
+ converted_value.release());
+ }
+ return result.Pass();
+ } else if (value.GetAsList(&list)) {
+ scoped_ptr<base::ListValue> result(new base::ListValue());
+ const base::DictionaryValue* item_schema =
+ GetEntry(schema, schema::kItems);
+ for (base::ListValue::const_iterator entry(list->begin());
+ entry != list->end(); ++entry) {
+ result->Append(ConvertValue(**entry, item_schema).release());
+ }
+ return result.Pass();
+ }
+ }
+ return make_scoped_ptr(value.DeepCopy());
+ }
+
+ // Else, do some conversions to map windows registry data types to JSON types.
+ std::string string_value;
+ int int_value = 0;
+ switch (result_type) {
+ case base::Value::TYPE_NULL: {
+ return make_scoped_ptr(base::Value::CreateNullValue());
+ }
+ case base::Value::TYPE_BOOLEAN: {
+ // Accept booleans encoded as either string or integer.
+ if (value.GetAsInteger(&int_value) ||
+ (value.GetAsString(&string_value) &&
+ base::StringToInt(string_value, &int_value))) {
+ return make_scoped_ptr(Value::CreateBooleanValue(int_value != 0));
+ }
+ break;
+ }
+ case base::Value::TYPE_INTEGER: {
+ // Integers may be string-encoded.
+ if (value.GetAsString(&string_value) &&
+ base::StringToInt(string_value, &int_value)) {
+ return make_scoped_ptr(base::Value::CreateIntegerValue(int_value));
+ }
+ break;
+ }
+ case base::Value::TYPE_DOUBLE: {
+ // Doubles may be string-encoded or integer-encoded.
+ double double_value = 0;
+ if (value.GetAsInteger(&int_value)) {
+ return make_scoped_ptr(base::Value::CreateDoubleValue(int_value));
+ } else if (value.GetAsString(&string_value) &&
+ base::StringToDouble(string_value, &double_value)) {
+ return make_scoped_ptr(base::Value::CreateDoubleValue(double_value));
+ }
+ break;
+ }
+ case base::Value::TYPE_LIST: {
+ // Lists are encoded as subkeys with numbered value in the registry.
+ const base::DictionaryValue* dict = NULL;
+ if (value.GetAsDictionary(&dict)) {
+ scoped_ptr<base::ListValue> result(new base::ListValue());
+ const base::DictionaryValue* item_schema =
+ GetEntry(schema, schema::kItems);
+ for (int i = 1; ; ++i) {
+ const base::Value* entry = NULL;
+ if (!dict->Get(base::IntToString(i), &entry))
+ break;
+ result->Append(ConvertValue(*entry, item_schema).release());
+ }
+ return result.Pass();
+ }
+ // Fall through in order to accept lists encoded as JSON strings.
+ }
+ case base::Value::TYPE_DICTIONARY: {
+ // Dictionaries may be encoded as JSON strings.
+ if (value.GetAsString(&string_value)) {
+ scoped_ptr<base::Value> result(base::JSONReader::Read(string_value));
+ if (result && result->IsType(result_type))
+ return result.Pass();
+ }
+ break;
+ }
+ case base::Value::TYPE_STRING:
+ case base::Value::TYPE_BINARY:
+ // No conversion possible.
+ break;
+ }
+
+ LOG(WARNING) << "Failed to convert " << value.GetType()
+ << " to " << result_type;
+ return make_scoped_ptr(base::Value::CreateNullValue());
+}
+
+} // namespace
+
+bool CaseInsensitiveStringCompare::operator()(const std::string& a,
+ const std::string& b) const {
+ return base::strcasecmp(a.c_str(), b.c_str()) < 0;
+}
+
+RegistryDict::RegistryDict() {}
+
+RegistryDict::~RegistryDict() {
+ ClearKeys();
+ ClearValues();
+}
+
+RegistryDict* RegistryDict::GetKey(const std::string& name) {
+ KeyMap::iterator entry = keys_.find(name);
+ return entry != keys_.end() ? entry->second : NULL;
+}
+
+const RegistryDict* RegistryDict::GetKey(const std::string& name) const {
+ KeyMap::const_iterator entry = keys_.find(name);
+ return entry != keys_.end() ? entry->second : NULL;
+}
+
+void RegistryDict::SetKey(const std::string& name,
+ scoped_ptr<RegistryDict> dict) {
+ if (!dict) {
+ RemoveKey(name);
+ return;
+ }
+
+ RegistryDict*& entry = keys_[name];
+ delete entry;
+ entry = dict.release();
+}
+
+scoped_ptr<RegistryDict> RegistryDict::RemoveKey(const std::string& name) {
+ scoped_ptr<RegistryDict> result;
+ KeyMap::iterator entry = keys_.find(name);
+ if (entry != keys_.end()) {
+ result.reset(entry->second);
+ keys_.erase(entry);
+ }
+ return result.Pass();
+}
+
+void RegistryDict::ClearKeys() {
+ STLDeleteValues(&keys_);
+}
+
+base::Value* RegistryDict::GetValue(const std::string& name) {
+ ValueMap::iterator entry = values_.find(name);
+ return entry != values_.end() ? entry->second : NULL;
+}
+
+const base::Value* RegistryDict::GetValue(const std::string& name) const {
+ ValueMap::const_iterator entry = values_.find(name);
+ return entry != values_.end() ? entry->second : NULL;
+}
+
+void RegistryDict::SetValue(const std::string& name,
+ scoped_ptr<base::Value> dict) {
+ if (!dict) {
+ RemoveValue(name);
+ return;
+ }
+
+ Value*& entry = values_[name];
+ delete entry;
+ entry = dict.release();
+}
+
+scoped_ptr<base::Value> RegistryDict::RemoveValue(const std::string& name) {
+ scoped_ptr<base::Value> result;
+ ValueMap::iterator entry = values_.find(name);
+ if (entry != values_.end()) {
+ result.reset(entry->second);
+ values_.erase(entry);
+ }
+ return result.Pass();
+}
+
+void RegistryDict::ClearValues() {
+ STLDeleteValues(&values_);
+}
+
+void RegistryDict::Merge(const RegistryDict& other) {
+ for (KeyMap::const_iterator entry(other.keys_.begin());
+ entry != other.keys_.end(); ++entry) {
+ RegistryDict*& subdict = keys_[entry->first];
+ if (!subdict)
+ subdict = new RegistryDict();
+ subdict->Merge(*entry->second);
+ }
+
+ for (ValueMap::const_iterator entry(other.values_.begin());
+ entry != other.values_.end(); ++entry) {
+ SetValue(entry->first, make_scoped_ptr(entry->second->DeepCopy()));
+ }
+}
+
+void RegistryDict::Swap(RegistryDict* other) {
+ keys_.swap(other->keys_);
+ values_.swap(other->values_);
+}
+
+void RegistryDict::ReadRegistry(HKEY hive, const string16& root) {
+ ClearKeys();
+ ClearValues();
+
+ // First, read all the values of the key.
+ for (RegistryValueIterator it(hive, root.c_str()); it.Valid(); ++it) {
+ const std::string name = UTF16ToUTF8(it.Name());
+ switch (it.Type()) {
+ case REG_SZ:
+ case REG_EXPAND_SZ:
+ SetValue(
+ name,
+ make_scoped_ptr(new base::StringValue(UTF16ToUTF8(it.Value()))));
+ continue;
+ case REG_DWORD_LITTLE_ENDIAN:
+ case REG_DWORD_BIG_ENDIAN:
+ if (it.ValueSize() == sizeof(DWORD)) {
+ DWORD dword_value = *(reinterpret_cast<const DWORD*>(it.Value()));
+ if (it.Type() == REG_DWORD_BIG_ENDIAN)
+ dword_value = base::NetToHost32(dword_value);
+ else
+ dword_value = base::ByteSwapToLE32(dword_value);
+ SetValue(
+ name,
+ make_scoped_ptr(base::Value::CreateIntegerValue(dword_value)));
+ continue;
+ }
+ case REG_NONE:
+ case REG_LINK:
+ case REG_MULTI_SZ:
+ case REG_RESOURCE_LIST:
+ case REG_FULL_RESOURCE_DESCRIPTOR:
+ case REG_RESOURCE_REQUIREMENTS_LIST:
+ case REG_QWORD_LITTLE_ENDIAN:
+ // Unsupported type, message gets logged below.
+ break;
+ }
+
+ LOG(WARNING) << "Failed to read hive " << hive << " at "
+ << root << "\\" << name
+ << " type " << it.Type();
+ }
+
+ // Recurse for all subkeys.
+ for (RegistryKeyIterator it(hive, root.c_str()); it.Valid(); ++it) {
+ std::string name(UTF16ToUTF8(it.Name()));
+ scoped_ptr<RegistryDict> subdict(new RegistryDict());
+ subdict->ReadRegistry(hive, root + L"\\" + it.Name());
+ SetKey(name, subdict.Pass());
+ }
+}
+
+scoped_ptr<base::Value> RegistryDict::ConvertToJSON(
+ const base::DictionaryValue* schema) const {
+ base::Value::Type type =
+ GetValueTypeForSchema(schema, base::Value::TYPE_DICTIONARY);
+ switch (type) {
+ case base::Value::TYPE_DICTIONARY: {
+ scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue());
+ for (RegistryDict::ValueMap::const_iterator entry(values_.begin());
+ entry != values_.end(); ++entry) {
+ result->SetWithoutPathExpansion(
+ entry->first,
+ ConvertValue(*entry->second,
+ GetSchemaFor(schema, entry->first)).release());
+ }
+ for (RegistryDict::KeyMap::const_iterator entry(keys_.begin());
+ entry != keys_.end(); ++entry) {
+ result->SetWithoutPathExpansion(
+ entry->first,
+ entry->second->ConvertToJSON(
+ GetSchemaFor(schema, entry->first)).release());
+ }
+ return result.Pass();
+ }
+ case base::Value::TYPE_LIST: {
+ scoped_ptr<base::ListValue> result(new base::ListValue());
+ const base::DictionaryValue* item_schema =
+ GetEntry(schema, schema::kItems);
+ for (int i = 1; ; ++i) {
+ const std::string name(base::IntToString(i));
+ const RegistryDict* key = GetKey(name);
+ if (key) {
+ result->Append(key->ConvertToJSON(item_schema).release());
+ continue;
+ }
+ const base::Value* value = GetValue(name);
+ if (value) {
+ result->Append(ConvertValue(*value, item_schema).release());
+ continue;
+ }
+ break;
+ }
+ return result.Pass();
+ }
+ default:
+ LOG(WARNING) << "Can't convert registry key to schema type " << type;
+ }
+
+ return make_scoped_ptr(base::Value::CreateNullValue());
+}
+
+} // namespace policy
diff --git a/components/policy/core/common/registry_dict_win.h b/components/policy/core/common/registry_dict_win.h
new file mode 100644
index 0000000..1cce59b
--- /dev/null
+++ b/components/policy/core/common/registry_dict_win.h
@@ -0,0 +1,91 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_POLICY_CORE_COMMON_REGISTRY_DICT_WIN_H_
+#define COMPONENTS_POLICY_CORE_COMMON_REGISTRY_DICT_WIN_H_
+
+#include <windows.h>
+
+#include <map>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string16.h"
+#include "components/policy/policy_export.h"
+
+namespace base {
+class DictionaryValue;
+class Value;
+}
+
+namespace policy {
+
+// A case-insensitive string comparison functor.
+struct POLICY_EXPORT CaseInsensitiveStringCompare {
+ bool operator()(const std::string& a, const std::string& b) const;
+};
+
+// In-memory representation of a registry subtree. Using a
+// base::DictionaryValue directly seems tempting, but that doesn't handle the
+// registry's case-insensitive-but-case-preserving semantics properly.
+class POLICY_EXPORT RegistryDict {
+ public:
+ typedef std::map<std::string, RegistryDict*,
+ CaseInsensitiveStringCompare> KeyMap;
+ typedef std::map<std::string, base::Value*,
+ CaseInsensitiveStringCompare> ValueMap;
+
+ RegistryDict();
+ ~RegistryDict();
+
+ // Returns a pointer to an existing key, NULL if not present.
+ RegistryDict* GetKey(const std::string& name);
+ const RegistryDict* GetKey(const std::string& name) const;
+ // Sets a key. If |dict| is NULL, clears that key.
+ void SetKey(const std::string& name, scoped_ptr<RegistryDict> dict);
+ // Removes a key. If the key doesn't exist, NULL is returned.
+ scoped_ptr<RegistryDict> RemoveKey(const std::string& name);
+ // Clears all keys.
+ void ClearKeys();
+
+ // Returns a pointer to a value, NULL if not present.
+ base::Value* GetValue(const std::string& name);
+ const base::Value* GetValue(const std::string& name) const;
+ // Sets a value. If |value| is NULL, removes the value.
+ void SetValue(const std::string& name, scoped_ptr<base::Value> value);
+ // Removes a value. If the value doesn't exist, NULL is returned.
+ scoped_ptr<base::Value> RemoveValue(const std::string& name);
+ // Clears all values.
+ void ClearValues();
+
+ // Merge keys and values from |other|, giving precedence to |other|.
+ void Merge(const RegistryDict& other);
+
+ // Swap with |other|.
+ void Swap(RegistryDict* other);
+
+ // Read a Windows registry subtree into this registry dictionary object.
+ void ReadRegistry(HKEY hive, const string16& root);
+
+ // Converts the dictionary to base::Value representation. For key/value name
+ // collisions, the key wins. |schema| supplies an optional JSON schema that
+ // will be used to map types to base::Value types. The returned object is
+ // either a base::DictionaryValue or a base::ListValue.
+ scoped_ptr<base::Value> ConvertToJSON(
+ const base::DictionaryValue* schema) const;
+
+ const KeyMap& keys() const { return keys_; }
+ const ValueMap& values() const { return values_; }
+
+ private:
+ KeyMap keys_;
+ ValueMap values_;
+
+ DISALLOW_COPY_AND_ASSIGN(RegistryDict);
+};
+
+} // namespace policy
+
+#endif // COMPONENTS_POLICY_CORE_COMMON_REGISTRY_DICT_WIN_H_
diff --git a/components/policy/core/common/registry_dict_win_unittest.cc b/components/policy/core/common/registry_dict_win_unittest.cc
new file mode 100644
index 0000000..e413b54
--- /dev/null
+++ b/components/policy/core/common/registry_dict_win_unittest.cc
@@ -0,0 +1,241 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/registry_dict_win.h"
+
+#include "base/values.h"
+#include "components/json_schema/json_schema_constants.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace schema = json_schema_constants;
+
+namespace policy {
+namespace {
+
+TEST(RegistryDictTest, SetAndGetValue) {
+ RegistryDict test_dict;
+
+ base::FundamentalValue int_value(42);
+ base::StringValue string_value("fortytwo");
+
+ test_dict.SetValue("one", make_scoped_ptr(int_value.DeepCopy()));
+ EXPECT_EQ(1, test_dict.values().size());
+ EXPECT_TRUE(base::Value::Equals(&int_value, test_dict.GetValue("one")));
+ EXPECT_FALSE(test_dict.GetValue("two"));
+
+ test_dict.SetValue("two", make_scoped_ptr(string_value.DeepCopy()));
+ EXPECT_EQ(2, test_dict.values().size());
+ EXPECT_TRUE(base::Value::Equals(&int_value, test_dict.GetValue("one")));
+ EXPECT_TRUE(base::Value::Equals(&string_value, test_dict.GetValue("two")));
+
+ scoped_ptr<base::Value> one(test_dict.RemoveValue("one"));
+ EXPECT_EQ(1, test_dict.values().size());
+ EXPECT_TRUE(base::Value::Equals(&int_value, one.get()));
+ EXPECT_FALSE(test_dict.GetValue("one"));
+ EXPECT_TRUE(base::Value::Equals(&string_value, test_dict.GetValue("two")));
+
+ test_dict.ClearValues();
+ EXPECT_FALSE(test_dict.GetValue("one"));
+ EXPECT_FALSE(test_dict.GetValue("two"));
+ EXPECT_TRUE(test_dict.values().empty());
+}
+
+TEST(RegistryDictTest, CaseInsensitiveButPreservingValueNames) {
+ RegistryDict test_dict;
+
+ base::FundamentalValue int_value(42);
+ base::StringValue string_value("fortytwo");
+
+ test_dict.SetValue("One", make_scoped_ptr(int_value.DeepCopy()));
+ EXPECT_EQ(1, test_dict.values().size());
+ EXPECT_TRUE(base::Value::Equals(&int_value, test_dict.GetValue("oNe")));
+
+ RegistryDict::ValueMap::const_iterator entry = test_dict.values().begin();
+ ASSERT_NE(entry, test_dict.values().end());
+ EXPECT_EQ("One", entry->first);
+
+ test_dict.SetValue("ONE", make_scoped_ptr(string_value.DeepCopy()));
+ EXPECT_EQ(1, test_dict.values().size());
+ EXPECT_TRUE(base::Value::Equals(&string_value, test_dict.GetValue("one")));
+
+ scoped_ptr<base::Value> removed_value(test_dict.RemoveValue("onE"));
+ EXPECT_TRUE(base::Value::Equals(&string_value, removed_value.get()));
+ EXPECT_TRUE(test_dict.values().empty());
+}
+
+TEST(RegistryDictTest, SetAndGetKeys) {
+ RegistryDict test_dict;
+
+ base::FundamentalValue int_value(42);
+ base::StringValue string_value("fortytwo");
+
+ scoped_ptr<RegistryDict> subdict(new RegistryDict());
+ subdict->SetValue("one", make_scoped_ptr(int_value.DeepCopy()));
+ test_dict.SetKey("two", subdict.Pass());
+ EXPECT_EQ(1, test_dict.keys().size());
+ RegistryDict* actual_subdict = test_dict.GetKey("two");
+ ASSERT_TRUE(actual_subdict);
+ EXPECT_TRUE(base::Value::Equals(&int_value, actual_subdict->GetValue("one")));
+
+ subdict.reset(new RegistryDict());
+ subdict->SetValue("three", make_scoped_ptr(string_value.DeepCopy()));
+ test_dict.SetKey("four", subdict.Pass());
+ EXPECT_EQ(2, test_dict.keys().size());
+ actual_subdict = test_dict.GetKey("two");
+ ASSERT_TRUE(actual_subdict);
+ EXPECT_TRUE(base::Value::Equals(&int_value, actual_subdict->GetValue("one")));
+ actual_subdict = test_dict.GetKey("four");
+ ASSERT_TRUE(actual_subdict);
+ EXPECT_TRUE(base::Value::Equals(&string_value,
+ actual_subdict->GetValue("three")));
+
+ test_dict.ClearKeys();
+ EXPECT_FALSE(test_dict.GetKey("one"));
+ EXPECT_FALSE(test_dict.GetKey("three"));
+ EXPECT_TRUE(test_dict.keys().empty());
+}
+
+TEST(RegistryDictTest, CaseInsensitiveButPreservingKeyNames) {
+ RegistryDict test_dict;
+
+ base::FundamentalValue int_value(42);
+
+ test_dict.SetKey("One", make_scoped_ptr(new RegistryDict()));
+ EXPECT_EQ(1, test_dict.keys().size());
+ RegistryDict* actual_subdict = test_dict.GetKey("One");
+ ASSERT_TRUE(actual_subdict);
+ EXPECT_TRUE(actual_subdict->values().empty());
+
+ RegistryDict::KeyMap::const_iterator entry = test_dict.keys().begin();
+ ASSERT_NE(entry, test_dict.keys().end());
+ EXPECT_EQ("One", entry->first);
+
+ scoped_ptr<RegistryDict> subdict(new RegistryDict());
+ subdict->SetValue("two", make_scoped_ptr(int_value.DeepCopy()));
+ test_dict.SetKey("ONE", subdict.Pass());
+ EXPECT_EQ(1, test_dict.keys().size());
+ actual_subdict = test_dict.GetKey("One");
+ ASSERT_TRUE(actual_subdict);
+ EXPECT_TRUE(base::Value::Equals(&int_value,
+ actual_subdict->GetValue("two")));
+
+ scoped_ptr<RegistryDict> removed_key(test_dict.RemoveKey("one"));
+ ASSERT_TRUE(removed_key);
+ EXPECT_TRUE(base::Value::Equals(&int_value,
+ removed_key->GetValue("two")));
+ EXPECT_TRUE(test_dict.keys().empty());
+}
+
+TEST(RegistryDictTest, Merge) {
+ RegistryDict dict_a;
+ RegistryDict dict_b;
+
+ base::FundamentalValue int_value(42);
+ base::StringValue string_value("fortytwo");
+
+ dict_a.SetValue("one", make_scoped_ptr(int_value.DeepCopy()));
+ scoped_ptr<RegistryDict> subdict(new RegistryDict());
+ subdict->SetValue("two", make_scoped_ptr(string_value.DeepCopy()));
+ dict_a.SetKey("three", subdict.Pass());
+
+ dict_b.SetValue("four", make_scoped_ptr(string_value.DeepCopy()));
+ subdict.reset(new RegistryDict());
+ subdict->SetValue("two", make_scoped_ptr(int_value.DeepCopy()));
+ dict_b.SetKey("three", subdict.Pass());
+ subdict.reset(new RegistryDict());
+ subdict->SetValue("five", make_scoped_ptr(int_value.DeepCopy()));
+ dict_b.SetKey("six", subdict.Pass());
+
+ dict_a.Merge(dict_b);
+
+ EXPECT_TRUE(base::Value::Equals(&int_value, dict_a.GetValue("one")));
+ EXPECT_TRUE(base::Value::Equals(&string_value, dict_b.GetValue("four")));
+ RegistryDict* actual_subdict = dict_a.GetKey("three");
+ ASSERT_TRUE(actual_subdict);
+ EXPECT_TRUE(base::Value::Equals(&int_value, actual_subdict->GetValue("two")));
+ actual_subdict = dict_a.GetKey("six");
+ ASSERT_TRUE(actual_subdict);
+ EXPECT_TRUE(base::Value::Equals(&int_value,
+ actual_subdict->GetValue("five")));
+}
+
+TEST(RegistryDictTest, Swap) {
+ RegistryDict dict_a;
+ RegistryDict dict_b;
+
+ base::FundamentalValue int_value(42);
+ base::StringValue string_value("fortytwo");
+
+ dict_a.SetValue("one", make_scoped_ptr(int_value.DeepCopy()));
+ dict_a.SetKey("two", make_scoped_ptr(new RegistryDict()));
+ dict_b.SetValue("three", make_scoped_ptr(string_value.DeepCopy()));
+
+ dict_a.Swap(&dict_b);
+
+ EXPECT_TRUE(base::Value::Equals(&int_value, dict_b.GetValue("one")));
+ EXPECT_TRUE(dict_b.GetKey("two"));
+ EXPECT_FALSE(dict_b.GetValue("two"));
+
+ EXPECT_TRUE(base::Value::Equals(&string_value, dict_a.GetValue("three")));
+ EXPECT_FALSE(dict_a.GetValue("one"));
+ EXPECT_FALSE(dict_a.GetKey("two"));
+}
+
+TEST(RegistryDictTest, ConvertToJSON) {
+ RegistryDict test_dict;
+
+ base::FundamentalValue int_value(42);
+ base::StringValue string_value("fortytwo");
+
+ test_dict.SetValue("one", make_scoped_ptr(int_value.DeepCopy()));
+ scoped_ptr<RegistryDict> subdict(new RegistryDict());
+ subdict->SetValue("two", make_scoped_ptr(string_value.DeepCopy()));
+ test_dict.SetKey("three", subdict.Pass());
+ scoped_ptr<RegistryDict> list(new RegistryDict());
+ list->SetValue("1", make_scoped_ptr(string_value.DeepCopy()));
+ test_dict.SetKey("four", list.Pass());
+
+ base::DictionaryValue schema;
+ scoped_ptr<base::DictionaryValue> list_schema(new base::DictionaryValue());
+ list_schema->SetString(schema::kType, schema::kArray);
+ scoped_ptr<base::DictionaryValue> properties(new base::DictionaryValue());
+ properties->Set("four", list_schema.release());
+ schema.SetString(schema::kType, schema::kObject);
+ schema.Set(schema::kProperties, properties.release());
+
+ scoped_ptr<base::Value> actual(test_dict.ConvertToJSON(&schema));
+
+ base::DictionaryValue expected;
+ expected.Set("one", int_value.DeepCopy());
+ scoped_ptr<base::DictionaryValue> expected_subdict(
+ new base::DictionaryValue());
+ expected_subdict->Set("two", string_value.DeepCopy());
+ expected.Set("three", expected_subdict.release());
+ scoped_ptr<base::ListValue> expected_list(new base::ListValue());
+ expected_list->Append(string_value.DeepCopy());
+ expected.Set("four", expected_list.release());
+
+ EXPECT_TRUE(base::Value::Equals(actual.get(), &expected));
+}
+
+TEST(RegistryDictTest, KeyValueNameClashes) {
+ RegistryDict test_dict;
+
+ base::FundamentalValue int_value(42);
+ base::StringValue string_value("fortytwo");
+
+ test_dict.SetValue("one", make_scoped_ptr(int_value.DeepCopy()));
+ scoped_ptr<RegistryDict> subdict(new RegistryDict());
+ subdict->SetValue("two", make_scoped_ptr(string_value.DeepCopy()));
+ test_dict.SetKey("one", subdict.Pass());
+
+ EXPECT_TRUE(base::Value::Equals(&int_value, test_dict.GetValue("one")));
+ RegistryDict* actual_subdict = test_dict.GetKey("one");
+ ASSERT_TRUE(actual_subdict);
+ EXPECT_TRUE(base::Value::Equals(&string_value,
+ actual_subdict->GetValue("two")));
+}
+
+} // namespace
+} // namespace policy