libchrome: Add preg_parser and registry_dict

preg_parser and registry_dict are used by authpolicy to fetch policy
from AD servers. Right now, a custom version of the two files is
checked into platform2 in Chromium OS. This CL puts the Chromium copy
of these files into libchrome, so that authpolicy can reuse this code.

Revision in chromium/src of added files:
f601211275a7ed8a187d49099ea3c81c4168b445
Jan 10 2017, Chrome revision 57.0.2978.0, r442564

BUG=chromium:659645
TEST=Compiles, policy lib is accessible by authpolicy code.

Change-Id: I221a73b50f58673369a5d1c28a1a742b28ed67a6
Reviewed-on: https://chromium-review.googlesource.com/426860
Commit-Ready: Lutz Justen <ljusten@chromium.org>
Tested-by: Lutz Justen <ljusten@chromium.org>
Reviewed-by: Dan Erat <derat@chromium.org>
diff --git a/PRESUBMIT.cfg b/PRESUBMIT.cfg
new file mode 100644
index 0000000..9fa9fcb
--- /dev/null
+++ b/PRESUBMIT.cfg
@@ -0,0 +1,5 @@
+# These files are straight copies from Chromium and keep their license notice.
+
+[Hook Overrides]
+cros_license_check: false
+
diff --git a/SConstruct b/SConstruct
index 5b413a7..483159c 100644
--- a/SConstruct
+++ b/SConstruct
@@ -265,6 +265,17 @@
     'pc_libs' : '',
   },
   {
+    'name' : 'policy',
+    'sources' : """
+                core/common/policy_load_status.cc
+                core/common/preg_parser.cc
+                core/common/registry_dict.cc
+                """,
+    'prefix' : 'components/policy',
+    'libs' : '',
+    'pc_libs' : '',
+  },
+  {
     'name' : 'crypto',
     'sources' : """
                 hmac.cc
diff --git a/components/policy/core/common/policy_load_status.cc b/components/policy/core/common/policy_load_status.cc
new file mode 100644
index 0000000..71c5059
--- /dev/null
+++ b/components/policy/core/common/policy_load_status.cc
@@ -0,0 +1,38 @@
+// Copyright (c) 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/policy_load_status.h"
+
+#include "base/metrics/histogram.h"
+#include "base/strings/stringprintf.h"
+#include "components/policy/core/common/policy_types.h"
+
+namespace policy {
+
+namespace {
+
+const char kHistogramName[] = "Enterprise.PolicyLoadStatus";
+
+}  // namespace
+
+PolicyLoadStatusSample::PolicyLoadStatusSample()
+    : histogram_(base::LinearHistogram::FactoryGet(
+          kHistogramName, 1, POLICY_LOAD_STATUS_SIZE,
+          POLICY_LOAD_STATUS_SIZE + 1,
+          base::Histogram::kUmaTargetedHistogramFlag)) {
+  Add(POLICY_LOAD_STATUS_STARTED);
+}
+
+PolicyLoadStatusSample::~PolicyLoadStatusSample() {
+  for (int i = 0; i < POLICY_LOAD_STATUS_SIZE; ++i) {
+    if (status_bits_[i])
+      histogram_->Add(i);
+  }
+}
+
+void PolicyLoadStatusSample::Add(PolicyLoadStatus status) {
+  status_bits_[status] = true;
+}
+
+}  // namespace policy
diff --git a/components/policy/core/common/policy_load_status.h b/components/policy/core/common/policy_load_status.h
new file mode 100644
index 0000000..6c9e907
--- /dev/null
+++ b/components/policy/core/common/policy_load_status.h
@@ -0,0 +1,67 @@
+// Copyright (c) 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_POLICY_LOAD_STATUS_H_
+#define COMPONENTS_POLICY_CORE_COMMON_POLICY_LOAD_STATUS_H_
+
+#include <bitset>
+
+#include "base/macros.h"
+#include "components/policy/policy_export.h"
+
+namespace base {
+class HistogramBase;
+}
+
+namespace policy {
+
+// UMA histogram enum for policy load status. Don't change existing constants,
+// append additional constants to the end if needed.
+enum PolicyLoadStatus {
+  // Policy load attempt started. This gets logged for each policy load attempt
+  // to get a baseline on the number of requests, and an arbitrary number of
+  // the below status codes may get added in addition.
+  POLICY_LOAD_STATUS_STARTED = 0,
+  // System failed to determine whether there's policy.
+  POLICY_LOAD_STATUS_QUERY_FAILED = 1,
+  // No policy present.
+  POLICY_LOAD_STATUS_NO_POLICY = 2,
+  // Data inaccessible, such as non-local policy file.
+  POLICY_LOAD_STATUS_INACCCESSIBLE = 3,
+  // Data missing, such as policy file not present.
+  POLICY_LOAD_STATUS_MISSING = 4,
+  // Trying with Wow64 redirection disabled.
+  POLICY_LOAD_STATUS_WOW64_REDIRECTION_DISABLED = 5,
+  // Data read error, for example file reading errors.
+  POLICY_LOAD_STATUS_READ_ERROR = 6,
+  // Data too large to process.
+  POLICY_LOAD_STATUS_TOO_BIG = 7,
+  // Parse error.
+  POLICY_LOAD_STATUS_PARSE_ERROR = 8,
+
+  // This must stay last.
+  POLICY_LOAD_STATUS_SIZE
+};
+
+// A helper for generating policy load status UMA statistics that'll collect
+// histogram samples for a policy load operation and records histogram samples
+// for the status codes that were seen on destruction.
+class POLICY_EXPORT PolicyLoadStatusSample {
+ public:
+  PolicyLoadStatusSample();
+  ~PolicyLoadStatusSample();
+
+  // Adds a status code.
+  void Add(PolicyLoadStatus status);
+
+ private:
+  std::bitset<POLICY_LOAD_STATUS_SIZE> status_bits_;
+  base::HistogramBase* histogram_;
+
+  DISALLOW_COPY_AND_ASSIGN(PolicyLoadStatusSample);
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_CORE_COMMON_POLICY_LOAD_STATUS_H_
diff --git a/components/policy/core/common/policy_types.h b/components/policy/core/common/policy_types.h
new file mode 100644
index 0000000..7595f86
--- /dev/null
+++ b/components/policy/core/common/policy_types.h
@@ -0,0 +1,58 @@
+// 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_POLICY_TYPES_H_
+#define COMPONENTS_POLICY_CORE_COMMON_POLICY_TYPES_H_
+
+namespace policy {
+
+// The scope of a policy flags whether it is meant to be applied to the current
+// user or to the machine.  Note that this property pertains to the source of
+// the policy and has no direct correspondence to the distinction between User
+// Policy and Device Policy.
+enum PolicyScope {
+  // USER policies apply to sessions of the current user.
+  POLICY_SCOPE_USER,
+
+  // MACHINE policies apply to any users of the current machine.
+  POLICY_SCOPE_MACHINE,
+};
+
+// The level of a policy determines its enforceability and whether users can
+// override it or not. The values are listed in increasing order of priority.
+enum PolicyLevel {
+  // RECOMMENDED policies can be overridden by users. They are meant as a
+  // default value configured by admins, that users can customize.
+  POLICY_LEVEL_RECOMMENDED,
+
+  // MANDATORY policies must be enforced and users can't circumvent them.
+  POLICY_LEVEL_MANDATORY,
+};
+
+// The source of a policy indicates where its value is originating from. The
+// sources are ordered by priority (with weakest policy first).
+enum PolicySource {
+  // The policy was set because we are running in an enterprise environment.
+  POLICY_SOURCE_ENTERPRISE_DEFAULT,
+
+  // The policy was set by a cloud source.
+  POLICY_SOURCE_CLOUD,
+
+  // The policy was set by an Active Directory source.
+  POLICY_SOURCE_ACTIVE_DIRECTORY,
+
+  // Any non-platform policy was overridden because we are running in a
+  // public session.
+  POLICY_SOURCE_PUBLIC_SESSION_OVERRIDE,
+
+  // The policy was set by a platform source.
+  POLICY_SOURCE_PLATFORM,
+
+  // Number of source types. Has to be the last element.
+  POLICY_SOURCE_COUNT
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_CORE_COMMON_POLICY_TYPES_H_
diff --git a/components/policy/core/common/preg_parser.cc b/components/policy/core/common/preg_parser.cc
new file mode 100644
index 0000000..1fd7c31
--- /dev/null
+++ b/components/policy/core/common/preg_parser.cc
@@ -0,0 +1,352 @@
+// Copyright (c) 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/preg_parser.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <algorithm>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/files/memory_mapped_file.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/sys_byteorder.h"
+#include "base/values.h"
+#include "components/policy/core/common/policy_load_status.h"
+#include "components/policy/core/common/registry_dict.h"
+
+#if defined(OS_WIN)
+#include "windows.h"
+#else
+// Registry data type constants.
+#define REG_NONE 0
+#define REG_SZ 1
+#define REG_EXPAND_SZ 2
+#define REG_BINARY 3
+#define REG_DWORD_LITTLE_ENDIAN 4
+#define REG_DWORD_BIG_ENDIAN 5
+#define REG_LINK 6
+#define REG_MULTI_SZ 7
+#define REG_RESOURCE_LIST 8
+#define REG_FULL_RESOURCE_DESCRIPTOR 9
+#define REG_RESOURCE_REQUIREMENTS_LIST 10
+#define REG_QWORD_LITTLE_ENDIAN 11
+#endif
+
+using RegistryDict = policy::RegistryDict;
+
+namespace {
+
+// Maximum PReg file size we're willing to accept.
+const int64_t kMaxPRegFileSize = 1024 * 1024 * 16;
+static_assert(kMaxPRegFileSize <= std::numeric_limits<ptrdiff_t>::max(),
+              "Max PReg file size too large.");
+
+// Constants for PReg file delimiters.
+const base::char16 kDelimBracketOpen = L'[';
+const base::char16 kDelimBracketClose = L']';
+const base::char16 kDelimSemicolon = L';';
+
+// Registry path separator.
+const base::string16 kRegistryPathSeparator = base::ASCIIToUTF16("\\");
+
+// Magic strings for the PReg value field to trigger special actions.
+const char kActionTriggerPrefix[] = "**";
+const char kActionTriggerDeleteValues[] = "deletevalues";
+const char kActionTriggerDel[] = "del.";
+const char kActionTriggerDelVals[] = "delvals";
+const char kActionTriggerDeleteKeys[] = "deletekeys";
+const char kActionTriggerSecureKey[] = "securekey";
+const char kActionTriggerSoft[] = "soft";
+
+// Returns the character at |cursor| and increments it, unless the end is here
+// in which case -1 is returned. The calling code must guarantee that
+// end - *cursor does not overflow ptrdiff_t.
+int NextChar(const uint8_t** cursor, const uint8_t* end) {
+  // Only read the character if a full base::char16 is available.
+  // This comparison makes sure no overflow can happen.
+  if (*cursor >= end ||
+      end - *cursor < static_cast<ptrdiff_t>(sizeof(base::char16)))
+    return -1;
+
+  int result = **cursor | (*(*cursor + 1) << 8);
+  *cursor += sizeof(base::char16);
+  return result;
+}
+
+// Reads a fixed-size field from a PReg file. The calling code must guarantee
+// that both end - *cursor and size do not overflow ptrdiff_t.
+bool ReadFieldBinary(const uint8_t** cursor,
+                     const uint8_t* end,
+                     uint32_t size,
+                     uint8_t* data) {
+  if (size == 0)
+    return true;
+
+  // Be careful to prevent possible overflows here (don't do *cursor + size).
+  if (*cursor >= end || end - *cursor < static_cast<ptrdiff_t>(size))
+    return false;
+  const uint8_t* field_end = *cursor + size;
+  std::copy(*cursor, field_end, data);
+  *cursor = field_end;
+  return true;
+}
+
+bool ReadField32(const uint8_t** cursor, const uint8_t* end, uint32_t* data) {
+  uint32_t value = 0;
+  if (!ReadFieldBinary(cursor, end, sizeof(uint32_t),
+                       reinterpret_cast<uint8_t*>(&value))) {
+    return false;
+  }
+  *data = base::ByteSwapToLE32(value);
+  return true;
+}
+
+// Reads a string field from a file.
+bool ReadFieldString(const uint8_t** cursor,
+                     const uint8_t* end,
+                     base::string16* str) {
+  int current = -1;
+  while ((current = NextChar(cursor, end)) > 0x0000)
+    *str += current;
+
+  return current == L'\0';
+}
+
+std::string DecodePRegStringValue(const std::vector<uint8_t>& data) {
+  size_t len = data.size() / sizeof(base::char16);
+  if (len <= 0)
+    return std::string();
+
+  const base::char16* chars =
+      reinterpret_cast<const base::char16*>(data.data());
+  base::string16 result;
+  std::transform(chars, chars + len - 1, std::back_inserter(result),
+                 std::ptr_fun(base::ByteSwapToLE16));
+  return base::UTF16ToUTF8(result);
+}
+
+// Decodes a value from a PReg file given as a uint8_t vector.
+bool DecodePRegValue(uint32_t type,
+                     const std::vector<uint8_t>& data,
+                     std::unique_ptr<base::Value>* value) {
+  switch (type) {
+    case REG_SZ:
+    case REG_EXPAND_SZ:
+      value->reset(new base::StringValue(DecodePRegStringValue(data)));
+      return true;
+    case REG_DWORD_LITTLE_ENDIAN:
+    case REG_DWORD_BIG_ENDIAN:
+      if (data.size() == sizeof(uint32_t)) {
+        uint32_t val = *reinterpret_cast<const uint32_t*>(data.data());
+        if (type == REG_DWORD_BIG_ENDIAN)
+          val = base::NetToHost32(val);
+        else
+          val = base::ByteSwapToLE32(val);
+        value->reset(new base::FundamentalValue(static_cast<int>(val)));
+        return true;
+      } else {
+        LOG(ERROR) << "Bad data size " << data.size();
+      }
+      break;
+    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:
+    default:
+      LOG(ERROR) << "Unsupported registry data type " << type;
+  }
+
+  return false;
+}
+
+// Adds |value| and |data| to |dict| or an appropriate sub-dictionary indicated
+// by |key_name|. Creates sub-dictionaries if necessary. Also handles special
+// action triggers, see |kActionTrigger*|, that can, for instance, remove an
+// existing value.
+void HandleRecord(const base::string16& key_name,
+                  const base::string16& value,
+                  uint32_t type,
+                  const std::vector<uint8_t>& data,
+                  RegistryDict* dict) {
+  // Locate/create the dictionary to place the value in.
+  std::vector<base::string16> path;
+
+  for (const base::string16& entry :
+       base::SplitString(key_name, kRegistryPathSeparator,
+                         base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
+    if (entry.empty())
+      continue;
+    const std::string name = base::UTF16ToUTF8(entry);
+    RegistryDict* subdict = dict->GetKey(name);
+    if (!subdict) {
+      subdict = new RegistryDict();
+      dict->SetKey(name, base::WrapUnique(subdict));
+    }
+    dict = subdict;
+  }
+
+  if (value.empty())
+    return;
+
+  std::string value_name(base::UTF16ToUTF8(value));
+  if (!base::StartsWith(value_name, kActionTriggerPrefix,
+                        base::CompareCase::SENSITIVE)) {
+    std::unique_ptr<base::Value> value;
+    if (DecodePRegValue(type, data, &value))
+      dict->SetValue(value_name, std::move(value));
+    return;
+  }
+
+  std::string action_trigger(base::ToLowerASCII(value_name.substr(
+      arraysize(kActionTriggerPrefix) - 1)));
+  if (action_trigger == kActionTriggerDeleteValues) {
+    for (const std::string& value :
+         base::SplitString(DecodePRegStringValue(data), ";",
+                           base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY))
+      dict->RemoveValue(value);
+  } else if (base::StartsWith(action_trigger, kActionTriggerDeleteKeys,
+                              base::CompareCase::SENSITIVE)) {
+    for (const std::string& key :
+         base::SplitString(DecodePRegStringValue(data), ";",
+                           base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY))
+      dict->RemoveKey(key);
+  } else if (base::StartsWith(action_trigger, kActionTriggerDel,
+                              base::CompareCase::SENSITIVE)) {
+  dict->RemoveValue(
+        value_name.substr(arraysize(kActionTriggerPrefix) - 1 +
+                          arraysize(kActionTriggerDel) - 1));
+  } else if (base::StartsWith(action_trigger, kActionTriggerDelVals,
+                              base::CompareCase::SENSITIVE)) {
+    // Delete all values.
+    dict->ClearValues();
+  } else if (base::StartsWith(action_trigger, kActionTriggerSecureKey,
+                              base::CompareCase::SENSITIVE) ||
+             base::StartsWith(action_trigger, kActionTriggerSoft,
+                              base::CompareCase::SENSITIVE)) {
+    // Doesn't affect values.
+  } else {
+    LOG(ERROR) << "Bad action trigger " << value_name;
+  }
+}
+
+}  // namespace
+
+namespace policy {
+namespace preg_parser {
+
+const char kPRegFileHeader[8] =
+    { 'P', 'R', 'e', 'g', '\x01', '\x00', '\x00', '\x00' };
+
+bool ReadFile(const base::FilePath& file_path,
+              const base::string16& root,
+              RegistryDict* dict,
+              PolicyLoadStatusSample* status) {
+  base::MemoryMappedFile mapped_file;
+  if (!mapped_file.Initialize(file_path) || !mapped_file.IsValid()) {
+    PLOG(ERROR) << "Failed to map " << file_path.value();
+    status->Add(POLICY_LOAD_STATUS_READ_ERROR);
+    return false;
+  }
+
+  if (mapped_file.length() > kMaxPRegFileSize) {
+    LOG(ERROR) << "PReg file " << file_path.value() << " too large: "
+               << mapped_file.length();
+    status->Add(POLICY_LOAD_STATUS_TOO_BIG);
+    return false;
+  }
+
+  // Check the header.
+  const int kHeaderSize = arraysize(kPRegFileHeader);
+  if (mapped_file.length() < kHeaderSize ||
+      memcmp(kPRegFileHeader, mapped_file.data(), kHeaderSize) != 0) {
+    LOG(ERROR) << "Bad policy file " << file_path.value();
+    status->Add(POLICY_LOAD_STATUS_PARSE_ERROR);
+    return false;
+  }
+
+  // Parse file contents, which is UCS-2 and little-endian. The latter I
+  // couldn't find documentation on, but the example I saw were all
+  // little-endian. It'd be interesting to check on big-endian hardware.
+  const uint8_t* cursor = mapped_file.data() + kHeaderSize;
+  const uint8_t* end = mapped_file.data() + mapped_file.length();
+  while (true) {
+    if (cursor == end)
+      return true;
+
+    if (NextChar(&cursor, end) != kDelimBracketOpen)
+      break;
+
+    // Read the record fields.
+    base::string16 key_name;
+    base::string16 value;
+    uint32_t type = 0;
+    uint32_t size = 0;
+    std::vector<uint8_t> data;
+
+    if (!ReadFieldString(&cursor, end, &key_name))
+      break;
+
+    int current = NextChar(&cursor, end);
+    if (current == kDelimSemicolon) {
+      if (!ReadFieldString(&cursor, end, &value))
+        break;
+      current = NextChar(&cursor, end);
+    }
+
+    if (current == kDelimSemicolon) {
+      if (!ReadField32(&cursor, end, &type))
+        break;
+      current = NextChar(&cursor, end);
+    }
+
+    if (current == kDelimSemicolon) {
+      if (!ReadField32(&cursor, end, &size))
+        break;
+      current = NextChar(&cursor, end);
+    }
+
+    if (current == kDelimSemicolon) {
+      if (size > kMaxPRegFileSize)
+        break;
+      data.resize(size);
+      if (!ReadFieldBinary(&cursor, end, size, data.data()))
+        break;
+      current = NextChar(&cursor, end);
+    }
+
+    if (current != kDelimBracketClose)
+      break;
+
+    // Process the record if it is within the |root| subtree.
+    if (base::StartsWith(key_name, root, base::CompareCase::INSENSITIVE_ASCII))
+      HandleRecord(key_name.substr(root.size()), value, type, data, dict);
+  }
+
+  LOG(ERROR) << "Error parsing " << file_path.value() << " at offset "
+             << reinterpret_cast<const uint8_t*>(cursor - 1) -
+                    mapped_file.data();
+  status->Add(POLICY_LOAD_STATUS_PARSE_ERROR);
+  return false;
+}
+
+}  // namespace preg_parser
+}  // namespace policy
diff --git a/components/policy/core/common/preg_parser.h b/components/policy/core/common/preg_parser.h
new file mode 100644
index 0000000..03872a4
--- /dev/null
+++ b/components/policy/core/common/preg_parser.h
@@ -0,0 +1,44 @@
+// Copyright (c) 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.
+
+// This file provides a parser for PReg files which are used for storing group
+// policy settings in the file system. The file format is documented here:
+//
+// http://msdn.microsoft.com/en-us/library/windows/desktop/aa374407(v=vs.85).aspx
+
+#ifndef COMPONENTS_POLICY_CORE_COMMON_PREG_PARSER_H_
+#define COMPONENTS_POLICY_CORE_COMMON_PREG_PARSER_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/strings/string16.h"
+#include "components/policy/policy_export.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace policy {
+
+class PolicyLoadStatusSample;
+class RegistryDict;
+
+namespace preg_parser {
+
+// The magic header in PReg files: ASCII "PReg" + version (0x0001).
+POLICY_EXPORT extern const char kPRegFileHeader[8];
+
+// Reads the PReg file at |file_path| and writes the registry data to |dict|.
+// |root| specifies the registry subtree the caller is interested in,
+// everything else gets ignored.
+POLICY_EXPORT bool ReadFile(const base::FilePath& file_path,
+                            const base::string16& root,
+                            RegistryDict* dict,
+                            PolicyLoadStatusSample* status);
+
+}  // namespace preg_parser
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_CORE_COMMON_PREG_PARSER_H_
diff --git a/components/policy/core/common/preg_parser_unittest.cc b/components/policy/core/common/preg_parser_unittest.cc
new file mode 100644
index 0000000..153f75a
--- /dev/null
+++ b/components/policy/core/common/preg_parser_unittest.cc
@@ -0,0 +1,123 @@
+// Copyright (c) 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/preg_parser.h"
+
+#include <utility>
+
+#include "base/base_paths.h"
+#include "base/files/file_path.h"
+#include "base/json/json_writer.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/path_service.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "components/policy/core/common/policy_load_status.h"
+#include "components/policy/core/common/registry_dict.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace policy {
+namespace preg_parser {
+namespace {
+
+// Check whether two RegistryDicts equal each other.
+testing::AssertionResult RegistryDictEquals(const RegistryDict& a,
+                                            const RegistryDict& b) {
+  auto iter_key_a = a.keys().begin();
+  auto iter_key_b = b.keys().begin();
+  for (; iter_key_a != a.keys().end() && iter_key_b != b.keys().end();
+       ++iter_key_a, ++iter_key_b) {
+    if (iter_key_a->first != iter_key_b->first) {
+      return testing::AssertionFailure()
+          << "Key mismatch " << iter_key_a->first
+          << " vs. " << iter_key_b->first;
+    }
+    testing::AssertionResult result = RegistryDictEquals(*iter_key_a->second,
+                                                         *iter_key_b->second);
+    if (!result)
+      return result;
+  }
+
+  auto iter_value_a = a.values().begin();
+  auto iter_value_b = b.values().begin();
+  for (; iter_value_a != a.values().end() && iter_value_b != b.values().end();
+       ++iter_value_a, ++iter_value_b) {
+    if (iter_value_a->first != iter_value_b->first ||
+        !base::Value::Equals(iter_value_a->second.get(),
+                             iter_value_b->second.get())) {
+      return testing::AssertionFailure()
+             << "Value mismatch " << iter_value_a->first << "="
+             << *iter_value_a->second.get() << " vs. " << iter_value_b->first
+             << "=" << *iter_value_b->second.get();
+    }
+  }
+
+  return testing::AssertionSuccess();
+}
+
+void SetInteger(RegistryDict* dict,
+                const std::string& name,
+                int value) {
+  dict->SetValue(
+      name, base::WrapUnique<base::Value>(new base::FundamentalValue(value)));
+}
+
+void SetString(RegistryDict* dict,
+               const std::string& name,
+               const std::string&  value) {
+  dict->SetValue(name,
+                 base::WrapUnique<base::Value>(new base::StringValue(value)));
+}
+
+TEST(PRegParserTest, TestParseFile) {
+  base::FilePath test_data_dir;
+  ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &test_data_dir));
+
+  // Prepare the test dictionary with some data so the test can check that the
+  // PReg action triggers work, i.e. remove these items.
+  RegistryDict dict;
+  SetInteger(&dict, "DeleteValuesTest1", 1);
+  SetString(&dict, "DeleteValuesTest2", "2");
+  dict.SetKey("DeleteKeysTest1", base::MakeUnique<RegistryDict>());
+  std::unique_ptr<RegistryDict> delete_keys_test(new RegistryDict());
+  SetInteger(delete_keys_test.get(), "DeleteKeysTest2Entry", 1);
+  dict.SetKey("DeleteKeysTest2", std::move(delete_keys_test));
+  SetInteger(&dict, "DelTest", 1);
+  std::unique_ptr<RegistryDict> subdict(new RegistryDict());
+  SetInteger(subdict.get(), "DelValsTest1", 1);
+  SetString(subdict.get(), "DelValsTest2", "2");
+  subdict->SetKey("DelValsTest3", base::MakeUnique<RegistryDict>());
+  dict.SetKey("DelValsTest", std::move(subdict));
+
+  // Run the parser.
+  base::FilePath test_file(
+      test_data_dir.AppendASCII("chrome/test/data/policy/registry.pol"));
+  PolicyLoadStatusSample status;
+  ASSERT_TRUE(preg_parser::ReadFile(
+      test_file, base::ASCIIToUTF16("SOFTWARE\\Policies\\Chromium"),
+      &dict, &status));
+
+  // Build the expected output dictionary.
+  RegistryDict expected;
+  std::unique_ptr<RegistryDict> del_vals_dict(new RegistryDict());
+  del_vals_dict->SetKey("DelValsTest3", base::MakeUnique<RegistryDict>());
+  expected.SetKey("DelValsTest", std::move(del_vals_dict));
+  SetInteger(&expected, "HomepageIsNewTabPage", 1);
+  SetString(&expected, "HomepageLocation", "http://www.example.com");
+  SetInteger(&expected, "RestoreOnStartup", 4);
+  std::unique_ptr<RegistryDict> startup_urls(new RegistryDict());
+  SetString(startup_urls.get(), "1", "http://www.chromium.org");
+  SetString(startup_urls.get(), "2", "http://www.example.com");
+  expected.SetKey("RestoreOnStartupURLs", std::move(startup_urls));
+  SetInteger(&expected, "ShowHomeButton", 1);
+  SetString(&expected, "Snowman", "\xE2\x98\x83");
+  SetString(&expected, "Empty", "");
+
+  EXPECT_TRUE(RegistryDictEquals(dict, expected));
+}
+
+}  // namespace
+}  // namespace preg_parser
+}  // namespace policy
diff --git a/components/policy/core/common/registry_dict.cc b/components/policy/core/common/registry_dict.cc
new file mode 100644
index 0000000..ca7df29
--- /dev/null
+++ b/components/policy/core/common/registry_dict.cc
@@ -0,0 +1,365 @@
+// 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.h"
+
+#include <utility>
+
+#include "base/json/json_reader.h"
+#include "base/memory/ptr_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"
+
+#if defined(OS_WIN)
+#include "base/win/registry.h"
+#include "components/policy/core/common/schema.h"
+
+using base::win::RegistryKeyIterator;
+using base::win::RegistryValueIterator;
+#endif  // #if defined(OS_WIN)
+
+namespace policy {
+
+namespace {
+
+#if defined(OS_WIN)
+// Validates that a key is numerical. Used for lists below.
+bool IsKeyNumerical(const std::string& key) {
+  int temp = 0;
+  return base::StringToInt(key, &temp);
+}
+
+// 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.
+std::unique_ptr<base::Value> ConvertValue(const base::Value& value,
+                                          const Schema& schema) {
+  if (!schema.valid())
+    return value.CreateDeepCopy();
+
+  // If the type is good already, go with it.
+  if (value.IsType(schema.type())) {
+    // Recurse for complex types.
+    const base::DictionaryValue* dict = nullptr;
+    const base::ListValue* list = nullptr;
+    if (value.GetAsDictionary(&dict)) {
+      std::unique_ptr<base::DictionaryValue> result(
+          new base::DictionaryValue());
+      for (base::DictionaryValue::Iterator entry(*dict); !entry.IsAtEnd();
+           entry.Advance()) {
+        std::unique_ptr<base::Value> converted =
+            ConvertValue(entry.value(), schema.GetProperty(entry.key()));
+        if (converted)
+          result->SetWithoutPathExpansion(entry.key(), converted.release());
+      }
+      return std::move(result);
+    } else if (value.GetAsList(&list)) {
+      std::unique_ptr<base::ListValue> result(new base::ListValue());
+      for (base::ListValue::const_iterator entry(list->begin());
+           entry != list->end(); ++entry) {
+        std::unique_ptr<base::Value> converted =
+            ConvertValue(**entry, schema.GetItems());
+        if (converted)
+          result->Append(std::move(converted));
+      }
+      return std::move(result);
+    }
+    return value.CreateDeepCopy();
+  }
+
+  // Else, do some conversions to map windows registry data types to JSON types.
+  std::string string_value;
+  int int_value = 0;
+  switch (schema.type()) {
+    case base::Value::Type::NONE: {
+      return 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 std::unique_ptr<base::Value>(
+            new base::FundamentalValue(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 std::unique_ptr<base::Value>(
+            new base::FundamentalValue(int_value));
+      }
+      break;
+    }
+    case base::Value::Type::DOUBLE: {
+      // Doubles may be string-encoded or integer-encoded.
+      double double_value = 0;
+      if (value.GetAsDouble(&double_value) ||
+          (value.GetAsString(&string_value) &&
+           base::StringToDouble(string_value, &double_value))) {
+        return std::unique_ptr<base::Value>(
+            new base::FundamentalValue(double_value));
+      }
+      break;
+    }
+    case base::Value::Type::LIST: {
+      // Lists are encoded as subkeys with numbered value in the registry
+      // (non-numerical keys are ignored).
+      const base::DictionaryValue* dict = nullptr;
+      if (value.GetAsDictionary(&dict)) {
+        std::unique_ptr<base::ListValue> result(new base::ListValue());
+        for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd();
+             it.Advance()) {
+          if (!IsKeyNumerical(it.key()))
+            continue;
+          std::unique_ptr<base::Value> converted =
+              ConvertValue(it.value(), schema.GetItems());
+          if (converted)
+            result->Append(std::move(converted));
+        }
+        return std::move(result);
+      }
+      // 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)) {
+        std::unique_ptr<base::Value> result =
+            base::JSONReader::Read(string_value);
+        if (result && result->IsType(schema.type()))
+          return result;
+      }
+      break;
+    }
+    case base::Value::Type::STRING:
+    case base::Value::Type::BINARY:
+      // No conversion possible.
+      break;
+  }
+
+  LOG(WARNING) << "Failed to convert " << value.GetType()
+               << " to " << schema.type();
+  return nullptr;
+}
+#endif  // #if defined(OS_WIN)
+
+}  // namespace
+
+bool CaseInsensitiveStringCompare::operator()(const std::string& a,
+                                              const std::string& b) const {
+  return base::CompareCaseInsensitiveASCII(a, b) < 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.get() : nullptr;
+}
+
+const RegistryDict* RegistryDict::GetKey(const std::string& name) const {
+  KeyMap::const_iterator entry = keys_.find(name);
+  return entry != keys_.end() ? entry->second.get() : nullptr;
+}
+
+void RegistryDict::SetKey(const std::string& name,
+                          std::unique_ptr<RegistryDict> dict) {
+  if (!dict) {
+    RemoveKey(name);
+    return;
+  }
+
+  keys_[name] = std::move(dict);
+}
+
+std::unique_ptr<RegistryDict> RegistryDict::RemoveKey(const std::string& name) {
+  std::unique_ptr<RegistryDict> result;
+  KeyMap::iterator entry = keys_.find(name);
+  if (entry != keys_.end()) {
+    result = std::move(entry->second);
+    keys_.erase(entry);
+  }
+  return result;
+}
+
+void RegistryDict::ClearKeys() {
+  keys_.clear();
+}
+
+base::Value* RegistryDict::GetValue(const std::string& name) {
+  ValueMap::iterator entry = values_.find(name);
+  return entry != values_.end() ? entry->second.get() : nullptr;
+}
+
+const base::Value* RegistryDict::GetValue(const std::string& name) const {
+  ValueMap::const_iterator entry = values_.find(name);
+  return entry != values_.end() ? entry->second.get() : nullptr;
+}
+
+void RegistryDict::SetValue(const std::string& name,
+                            std::unique_ptr<base::Value> dict) {
+  if (!dict) {
+    RemoveValue(name);
+    return;
+  }
+
+  values_[name] = std::move(dict);
+}
+
+std::unique_ptr<base::Value> RegistryDict::RemoveValue(
+    const std::string& name) {
+  std::unique_ptr<base::Value> result;
+  ValueMap::iterator entry = values_.find(name);
+  if (entry != values_.end()) {
+    result = std::move(entry->second);
+    values_.erase(entry);
+  }
+  return result;
+}
+
+void RegistryDict::ClearValues() {
+  values_.clear();
+}
+
+void RegistryDict::Merge(const RegistryDict& other) {
+  for (KeyMap::const_iterator entry(other.keys_.begin());
+       entry != other.keys_.end(); ++entry) {
+    std::unique_ptr<RegistryDict>& subdict = keys_[entry->first];
+    if (!subdict)
+      subdict = base::MakeUnique<RegistryDict>();
+    subdict->Merge(*entry->second);
+  }
+
+  for (ValueMap::const_iterator entry(other.values_.begin());
+       entry != other.values_.end(); ++entry) {
+    SetValue(entry->first, entry->second->CreateDeepCopy());
+  }
+}
+
+void RegistryDict::Swap(RegistryDict* other) {
+  keys_.swap(other->keys_);
+  values_.swap(other->values_);
+}
+
+#if defined(OS_WIN)
+void RegistryDict::ReadRegistry(HKEY hive, const base::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 = base::UTF16ToUTF8(it.Name());
+    switch (it.Type()) {
+      case REG_SZ:
+      case REG_EXPAND_SZ:
+        SetValue(name, std::unique_ptr<base::Value>(new base::StringValue(
+                           base::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,
+                   std::unique_ptr<base::Value>(new base::FundamentalValue(
+                       static_cast<int>(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(base::UTF16ToUTF8(it.Name()));
+    std::unique_ptr<RegistryDict> subdict(new RegistryDict());
+    subdict->ReadRegistry(hive, root + L"\\" + it.Name());
+    SetKey(name, std::move(subdict));
+  }
+}
+
+std::unique_ptr<base::Value> RegistryDict::ConvertToJSON(
+    const Schema& schema) const {
+  base::Value::Type type =
+      schema.valid() ? schema.type() : base::Value::Type::DICTIONARY;
+  switch (type) {
+    case base::Value::Type::DICTIONARY: {
+      std::unique_ptr<base::DictionaryValue> result(
+          new base::DictionaryValue());
+      for (RegistryDict::ValueMap::const_iterator entry(values_.begin());
+           entry != values_.end(); ++entry) {
+        Schema subschema =
+            schema.valid() ? schema.GetProperty(entry->first) : Schema();
+        std::unique_ptr<base::Value> converted =
+            ConvertValue(*entry->second, subschema);
+        if (converted)
+          result->SetWithoutPathExpansion(entry->first, converted.release());
+      }
+      for (RegistryDict::KeyMap::const_iterator entry(keys_.begin());
+           entry != keys_.end(); ++entry) {
+        Schema subschema =
+            schema.valid() ? schema.GetProperty(entry->first) : Schema();
+        std::unique_ptr<base::Value> converted =
+            entry->second->ConvertToJSON(subschema);
+        if (converted)
+          result->SetWithoutPathExpansion(entry->first, converted.release());
+      }
+      return std::move(result);
+    }
+    case base::Value::Type::LIST: {
+      std::unique_ptr<base::ListValue> result(new base::ListValue());
+      Schema item_schema = schema.valid() ? schema.GetItems() : Schema();
+      for (RegistryDict::KeyMap::const_iterator entry(keys_.begin());
+           entry != keys_.end(); ++entry) {
+        if (!IsKeyNumerical(entry->first))
+          continue;
+        std::unique_ptr<base::Value> converted =
+            entry->second->ConvertToJSON(item_schema);
+        if (converted)
+          result->Append(std::move(converted));
+      }
+      for (RegistryDict::ValueMap::const_iterator entry(values_.begin());
+           entry != values_.end(); ++entry) {
+        if (!IsKeyNumerical(entry->first))
+          continue;
+        std::unique_ptr<base::Value> converted =
+            ConvertValue(*entry->second, item_schema);
+        if (converted)
+          result->Append(std::move(converted));
+      }
+      return std::move(result);
+    }
+    default:
+      LOG(WARNING) << "Can't convert registry key to schema type " << type;
+  }
+
+  return nullptr;
+}
+#endif  // #if defined(OS_WIN)
+}  // namespace policy
diff --git a/components/policy/core/common/registry_dict.h b/components/policy/core/common/registry_dict.h
new file mode 100644
index 0000000..ca3b901
--- /dev/null
+++ b/components/policy/core/common/registry_dict.h
@@ -0,0 +1,95 @@
+// 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_H_
+#define COMPONENTS_POLICY_CORE_COMMON_REGISTRY_DICT_H_
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "components/policy/policy_export.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+namespace base {
+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:
+  using KeyMap = std::map<std::string,
+                          std::unique_ptr<RegistryDict>,
+                          CaseInsensitiveStringCompare>;
+  using ValueMap = std::map<std::string,
+                            std::unique_ptr<base::Value>,
+                            CaseInsensitiveStringCompare>;
+
+  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, std::unique_ptr<RegistryDict> dict);
+  // Removes a key. If the key doesn't exist, NULL is returned.
+  std::unique_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, std::unique_ptr<base::Value> value);
+  // Removes a value. If the value doesn't exist, NULL is returned.
+  std::unique_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);
+
+#if defined(OS_WIN)
+  // Read a Windows registry subtree into this registry dictionary object.
+  void ReadRegistry(HKEY hive, const base::string16& root);
+
+  // Converts the dictionary to base::Value representation. For key/value name
+  // collisions, the key wins. |schema| is used to determine the expected type
+  // for each policy.
+  // The returned object is either a base::DictionaryValue or a base::ListValue.
+  std::unique_ptr<base::Value> ConvertToJSON(const class Schema& schema) const;
+#endif
+
+  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_H_
diff --git a/components/policy/core/common/registry_dict_unittest.cc b/components/policy/core/common/registry_dict_unittest.cc
new file mode 100644
index 0000000..553ee6c
--- /dev/null
+++ b/components/policy/core/common/registry_dict_unittest.cc
@@ -0,0 +1,312 @@
+// 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.h"
+
+#include <string>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "components/policy/core/common/schema.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace policy {
+namespace {
+
+TEST(RegistryDictTest, SetAndGetValue) {
+  RegistryDict test_dict;
+
+  base::FundamentalValue int_value(42);
+  base::StringValue string_value("fortytwo");
+
+  test_dict.SetValue("one", int_value.CreateDeepCopy());
+  EXPECT_EQ(1u, 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", string_value.CreateDeepCopy());
+  EXPECT_EQ(2u, 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")));
+
+  std::unique_ptr<base::Value> one(test_dict.RemoveValue("one"));
+  EXPECT_EQ(1u, 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", int_value.CreateDeepCopy());
+  EXPECT_EQ(1u, 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", string_value.CreateDeepCopy());
+  EXPECT_EQ(1u, test_dict.values().size());
+  EXPECT_TRUE(base::Value::Equals(&string_value, test_dict.GetValue("one")));
+
+  std::unique_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");
+
+  std::unique_ptr<RegistryDict> subdict(new RegistryDict());
+  subdict->SetValue("one", int_value.CreateDeepCopy());
+  test_dict.SetKey("two", std::move(subdict));
+  EXPECT_EQ(1u, 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", string_value.CreateDeepCopy());
+  test_dict.SetKey("four", std::move(subdict));
+  EXPECT_EQ(2u, 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", base::MakeUnique<RegistryDict>());
+  EXPECT_EQ(1u, 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);
+
+  std::unique_ptr<RegistryDict> subdict(new RegistryDict());
+  subdict->SetValue("two", int_value.CreateDeepCopy());
+  test_dict.SetKey("ONE", std::move(subdict));
+  EXPECT_EQ(1u, 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")));
+
+  std::unique_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", int_value.CreateDeepCopy());
+  std::unique_ptr<RegistryDict> subdict(new RegistryDict());
+  subdict->SetValue("two", string_value.CreateDeepCopy());
+  dict_a.SetKey("three", std::move(subdict));
+
+  dict_b.SetValue("four", string_value.CreateDeepCopy());
+  subdict.reset(new RegistryDict());
+  subdict->SetValue("two", int_value.CreateDeepCopy());
+  dict_b.SetKey("three", std::move(subdict));
+  subdict.reset(new RegistryDict());
+  subdict->SetValue("five", int_value.CreateDeepCopy());
+  dict_b.SetKey("six", std::move(subdict));
+
+  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", int_value.CreateDeepCopy());
+  dict_a.SetKey("two", base::MakeUnique<RegistryDict>());
+  dict_b.SetValue("three", string_value.CreateDeepCopy());
+
+  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"));
+}
+
+#if defined(OS_WIN)
+TEST(RegistryDictTest, ConvertToJSON) {
+  RegistryDict test_dict;
+
+  base::FundamentalValue int_value(42);
+  base::StringValue string_value("fortytwo");
+  base::StringValue string_zero("0");
+  base::StringValue string_dict("{ \"key\": [ \"value\" ] }");
+
+  test_dict.SetValue("one", int_value.CreateDeepCopy());
+  std::unique_ptr<RegistryDict> subdict(new RegistryDict());
+  subdict->SetValue("two", string_value.CreateDeepCopy());
+  test_dict.SetKey("three", std::move(subdict));
+  std::unique_ptr<RegistryDict> list(new RegistryDict());
+  list->SetValue("1", string_value.CreateDeepCopy());
+  test_dict.SetKey("dict-to-list", std::move(list));
+  test_dict.SetValue("int-to-bool", int_value.CreateDeepCopy());
+  test_dict.SetValue("int-to-double", int_value.CreateDeepCopy());
+  test_dict.SetValue("string-to-bool", string_zero.CreateDeepCopy());
+  test_dict.SetValue("string-to-double", string_zero.CreateDeepCopy());
+  test_dict.SetValue("string-to-int", string_zero.CreateDeepCopy());
+  test_dict.SetValue("string-to-dict", string_dict.CreateDeepCopy());
+
+  std::string error;
+  Schema schema = Schema::Parse(
+      "{"
+      "  \"type\": \"object\","
+      "  \"properties\": {"
+      "    \"dict-to-list\": {"
+      "      \"type\": \"array\","
+      "      \"items\": { \"type\": \"string\" }"
+      "    },"
+      "    \"int-to-bool\": { \"type\": \"boolean\" },"
+      "    \"int-to-double\": { \"type\": \"number\" },"
+      "    \"string-to-bool\": { \"type\": \"boolean\" },"
+      "    \"string-to-double\": { \"type\": \"number\" },"
+      "    \"string-to-int\": { \"type\": \"integer\" },"
+      "    \"string-to-dict\": { \"type\": \"object\" }"
+      "  }"
+      "}",
+      &error);
+  ASSERT_TRUE(schema.valid()) << error;
+
+  std::unique_ptr<base::Value> actual(test_dict.ConvertToJSON(schema));
+
+  base::DictionaryValue expected;
+  expected.Set("one", int_value.CreateDeepCopy());
+  std::unique_ptr<base::DictionaryValue> expected_subdict(
+      new base::DictionaryValue());
+  expected_subdict->Set("two", string_value.CreateDeepCopy());
+  expected.Set("three", std::move(expected_subdict));
+  std::unique_ptr<base::ListValue> expected_list(new base::ListValue());
+  expected_list->Append(string_value.CreateDeepCopy());
+  expected.Set("dict-to-list", std::move(expected_list));
+  expected.Set("int-to-bool", new base::FundamentalValue(true));
+  expected.Set("int-to-double", new base::FundamentalValue(42.0));
+  expected.Set("string-to-bool", new base::FundamentalValue(false));
+  expected.Set("string-to-double", new base::FundamentalValue(0.0));
+  expected.Set("string-to-int",
+               new base::FundamentalValue(static_cast<int>(0)));
+  expected_list.reset(new base::ListValue());
+  expected_list->Append(base::MakeUnique<base::StringValue>("value"));
+  expected_subdict.reset(new base::DictionaryValue());
+  expected_subdict->Set("key", std::move(expected_list));
+  expected.Set("string-to-dict", std::move(expected_subdict));
+
+  EXPECT_TRUE(base::Value::Equals(actual.get(), &expected));
+}
+
+TEST(RegistryDictTest, NonSequentialConvertToJSON) {
+  RegistryDict test_dict;
+
+  std::unique_ptr<RegistryDict> list(new RegistryDict());
+  list->SetValue("1", base::StringValue("1").CreateDeepCopy());
+  list->SetValue("2", base::StringValue("2").CreateDeepCopy());
+  list->SetValue("THREE", base::StringValue("3").CreateDeepCopy());
+  list->SetValue("4", base::StringValue("4").CreateDeepCopy());
+  test_dict.SetKey("dict-to-list", std::move(list));
+
+  std::string error;
+  Schema schema = Schema::Parse(
+      "{"
+      "  \"type\": \"object\","
+      "  \"properties\": {"
+      "    \"dict-to-list\": {"
+      "      \"type\": \"array\","
+      "      \"items\": { \"type\": \"string\" }"
+      "    }"
+      "  }"
+      "}",
+      &error);
+  ASSERT_TRUE(schema.valid()) << error;
+
+  std::unique_ptr<base::Value> actual(test_dict.ConvertToJSON(schema));
+
+  base::DictionaryValue expected;
+  std::unique_ptr<base::ListValue> expected_list(new base::ListValue());
+  expected_list->Append(base::StringValue("1").CreateDeepCopy());
+  expected_list->Append(base::StringValue("2").CreateDeepCopy());
+  expected_list->Append(base::StringValue("4").CreateDeepCopy());
+  expected.Set("dict-to-list", std::move(expected_list));
+
+  EXPECT_TRUE(base::Value::Equals(actual.get(), &expected));
+}
+#endif
+
+TEST(RegistryDictTest, KeyValueNameClashes) {
+  RegistryDict test_dict;
+
+  base::FundamentalValue int_value(42);
+  base::StringValue string_value("fortytwo");
+
+  test_dict.SetValue("one", int_value.CreateDeepCopy());
+  std::unique_ptr<RegistryDict> subdict(new RegistryDict());
+  subdict->SetValue("two", string_value.CreateDeepCopy());
+  test_dict.SetKey("one", std::move(subdict));
+
+  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/components/policy/policy_export.h b/components/policy/policy_export.h
new file mode 100644
index 0000000..b6030aa
--- /dev/null
+++ b/components/policy/policy_export.h
@@ -0,0 +1,34 @@
+// 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_POLICY_EXPORT_H_
+#define COMPONENTS_POLICY_POLICY_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+
+#if defined(WIN32)
+
+#if defined(POLICY_COMPONENT_IMPLEMENTATION)
+#define POLICY_EXPORT __declspec(dllexport)
+#else
+#define POLICY_EXPORT __declspec(dllimport)
+#endif  // defined(POLICY_COMPONENT_IMPLEMENTATION)
+
+#else  // defined(WIN32)
+
+#if defined(POLICY_COMPONENT_IMPLEMENTATION)
+#define POLICY_EXPORT __attribute__((visibility("default")))
+#else
+#define POLICY_EXPORT
+#endif  // defined(POLICY_COMPONENT_IMPLEMENTATION)
+
+#endif  // defined(WIN32)
+
+#else  // defined(COMPONENT_BUILD)
+
+#define POLICY_EXPORT
+
+#endif  // defined(COMPONENT_BUILD)
+
+#endif  // COMPONENTS_POLICY_POLICY_EXPORT_H_