From 23a8cf5cd9e002b27fdb038c44f34faf5ecb6751 Mon Sep 17 00:00:00 2001 From: "dconnelly@chromium.org" Date: Tue, 26 Nov 2013 13:53:37 +0000 Subject: 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 --- chrome/browser/policy/policy_loader_win.cc | 2 +- chrome/browser/policy/preg_parser_win.cc | 2 +- chrome/browser/policy/preg_parser_win_unittest.cc | 2 +- chrome/browser/policy/registry_dict_win.cc | 400 --------------------- chrome/browser/policy/registry_dict_win.h | 90 ----- .../browser/policy/registry_dict_win_unittest.cc | 241 ------------- chrome/chrome_browser.gypi | 2 - chrome/chrome_tests_unit.gypi | 1 - components/components_tests.gyp | 1 + components/policy.gypi | 2 + components/policy/core/common/registry_dict_win.cc | 400 +++++++++++++++++++++ components/policy/core/common/registry_dict_win.h | 91 +++++ .../core/common/registry_dict_win_unittest.cc | 241 +++++++++++++ 13 files changed, 738 insertions(+), 737 deletions(-) delete mode 100644 chrome/browser/policy/registry_dict_win.cc delete mode 100644 chrome/browser/policy/registry_dict_win.h delete mode 100644 chrome/browser/policy/registry_dict_win_unittest.cc create mode 100644 components/policy/core/common/registry_dict_win.cc create mode 100644 components/policy/core/common/registry_dict_win.h create mode 100644 components/policy/core/common/registry_dict_win_unittest.cc diff --git a/chrome/browser/policy/policy_loader_win.cc b/chrome/browser/policy/policy_loader_win.cc index eb39bce..8d89206 100644 --- a/chrome/browser/policy/policy_loader_win.cc +++ b/chrome/browser/policy/policy_loader_win.cc @@ -29,11 +29,11 @@ #include "base/strings/string_util.h" #include "chrome/browser/policy/policy_load_status.h" #include "chrome/browser/policy/preg_parser_win.h" -#include "chrome/browser/policy/registry_dict_win.h" #include "components/json_schema/json_schema_constants.h" #include "components/policy/core/common/policy_bundle.h" #include "components/policy/core/common/policy_map.h" #include "components/policy/core/common/policy_namespace.h" +#include "components/policy/core/common/registry_dict_win.h" #include "components/policy/core/common/schema.h" namespace schema = json_schema_constants; diff --git a/chrome/browser/policy/preg_parser_win.cc b/chrome/browser/policy/preg_parser_win.cc index cd855d3..a30714a 100644 --- a/chrome/browser/policy/preg_parser_win.cc +++ b/chrome/browser/policy/preg_parser_win.cc @@ -21,7 +21,7 @@ #include "base/sys_byteorder.h" #include "base/values.h" #include "chrome/browser/policy/policy_load_status.h" -#include "chrome/browser/policy/registry_dict_win.h" +#include "components/policy/core/common/registry_dict_win.h" namespace policy { namespace preg_parser { diff --git a/chrome/browser/policy/preg_parser_win_unittest.cc b/chrome/browser/policy/preg_parser_win_unittest.cc index 3d219b5..ede4e97 100644 --- a/chrome/browser/policy/preg_parser_win_unittest.cc +++ b/chrome/browser/policy/preg_parser_win_unittest.cc @@ -11,7 +11,7 @@ #include "base/path_service.h" #include "base/values.h" #include "chrome/browser/policy/policy_load_status.h" -#include "chrome/browser/policy/registry_dict_win.h" +#include "components/policy/core/common/registry_dict_win.h" #include "testing/gtest/include/gtest/gtest.h" namespace policy { diff --git a/chrome/browser/policy/registry_dict_win.cc b/chrome/browser/policy/registry_dict_win.cc deleted file mode 100644 index 00a5193..0000000 --- a/chrome/browser/policy/registry_dict_win.cc +++ /dev/null @@ -1,400 +0,0 @@ -// 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 "chrome/browser/policy/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 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 result(new base::DictionaryValue()); - for (base::DictionaryValue::Iterator entry(*dict); !entry.IsAtEnd(); - entry.Advance()) { - scoped_ptr 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 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 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 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 dict) { - if (!dict) { - RemoveKey(name); - return; - } - - RegistryDict*& entry = keys_[name]; - delete entry; - entry = dict.release(); -} - -scoped_ptr RegistryDict::RemoveKey(const std::string& name) { - scoped_ptr 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 dict) { - if (!dict) { - RemoveValue(name); - return; - } - - Value*& entry = values_[name]; - delete entry; - entry = dict.release(); -} - -scoped_ptr RegistryDict::RemoveValue(const std::string& name) { - scoped_ptr 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(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 subdict(new RegistryDict()); - subdict->ReadRegistry(hive, root + L"\\" + it.Name()); - SetKey(name, subdict.Pass()); - } -} - -scoped_ptr 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 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 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/chrome/browser/policy/registry_dict_win.h b/chrome/browser/policy/registry_dict_win.h deleted file mode 100644 index 43abf26..0000000 --- a/chrome/browser/policy/registry_dict_win.h +++ /dev/null @@ -1,90 +0,0 @@ -// 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 CHROME_BROWSER_POLICY_REGISTRY_DICT_WIN_H_ -#define CHROME_BROWSER_POLICY_REGISTRY_DICT_WIN_H_ - -#include - -#include -#include - -#include "base/basictypes.h" -#include "base/memory/scoped_ptr.h" -#include "base/strings/string16.h" - -namespace base { -class DictionaryValue; -class Value; -} - -namespace policy { - -// A case-insensitive string comparison functor. -struct 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 RegistryDict { - public: - typedef std::map KeyMap; - typedef std::map 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 dict); - // Removes a key. If the key doesn't exist, NULL is returned. - scoped_ptr 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 value); - // Removes a value. If the value doesn't exist, NULL is returned. - scoped_ptr 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 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 // CHROME_BROWSER_POLICY_REGISTRY_DICT_WIN_H_ diff --git a/chrome/browser/policy/registry_dict_win_unittest.cc b/chrome/browser/policy/registry_dict_win_unittest.cc deleted file mode 100644 index 6f31397..0000000 --- a/chrome/browser/policy/registry_dict_win_unittest.cc +++ /dev/null @@ -1,241 +0,0 @@ -// 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 "chrome/browser/policy/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 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 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 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 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 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 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 subdict(new RegistryDict()); - subdict->SetValue("two", make_scoped_ptr(string_value.DeepCopy())); - test_dict.SetKey("three", subdict.Pass()); - scoped_ptr list(new RegistryDict()); - list->SetValue("1", make_scoped_ptr(string_value.DeepCopy())); - test_dict.SetKey("four", list.Pass()); - - base::DictionaryValue schema; - scoped_ptr list_schema(new base::DictionaryValue()); - list_schema->SetString(schema::kType, schema::kArray); - scoped_ptr properties(new base::DictionaryValue()); - properties->Set("four", list_schema.release()); - schema.SetString(schema::kType, schema::kObject); - schema.Set(schema::kProperties, properties.release()); - - scoped_ptr actual(test_dict.ConvertToJSON(&schema)); - - base::DictionaryValue expected; - expected.Set("one", int_value.DeepCopy()); - scoped_ptr expected_subdict( - new base::DictionaryValue()); - expected_subdict->Set("two", string_value.DeepCopy()); - expected.Set("three", expected_subdict.release()); - scoped_ptr 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 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 diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index fa6de1d..f904c4f 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1587,8 +1587,6 @@ 'browser/policy/profile_policy_connector.h', 'browser/policy/profile_policy_connector_factory.cc', 'browser/policy/profile_policy_connector_factory.h', - 'browser/policy/registry_dict_win.cc', - 'browser/policy/registry_dict_win.h', 'browser/policy/schema_registry_service.cc', 'browser/policy/schema_registry_service.h', 'browser/policy/schema_registry_service_factory.cc', diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi index 31edbc8..eb7e9c5 100644 --- a/chrome/chrome_tests_unit.gypi +++ b/chrome/chrome_tests_unit.gypi @@ -1135,7 +1135,6 @@ 'browser/policy/policy_statistics_collector_unittest.cc', 'browser/policy/policy_transformations_unittest.cc', 'browser/policy/preg_parser_win_unittest.cc', - 'browser/policy/registry_dict_win_unittest.cc', 'browser/policy/url_blacklist_manager_unittest.cc', 'browser/policy/url_blacklist_policy_handler_unittest.cc', 'browser/predictors/autocomplete_action_predictor_table_unittest.cc', diff --git a/components/components_tests.gyp b/components/components_tests.gyp index 9847b90..e21d2af 100644 --- a/components/components_tests.gyp +++ b/components/components_tests.gyp @@ -177,6 +177,7 @@ 'sources': [ 'policy/core/common/policy_bundle_unittest.cc', 'policy/core/common/policy_map_unittest.cc', + 'policy/core/common/registry_dict_win_unittest.cc', 'policy/core/common/schema_map_unittest.cc', 'policy/core/common/schema_registry_unittest.cc', 'policy/core/common/schema_unittest.cc', diff --git a/components/policy.gypi b/components/policy.gypi index 165c477..a08ba9e 100644 --- a/components/policy.gypi +++ b/components/policy.gypi @@ -35,6 +35,8 @@ 'policy/core/common/policy_switches.cc', 'policy/core/common/policy_switches.h', 'policy/core/common/policy_types.h', + 'policy/core/common/registry_dict_win.cc', + 'policy/core/common/registry_dict_win.h', 'policy/core/common/schema.cc', 'policy/core/common/schema.h', 'policy/core/common/schema_internal.h', 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 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 result(new base::DictionaryValue()); + for (base::DictionaryValue::Iterator entry(*dict); !entry.IsAtEnd(); + entry.Advance()) { + scoped_ptr 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 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 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 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 dict) { + if (!dict) { + RemoveKey(name); + return; + } + + RegistryDict*& entry = keys_[name]; + delete entry; + entry = dict.release(); +} + +scoped_ptr RegistryDict::RemoveKey(const std::string& name) { + scoped_ptr 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 dict) { + if (!dict) { + RemoveValue(name); + return; + } + + Value*& entry = values_[name]; + delete entry; + entry = dict.release(); +} + +scoped_ptr RegistryDict::RemoveValue(const std::string& name) { + scoped_ptr 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(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 subdict(new RegistryDict()); + subdict->ReadRegistry(hive, root + L"\\" + it.Name()); + SetKey(name, subdict.Pass()); + } +} + +scoped_ptr 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 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 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 + +#include +#include + +#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 KeyMap; + typedef std::map 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 dict); + // Removes a key. If the key doesn't exist, NULL is returned. + scoped_ptr 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 value); + // Removes a value. If the value doesn't exist, NULL is returned. + scoped_ptr 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 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 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 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 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 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 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 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 subdict(new RegistryDict()); + subdict->SetValue("two", make_scoped_ptr(string_value.DeepCopy())); + test_dict.SetKey("three", subdict.Pass()); + scoped_ptr list(new RegistryDict()); + list->SetValue("1", make_scoped_ptr(string_value.DeepCopy())); + test_dict.SetKey("four", list.Pass()); + + base::DictionaryValue schema; + scoped_ptr list_schema(new base::DictionaryValue()); + list_schema->SetString(schema::kType, schema::kArray); + scoped_ptr properties(new base::DictionaryValue()); + properties->Set("four", list_schema.release()); + schema.SetString(schema::kType, schema::kObject); + schema.Set(schema::kProperties, properties.release()); + + scoped_ptr actual(test_dict.ConvertToJSON(&schema)); + + base::DictionaryValue expected; + expected.Set("one", int_value.DeepCopy()); + scoped_ptr expected_subdict( + new base::DictionaryValue()); + expected_subdict->Set("two", string_value.DeepCopy()); + expected.Set("three", expected_subdict.release()); + scoped_ptr 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 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 -- cgit v1.1