Add base to the repository.



git-svn-id: svn://svn.chromium.org/chrome/trunk/src@8 0039d316-1c4b-4281-b951-d872f2087c98


CrOS-Libchrome-Original-Commit: d7cae12696b96500c05dd2d430f6238922c20c96
diff --git a/base/SConscript b/base/SConscript
new file mode 100644
index 0000000..2b7a7cc
--- /dev/null
+++ b/base/SConscript
@@ -0,0 +1,296 @@
+# Copyright 2008, Google Inc.

+# All rights reserved.

+#

+# Redistribution and use in source and binary forms, with or without

+# modification, are permitted provided that the following conditions are

+# met:

+#

+#    * Redistributions of source code must retain the above copyright

+# notice, this list of conditions and the following disclaimer.

+#    * Redistributions in binary form must reproduce the above

+# copyright notice, this list of conditions and the following disclaimer

+# in the documentation and/or other materials provided with the

+# distribution.

+#    * Neither the name of Google Inc. nor the names of its

+# contributors may be used to endorse or promote products derived from

+# this software without specific prior written permission.

+#

+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+

+Import('env')

+

+env = env.Clone()

+env_tests = env.Clone()

+

+env.Prepend(

+    CPPPATH = [

+        '$ICU38_DIR/public/common',

+        '$ICU38_DIR/public/i18n',

+        '..',

+    ],

+    CPPDEFINES = [

+        'U_STATIC_IMPLEMENTATION',

+        'CERT_CHAIN_PARA_HAS_EXTRA_FIELDS',

+        'WIN32_LEAN_AND_MEAN',

+    ],

+    CCFLAGS = [

+        '/TP',

+

+        '/Wp64',

+

+        '/wd4503',

+        '/wd4819',

+    ],

+)

+

+input_files = [

+    'base_drag_source.cc',

+    'base_drop_target.cc',

+    'base_paths.cc',

+    'base_switches.cc',

+    'clipboard.cc',

+    'clipboard_util.cc',

+    'command_line.cc',

+    'condition_variable.cc',

+    'debug_on_start.cc',

+    'debug_util.cc',

+    'event_recorder.cc',

+    'file_util.cc',

+    'file_version_info.cc',

+    'histogram.cc',

+    'hmac.cc',

+    'iat_patch.cc',

+    'icu_util.cc',

+    'idle_timer.cc',

+    'image_util.cc',

+    'json_reader.cc',

+    'json_writer.cc',

+    'lock.cc',

+    'lock_impl_win.cc',

+    'logging.cc',

+    'md5.cc',

+    'memory_debug.cc',

+    'message_loop.cc',

+    'non_thread_safe.cc',

+    'path_service.cc',

+    'pe_image.cc',

+    'pickle.cc',

+    'platform_thread.cc',

+    'process.cc',

+    'process_util.cc',

+    'registry.cc',

+    'resource_util.cc',

+    'revocable_store.cc',

+    'sha2.cc',

+    'shared_event.cc',

+    'shared_memory.cc',

+    'stats_table.cc',

+    'string_escape.cc',

+    'string_util.cc',

+    'string_util_icu.cc',

+    'string_util_win.cc',

+    'third_party/nspr/prtime.cc',

+    'third_party/nss/sha512.cc',

+    'thread.cc',

+    'thread_local_storage_win.cc',

+    'time.cc',

+    'time_win.cc',

+    'timer.cc',

+    'tracked.cc',

+    'tracked_objects.cc',

+    'values.cc',

+    'watchdog.cc',

+    'win_util.cc',

+    'wmi_util.cc',

+    'word_iterator.cc',

+    'worker_pool.cc',

+]

+

+env.StaticLibrary('base', input_files)

+

+

+env_tests.Prepend(

+    CPPPATH = [

+        '$SKIA_DIR/include',

+        '$SKIA_DIR/include/corecg',

+        '$SKIA_DIR/platform',

+        '$ZLIB_DIR',

+        '$LIBPNG_DIR',

+        '$ICU38_DIR/public/common',

+        '$ICU38/_DIRpublic/i18n',

+        '..',

+    ],

+    CPPDEFINES = [

+        'UNIT_TEST',

+        'PNG_USER_CONFIG',

+        'CHROME_PNG_WRITE_SUPPORT',

+        'U_STATIC_IMPLEMENTATION',

+        '_WIN32_WINNT=0x0600',

+        'WINVER=0x0600',

+        '_HAS_EXCEPTIONS=0',

+        'CERT_CHAIN_PARA_HAS_EXTRA_FIELDS',

+        'WIN32_LEAN_AND_MEAN',

+    ],

+    CCFLAGS = [

+        '/WX',

+        '/Wp64',

+        '/TP',

+

+        '/wd4503',

+        '/wd4819',

+    ],

+    LINKFLAGS = [

+        '/MANIFEST',

+        '/DELAYLOAD:"dwmapi.dll"',

+        '/DELAYLOAD:"uxtheme.dll"',

+        '/MACHINE:X86',

+        '/FIXED:No',

+

+        '/safeseh',

+        '/dynamicbase',

+        '/ignore:4199',

+        '/nxcompat',

+    ],

+    LIBS = [

+        'advapi32.lib',

+        'comdlg32.lib',

+        'DelayImp.lib',

+        'gdi32.lib',

+        'kernel32.lib',

+        'msimg32.lib',

+        'odbc32.lib',

+        'odbccp32.lib',

+        'ole32.lib',

+        'oleaut32.lib',

+        'psapi.lib',

+        'shell32.lib',

+        'user32.lib',

+        'usp10.lib',

+        'uuid.lib',

+        'version.lib',

+        'wininet.lib',

+        'winspool.lib',

+        'ws2_32.lib',

+    ],

+)

+

+libs = [

+    'base.lib',

+    'gfx/base_gfx.lib',

+    '$SKIA_DIR/skia.lib',

+    '$LIBPNG_DIR/libpng.lib',

+    '$TESTING_DIR/gtest.lib',

+    '$ICU38_DIR/icuuc.lib',

+    '$ZLIB_DIR/zlib.lib',

+]

+

+env_tests.Append(

+    CPPPATH = [

+        '$GTEST_DIR/include',

+    ],

+)

+

+env_tests_dll = env_tests.Clone()

+env_tests_dll.Append(

+    CPPDEFINES = [

+        '_WINDLL',

+        'SINGLETON_UNITTEST_EXPORTS',

+    ],

+)

+dll = env_tests_dll.SharedLibrary(['singleton_dll_unittest.dll',

+                                   'singleton_dll_unittest.ilk',

+                                   'singleton_dll_unittest.pdb'],

+                                  ['singleton_dll_unittest.cc',

+                                   'build/singleton_dll_unittest.def'] + libs)

+i = env.Install('$TARGET_ROOT', dll[0])

+env.Alias('base', i)

+

+env_tests.Program(['debug_message.exe',

+                   'debug_message.ilk',

+                   'debug_message.pdb'],

+                  ['debug_message.cc'] + libs)

+

+test_files = [

+    'atomic_unittest.cc',

+    'check_handler_unittest.cc',

+    'clipboard_unittest.cc',

+    'command_line_unittest.cc',

+    'condition_variable_test.cc',

+    'file_util_unittest.cc',

+    'file_version_info_unittest.cc',

+    'fixed_string_unittest.cc',

+    'gfx/convolver_unittest.cc',

+    'gfx/image_operations_unittest.cc',

+    'gfx/native_theme_unittest.cc',

+    'gfx/platform_canvas_unittest.cc',

+    'gfx/png_codec_unittest.cc',

+    'gfx/rect_unittest.cc',

+    'gfx/uniscribe_unittest.cc',

+    'gfx/vector_canvas_unittest.cc',

+    'hmac_unittest.cc',

+    'json_reader_unittest.cc',

+    'json_writer_unittest.cc',

+    'linked_ptr_unittest.cc',

+    'message_loop_unittest.cc',

+    'path_service_unittest.cc',

+    'pe_image_unittest.cc',

+    'pickle_unittest.cc',

+    'pr_time_test.cc',

+    'process_util_unittest.cc',

+    'ref_counted_unittest.cc',

+    'run_all_unittests.cc',

+    'sha2_unittest.cc',

+    'shared_event_unittest.cc',

+    'shared_memory_unittest.cc',

+    'singleton_unittest.cc',

+    'stack_container_unittest.cc',

+    'stats_table_unittest.cc',

+    'string_tokenizer_unittest.cc',

+    'string_util_unittest.cc',

+    'thread_local_storage_unittest.cc',

+    'thread_unittest.cc',

+    'tracked_objects_test.cc',

+    'time_unittest.cc',

+    'timer_unittest.cc',

+    'values_unittest.cc',

+    'win_util_unittest.cc',

+    'wmi_util_unittest.cc',

+

+    'singleton_dll_unittest.lib',

+]

+

+base_unittests = env_tests.Program([

+    'base_unittests',

+    'base_unittests.exp',

+    'base_unittests.ilk',

+    'base_unittests.lib',

+    'base_unittests.pdb'], test_files + libs)

+

+

+# Install up a level to allow unit test path assumptions to be valid.

+installed_base_unittests = env.Install('$TARGET_ROOT', base_unittests)

+

+

+sconscript_dirs = [

+    'gfx/SConscript',

+]

+

+SConscript(sconscript_dirs, exports=['env'])

+

+

+# Setup alias for all base related targets.

+env.Alias('base', ['.', installed_base_unittests, '../icudt38.dll'])

+

+

+env_tests.StaticObject('perftimer.cc')

+env_tests.StaticObject('run_all_perftests.cc')

diff --git a/base/SConstruct b/base/SConstruct
new file mode 100644
index 0000000..e9acd54
--- /dev/null
+++ b/base/SConstruct
@@ -0,0 +1,3 @@
+build_component = 'base'

+SConscript('../build/SConscript.main',

+           exports=['build_component'])

diff --git a/base/atomic.h b/base/atomic.h
new file mode 100644
index 0000000..becbcc3
--- /dev/null
+++ b/base/atomic.h
@@ -0,0 +1,111 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_ATOMIC_H__
+#define BASE_ATOMIC_H__
+
+#if defined(WIN32)
+#include <windows.h>
+#elif defined(__APPLE__)
+#include <libkern/OSAtomic.h>
+#else
+#error Implement atomic support on your platform
+#endif
+
+#include "base/basictypes.h"
+
+namespace base {
+
+///////////////////////////////////////////////////////////////////////////
+//
+// Forward declarations and descriptions of the functions
+//
+///////////////////////////////////////////////////////////////////////////
+
+// Atomically increments *value and returns the resulting incremented value.
+// This function implies no memory barriers.
+int32 AtomicIncrement(volatile int32* value);
+
+// Atomically decrements *value and returns the resulting decremented value.
+// This function implies no memory barriers.
+int32 AtomicDecrement(volatile int32* value);
+
+// Atomically sets *target to new_value and returns the old value of *target.
+// This function implies no memory barriers.
+int32 AtomicSwap(volatile int32* target, int32 new_value);
+
+///////////////////////////////////////////////////////////////////////////
+//
+// Implementations for various platforms
+//
+///////////////////////////////////////////////////////////////////////////
+
+#if defined(WIN32)
+
+inline int32 AtomicIncrement(volatile int32* value) {
+  return InterlockedIncrement(reinterpret_cast<volatile LONG*>(value));
+}
+
+inline int32 AtomicDecrement(volatile int32* value) {
+  return InterlockedDecrement(reinterpret_cast<volatile LONG*>(value));
+}
+
+inline int32 AtomicSwap(volatile int32* target, int32 new_value) {
+  return InterlockedExchange(reinterpret_cast<volatile LONG*>(target),
+                             new_value);
+}
+
+#elif defined(__APPLE__)
+
+inline int32 AtomicIncrement(volatile int32* value) {
+  return OSAtomicIncrement32(reinterpret_cast<volatile int32_t*>(value));
+}
+
+inline int32 AtomicDecrement(volatile int32* value) {
+  return OSAtomicDecrement32(reinterpret_cast<volatile int32_t*>(value));
+}
+
+inline int32 AtomicSwap(volatile int32* target, int32 new_value) {
+  int32 old_value;
+  do {
+    old_value = *target;
+  } while (!OSAtomicCompareAndSwap32(old_value, new_value,
+      reinterpret_cast<volatile int32_t*>(target)));
+  return old_value;
+}
+
+#else
+
+#error Implement atomic support on your platform
+
+#endif
+
+}  // namespace base
+
+#endif // BASE_ATOMIC_H__
diff --git a/base/atomic_unittest.cc b/base/atomic_unittest.cc
new file mode 100644
index 0000000..770a520
--- /dev/null
+++ b/base/atomic_unittest.cc
@@ -0,0 +1,63 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/atomic.h"
+
+#include "base/lock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(AtomicTest, Increment) {
+  int32 value = 38;
+  int32 new_value = base::AtomicIncrement(&value);
+  EXPECT_EQ(39, value);
+  EXPECT_EQ(39, new_value);
+}
+
+TEST(AtomicTest, Decrement) {
+  int32 value = 49;
+  int32 new_value = base::AtomicDecrement(&value);
+  EXPECT_EQ(48, value);
+  EXPECT_EQ(48, new_value);
+}
+
+TEST(AtomicTest, Swap) {
+  int32 value = 38;
+  int32 old_value = base::AtomicSwap(&value, 49);
+  EXPECT_EQ(49, value);
+  EXPECT_EQ(38, old_value);
+
+  // Now do another test.
+  value = 0;
+  old_value = base::AtomicSwap(&value, 1);
+  EXPECT_EQ(0, old_value);
+  old_value = base::AtomicSwap(&value, 1);
+  EXPECT_EQ(1, old_value);
+  old_value = base::AtomicSwap(&value, 1);
+  EXPECT_EQ(1, old_value);
+}
diff --git a/base/base.sln b/base/base.sln
new file mode 100644
index 0000000..c928064
--- /dev/null
+++ b/base/base.sln
@@ -0,0 +1,109 @@
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual Studio 2005
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "base", "build\base.vcproj", "{1832A374-8A74-4F9E-B536-69A699B3E165}"
+	ProjectSection(ProjectDependencies) = postProject
+		{0E5474AC-5996-4B13-87C0-4AE931EE0815} = {0E5474AC-5996-4B13-87C0-4AE931EE0815}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "base_unittests", "build\base_unittests.vcproj", "{27A30967-4BBA-48D1-8522-CDE95F7B1CEC}"
+	ProjectSection(ProjectDependencies) = postProject
+		{1832A374-8A74-4F9E-B536-69A699B3E165} = {1832A374-8A74-4F9E-B536-69A699B3E165}
+		{8423AF0D-4B88-4EBF-94E1-E4D00D00E21C} = {8423AF0D-4B88-4EBF-94E1-E4D00D00E21C}
+		{8C27D792-2648-4F5E-9ED0-374276327308} = {8C27D792-2648-4F5E-9ED0-374276327308}
+		{A508ADD3-CECE-4E0F-8448-2F5E454DF551} = {A508ADD3-CECE-4E0F-8448-2F5E454DF551}
+		{BFE8E2A7-3B3B-43B0-A994-3058B852DB8B} = {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B}
+		{C564F145-9172-42C3-BFCB-6014CA97DBCD} = {C564F145-9172-42C3-BFCB-6014CA97DBCD}
+		{CD9CA56E-4E94-444C-87D4-58CA1E6F300D} = {CD9CA56E-4E94-444C-87D4-58CA1E6F300D}
+		{E457F2FB-4708-4001-9B1C-275D7BD7F2A8} = {E457F2FB-4708-4001-9B1C-275D7BD7F2A8}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "debug_message", "build\debug_message.vcproj", "{0E5474AC-5996-4B13-87C0-4AE931EE0815}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dependencies", "dependencies", "{F216062D-F9C4-4883-A52C-2BE9ECADEEA0}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "icuuc", "..\third_party\icu38\build\icuuc.vcproj", "{8C27D792-2648-4F5E-9ED0-374276327308}"
+	ProjectSection(ProjectDependencies) = postProject
+		{A0D94973-D355-47A5-A1E2-3456F321F010} = {A0D94973-D355-47A5-A1E2-3456F321F010}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "icudt", "..\third_party\icu38\build\icudt.vcproj", "{A0D94973-D355-47A5-A1E2-3456F321F010}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "base_gfx", "build\base_gfx.vcproj", "{A508ADD3-CECE-4E0F-8448-2F5E454DF551}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "skia", "..\skia\skia.vcproj", "{CD9CA56E-4E94-444C-87D4-58CA1E6F300D}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libpng", "..\third_party\libpng\libpng.vcproj", "{C564F145-9172-42C3-BFCB-6014CA97DBCD}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "..\third_party\zlib\zlib.vcproj", "{8423AF0D-4B88-4EBF-94E1-E4D00D00E21C}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "singleton_dll_unittest", "build\singleton_unittest.vcproj", "{E457F2FB-4708-4001-9B1C-275D7BD7F2A8}"
+	ProjectSection(ProjectDependencies) = postProject
+		{1832A374-8A74-4F9E-B536-69A699B3E165} = {1832A374-8A74-4F9E-B536-69A699B3E165}
+		{8C27D792-2648-4F5E-9ED0-374276327308} = {8C27D792-2648-4F5E-9ED0-374276327308}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gtest", "..\testing\gtest.vcproj", "{BFE8E2A7-3B3B-43B0-A994-3058B852DB8B}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Win32 = Debug|Win32
+		Release|Win32 = Release|Win32
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{0E5474AC-5996-4B13-87C0-4AE931EE0815}.Debug|Win32.ActiveCfg = Debug|Win32
+		{0E5474AC-5996-4B13-87C0-4AE931EE0815}.Debug|Win32.Build.0 = Debug|Win32
+		{0E5474AC-5996-4B13-87C0-4AE931EE0815}.Release|Win32.ActiveCfg = Release|Win32
+		{0E5474AC-5996-4B13-87C0-4AE931EE0815}.Release|Win32.Build.0 = Release|Win32
+		{1832A374-8A74-4F9E-B536-69A699B3E165}.Debug|Win32.ActiveCfg = Debug|Win32
+		{1832A374-8A74-4F9E-B536-69A699B3E165}.Debug|Win32.Build.0 = Debug|Win32
+		{1832A374-8A74-4F9E-B536-69A699B3E165}.Release|Win32.ActiveCfg = Release|Win32
+		{1832A374-8A74-4F9E-B536-69A699B3E165}.Release|Win32.Build.0 = Release|Win32
+		{27A30967-4BBA-48D1-8522-CDE95F7B1CEC}.Debug|Win32.ActiveCfg = Debug|Win32
+		{27A30967-4BBA-48D1-8522-CDE95F7B1CEC}.Debug|Win32.Build.0 = Debug|Win32
+		{27A30967-4BBA-48D1-8522-CDE95F7B1CEC}.Release|Win32.ActiveCfg = Release|Win32
+		{27A30967-4BBA-48D1-8522-CDE95F7B1CEC}.Release|Win32.Build.0 = Release|Win32
+		{8423AF0D-4B88-4EBF-94E1-E4D00D00E21C}.Debug|Win32.ActiveCfg = Debug|Win32
+		{8423AF0D-4B88-4EBF-94E1-E4D00D00E21C}.Debug|Win32.Build.0 = Debug|Win32
+		{8423AF0D-4B88-4EBF-94E1-E4D00D00E21C}.Release|Win32.ActiveCfg = Release|Win32
+		{8423AF0D-4B88-4EBF-94E1-E4D00D00E21C}.Release|Win32.Build.0 = Release|Win32
+		{8C27D792-2648-4F5E-9ED0-374276327308}.Debug|Win32.ActiveCfg = Debug|Win32
+		{8C27D792-2648-4F5E-9ED0-374276327308}.Debug|Win32.Build.0 = Debug|Win32
+		{8C27D792-2648-4F5E-9ED0-374276327308}.Release|Win32.ActiveCfg = Release|Win32
+		{8C27D792-2648-4F5E-9ED0-374276327308}.Release|Win32.Build.0 = Release|Win32
+		{A0D94973-D355-47A5-A1E2-3456F321F010}.Debug|Win32.ActiveCfg = Debug|Win32
+		{A0D94973-D355-47A5-A1E2-3456F321F010}.Debug|Win32.Build.0 = Debug|Win32
+		{A0D94973-D355-47A5-A1E2-3456F321F010}.Release|Win32.ActiveCfg = Release|Win32
+		{A0D94973-D355-47A5-A1E2-3456F321F010}.Release|Win32.Build.0 = Release|Win32
+		{A508ADD3-CECE-4E0F-8448-2F5E454DF551}.Debug|Win32.ActiveCfg = Debug|Win32
+		{A508ADD3-CECE-4E0F-8448-2F5E454DF551}.Debug|Win32.Build.0 = Debug|Win32
+		{A508ADD3-CECE-4E0F-8448-2F5E454DF551}.Release|Win32.ActiveCfg = Release|Win32
+		{A508ADD3-CECE-4E0F-8448-2F5E454DF551}.Release|Win32.Build.0 = Release|Win32
+		{BFE8E2A7-3B3B-43B0-A994-3058B852DB8B}.Debug|Win32.ActiveCfg = Debug|Win32
+		{BFE8E2A7-3B3B-43B0-A994-3058B852DB8B}.Debug|Win32.Build.0 = Debug|Win32
+		{BFE8E2A7-3B3B-43B0-A994-3058B852DB8B}.Release|Win32.ActiveCfg = Release|Win32
+		{BFE8E2A7-3B3B-43B0-A994-3058B852DB8B}.Release|Win32.Build.0 = Release|Win32
+		{C564F145-9172-42C3-BFCB-6014CA97DBCD}.Debug|Win32.ActiveCfg = Debug|Win32
+		{C564F145-9172-42C3-BFCB-6014CA97DBCD}.Debug|Win32.Build.0 = Debug|Win32
+		{C564F145-9172-42C3-BFCB-6014CA97DBCD}.Release|Win32.ActiveCfg = Release|Win32
+		{C564F145-9172-42C3-BFCB-6014CA97DBCD}.Release|Win32.Build.0 = Release|Win32
+		{CD9CA56E-4E94-444C-87D4-58CA1E6F300D}.Debug|Win32.ActiveCfg = Debug|Win32
+		{CD9CA56E-4E94-444C-87D4-58CA1E6F300D}.Debug|Win32.Build.0 = Debug|Win32
+		{CD9CA56E-4E94-444C-87D4-58CA1E6F300D}.Release|Win32.ActiveCfg = Release|Win32
+		{CD9CA56E-4E94-444C-87D4-58CA1E6F300D}.Release|Win32.Build.0 = Release|Win32
+		{E457F2FB-4708-4001-9B1C-275D7BD7F2A8}.Debug|Win32.ActiveCfg = Debug|Win32
+		{E457F2FB-4708-4001-9B1C-275D7BD7F2A8}.Debug|Win32.Build.0 = Debug|Win32
+		{E457F2FB-4708-4001-9B1C-275D7BD7F2A8}.Release|Win32.ActiveCfg = Release|Win32
+		{E457F2FB-4708-4001-9B1C-275D7BD7F2A8}.Release|Win32.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(NestedProjects) = preSolution
+		{8423AF0D-4B88-4EBF-94E1-E4D00D00E21C} = {F216062D-F9C4-4883-A52C-2BE9ECADEEA0}
+		{8C27D792-2648-4F5E-9ED0-374276327308} = {F216062D-F9C4-4883-A52C-2BE9ECADEEA0}
+		{A0D94973-D355-47A5-A1E2-3456F321F010} = {F216062D-F9C4-4883-A52C-2BE9ECADEEA0}
+		{BFE8E2A7-3B3B-43B0-A994-3058B852DB8B} = {F216062D-F9C4-4883-A52C-2BE9ECADEEA0}
+		{C564F145-9172-42C3-BFCB-6014CA97DBCD} = {F216062D-F9C4-4883-A52C-2BE9ECADEEA0}
+		{CD9CA56E-4E94-444C-87D4-58CA1E6F300D} = {F216062D-F9C4-4883-A52C-2BE9ECADEEA0}
+	EndGlobalSection
+EndGlobal
diff --git a/base/base.xcodeproj/project.pbxproj b/base/base.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..64eaff5
--- /dev/null
+++ b/base/base.xcodeproj/project.pbxproj
@@ -0,0 +1,970 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 42;
+	objects = {
+
+/* Begin PBXAggregateTarget section */
+		825404020D92D3340006B936 /* All */ = {
+			isa = PBXAggregateTarget;
+			buildConfigurationList = 825404110D92D35C0006B936 /* Build configuration list for PBXAggregateTarget "All" */;
+			buildPhases = (
+			);
+			dependencies = (
+				825404060D92D33A0006B936 /* PBXTargetDependency */,
+				825404080D92D33C0006B936 /* PBXTargetDependency */,
+			);
+			name = All;
+			productName = All;
+		};
+/* End PBXAggregateTarget section */
+
+/* Begin PBXBuildFile section */
+		7B5AD60E0D9DD8050012BCF1 /* scoped_cftyperef.h in Headers */ = {isa = PBXBuildFile; fileRef = 7B5AD60D0D9DD8050012BCF1 /* scoped_cftyperef.h */; };
+		7BD9E84F0DA447F800FC7A01 /* singleton.h in Headers */ = {isa = PBXBuildFile; fileRef = 7BD9E84E0DA447F800FC7A01 /* singleton.h */; };
+		7BEB81110D9AD288009BA8DD /* prcpucfg_mac.h in Headers */ = {isa = PBXBuildFile; fileRef = 7BEB81100D9AD288009BA8DD /* prcpucfg_mac.h */; };
+		7BEB814A0D9B0F33009BA8DD /* time_mac.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7BEB81490D9B0F33009BA8DD /* time_mac.cc */; };
+		7BEB834E0D9C4BE0009BA8DD /* string_util_mac.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7BEB834D0D9C4BE0009BA8DD /* string_util_mac.cc */; };
+		7BEE52C20D9D84FD0067FF23 /* string_util_mac.h in Headers */ = {isa = PBXBuildFile; fileRef = 7BEE52C10D9D84FD0067FF23 /* string_util_mac.h */; };
+		7BEFC29E0D99832D000829AD /* lock_impl.h in Headers */ = {isa = PBXBuildFile; fileRef = 7BEFC29C0D99832D000829AD /* lock_impl.h */; };
+		7BEFC29F0D99832D000829AD /* lock_impl_mac.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7BEFC29D0D99832D000829AD /* lock_impl_mac.cc */; };
+		821B91690DAABD7F00F350D7 /* string16.h in Headers */ = {isa = PBXBuildFile; fileRef = 821B91680DAABD7F00F350D7 /* string16.h */; };
+		825402CE0D92D1390006B936 /* base_drag_source.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825402C50D92D1390006B936 /* base_drag_source.cc */; };
+		825402CF0D92D1390006B936 /* base_drag_source.h in Headers */ = {isa = PBXBuildFile; fileRef = 825402C60D92D1390006B936 /* base_drag_source.h */; };
+		825402D00D92D1390006B936 /* base_drop_target.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825402C70D92D1390006B936 /* base_drop_target.cc */; };
+		825402D10D92D1390006B936 /* base_drop_target.h in Headers */ = {isa = PBXBuildFile; fileRef = 825402C80D92D1390006B936 /* base_drop_target.h */; };
+		825402D20D92D1390006B936 /* base_paths.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825402C90D92D1390006B936 /* base_paths.cc */; };
+		825402D30D92D1390006B936 /* base_paths.h in Headers */ = {isa = PBXBuildFile; fileRef = 825402CA0D92D1390006B936 /* base_paths.h */; };
+		825402D40D92D1390006B936 /* base_switches.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825402CB0D92D1390006B936 /* base_switches.cc */; };
+		825402D50D92D1390006B936 /* base_switches.h in Headers */ = {isa = PBXBuildFile; fileRef = 825402CC0D92D1390006B936 /* base_switches.h */; };
+		825402D60D92D1390006B936 /* basictypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 825402CD0D92D1390006B936 /* basictypes.h */; };
+		825402D90D92D15E0006B936 /* blapi.h in Headers */ = {isa = PBXBuildFile; fileRef = 825402D70D92D15E0006B936 /* blapi.h */; };
+		825402DA0D92D15E0006B936 /* blapit.h in Headers */ = {isa = PBXBuildFile; fileRef = 825402D80D92D15E0006B936 /* blapit.h */; };
+		825402DF0D92D1730006B936 /* clipboard.h in Headers */ = {isa = PBXBuildFile; fileRef = 825402DB0D92D1730006B936 /* clipboard.h */; };
+		825402E00D92D1730006B936 /* clipboard.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825402DC0D92D1730006B936 /* clipboard.cc */; };
+		825402E10D92D1730006B936 /* clipboard_util.h in Headers */ = {isa = PBXBuildFile; fileRef = 825402DD0D92D1730006B936 /* clipboard_util.h */; };
+		825402E20D92D1730006B936 /* clipboard_util.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825402DE0D92D1730006B936 /* clipboard_util.cc */; };
+		825402E50D92D1850006B936 /* command_line.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825402E30D92D1850006B936 /* command_line.cc */; };
+		825402E60D92D1850006B936 /* command_line.h in Headers */ = {isa = PBXBuildFile; fileRef = 825402E40D92D1850006B936 /* command_line.h */; };
+		825402EE0D92D1940006B936 /* condition_variable.h in Headers */ = {isa = PBXBuildFile; fileRef = 825402EB0D92D1940006B936 /* condition_variable.h */; };
+		825402EF0D92D1940006B936 /* condition_variable.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825402EC0D92D1940006B936 /* condition_variable.cc */; };
+		825402F00D92D1940006B936 /* condition_variable_events.h in Headers */ = {isa = PBXBuildFile; fileRef = 825402ED0D92D1940006B936 /* condition_variable_events.h */; };
+		825402F50D92D1AC0006B936 /* debug_util.h in Headers */ = {isa = PBXBuildFile; fileRef = 825402F10D92D1AC0006B936 /* debug_util.h */; };
+		825402F60D92D1AC0006B936 /* debug_util.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825402F20D92D1AC0006B936 /* debug_util.cc */; };
+		825402F70D92D1AC0006B936 /* debug_on_start.h in Headers */ = {isa = PBXBuildFile; fileRef = 825402F30D92D1AC0006B936 /* debug_on_start.h */; };
+		825402F80D92D1AC0006B936 /* debug_on_start.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825402F40D92D1AC0006B936 /* debug_on_start.cc */; };
+		825403010D92D1BC0006B936 /* event_recorder.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825402FF0D92D1BC0006B936 /* event_recorder.cc */; };
+		825403020D92D1BC0006B936 /* event_recorder.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403000D92D1BC0006B936 /* event_recorder.h */; };
+		825403050D92D1C50006B936 /* file_util.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403030D92D1C50006B936 /* file_util.h */; };
+		825403060D92D1C50006B936 /* file_util.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403040D92D1C50006B936 /* file_util.cc */; };
+		825403090D92D1CD0006B936 /* file_version_info.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403070D92D1CD0006B936 /* file_version_info.h */; };
+		8254030A0D92D1CD0006B936 /* file_version_info.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403080D92D1CD0006B936 /* file_version_info.cc */; };
+		8254030C0D92D1D10006B936 /* fix_wp64.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254030B0D92D1D10006B936 /* fix_wp64.h */; };
+		8254030E0D92D1DB0006B936 /* fixed_string.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254030D0D92D1DB0006B936 /* fixed_string.h */; };
+		825403140D92D1E80006B936 /* iat_patch.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8254030F0D92D1E80006B936 /* iat_patch.cc */; };
+		825403150D92D1E80006B936 /* iat_patch.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403100D92D1E80006B936 /* iat_patch.h */; };
+		825403160D92D1E80006B936 /* icu_util.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403110D92D1E80006B936 /* icu_util.cc */; };
+		825403170D92D1E80006B936 /* icu_util.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403120D92D1E80006B936 /* icu_util.h */; };
+		825403180D92D1E80006B936 /* id_map.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403130D92D1E80006B936 /* id_map.h */; };
+		8254031F0D92D1F40006B936 /* image_util.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403190D92D1F40006B936 /* image_util.cc */; };
+		825403200D92D1F40006B936 /* image_util.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254031A0D92D1F40006B936 /* image_util.h */; };
+		825403210D92D1F40006B936 /* json_reader.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8254031B0D92D1F40006B936 /* json_reader.cc */; };
+		825403220D92D1F40006B936 /* json_reader.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254031C0D92D1F40006B936 /* json_reader.h */; };
+		825403230D92D1F40006B936 /* json_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8254031D0D92D1F40006B936 /* json_writer.cc */; };
+		825403240D92D1F40006B936 /* json_writer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254031E0D92D1F40006B936 /* json_writer.h */; };
+		8254032D0D92D2090006B936 /* lock.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403250D92D2090006B936 /* lock.cc */; };
+		8254032E0D92D2090006B936 /* lock.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403260D92D2090006B936 /* lock.h */; };
+		8254032F0D92D2090006B936 /* logging.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403270D92D2090006B936 /* logging.cc */; };
+		825403300D92D2090006B936 /* logging.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403280D92D2090006B936 /* logging.h */; };
+		825403310D92D2090006B936 /* md5.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403290D92D2090006B936 /* md5.cc */; };
+		825403320D92D2090006B936 /* md5.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254032A0D92D2090006B936 /* md5.h */; };
+		825403330D92D2090006B936 /* memory_debug.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8254032B0D92D2090006B936 /* memory_debug.cc */; };
+		825403340D92D2090006B936 /* memory_debug.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254032C0D92D2090006B936 /* memory_debug.h */; };
+		825403370D92D2110006B936 /* message_loop.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403350D92D2110006B936 /* message_loop.cc */; };
+		825403380D92D2110006B936 /* message_loop.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403360D92D2110006B936 /* message_loop.h */; };
+		825403410D92D2210006B936 /* observer_list.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403390D92D2210006B936 /* observer_list.h */; };
+		825403420D92D2210006B936 /* path_service.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8254033A0D92D2210006B936 /* path_service.cc */; };
+		825403430D92D2210006B936 /* path_service.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254033B0D92D2210006B936 /* path_service.h */; };
+		825403440D92D2210006B936 /* pe_image.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8254033C0D92D2210006B936 /* pe_image.cc */; };
+		825403450D92D2210006B936 /* pe_image.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254033D0D92D2210006B936 /* pe_image.h */; };
+		825403460D92D2210006B936 /* pickle.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8254033E0D92D2210006B936 /* pickle.cc */; };
+		825403470D92D2210006B936 /* pickle.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254033F0D92D2210006B936 /* pickle.h */; };
+		825403480D92D2210006B936 /* port.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403400D92D2210006B936 /* port.h */; };
+		8254034D0D92D23C0006B936 /* prtypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403490D92D23C0006B936 /* prtypes.h */; };
+		8254034E0D92D23C0006B936 /* prtime.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254034A0D92D23C0006B936 /* prtime.h */; };
+		8254034F0D92D23C0006B936 /* prtime.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8254034B0D92D23C0006B936 /* prtime.cc */; };
+		825403500D92D23C0006B936 /* prcpucfg.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254034C0D92D23C0006B936 /* prcpucfg.h */; };
+		825403530D92D24D0006B936 /* process_util.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403510D92D24D0006B936 /* process_util.h */; };
+		825403540D92D24D0006B936 /* process_util.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403520D92D24D0006B936 /* process_util.cc */; };
+		825403560D92D2580006B936 /* pure.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403550D92D2580006B936 /* pure.h */; };
+		825403580D92D25E0006B936 /* pure_api.c in Sources */ = {isa = PBXBuildFile; fileRef = 825403570D92D25E0006B936 /* pure_api.c */; };
+		825403640D92D27C0006B936 /* ref_counted.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403590D92D27C0006B936 /* ref_counted.h */; };
+		825403650D92D27C0006B936 /* registry.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8254035A0D92D27C0006B936 /* registry.cc */; };
+		825403660D92D27C0006B936 /* registry.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254035B0D92D27C0006B936 /* registry.h */; };
+		825403670D92D27C0006B936 /* resource_util.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8254035C0D92D27C0006B936 /* resource_util.cc */; };
+		825403680D92D27C0006B936 /* resource_util.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254035D0D92D27C0006B936 /* resource_util.h */; };
+		825403690D92D27C0006B936 /* revocable_store.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8254035E0D92D27C0006B936 /* revocable_store.cc */; };
+		8254036A0D92D27C0006B936 /* revocable_store.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254035F0D92D27C0006B936 /* revocable_store.h */; };
+		8254036C0D92D27C0006B936 /* scoped_ptr.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403610D92D27C0006B936 /* scoped_ptr.h */; };
+		8254036D0D92D27C0006B936 /* sha2.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403620D92D27C0006B936 /* sha2.cc */; };
+		8254036E0D92D27C0006B936 /* sha2.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403630D92D27C0006B936 /* sha2.h */; };
+		825403710D92D2840006B936 /* sha256.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254036F0D92D2840006B936 /* sha256.h */; };
+		825403720D92D2840006B936 /* sha512.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403700D92D2840006B936 /* sha512.cc */; };
+		825403900D92D2CF0006B936 /* shared_event.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403730D92D2CF0006B936 /* shared_event.cc */; };
+		825403910D92D2CF0006B936 /* shared_event.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403740D92D2CF0006B936 /* shared_event.h */; };
+		825403920D92D2CF0006B936 /* shared_memory.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403750D92D2CF0006B936 /* shared_memory.cc */; };
+		825403930D92D2CF0006B936 /* shared_memory.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403760D92D2CF0006B936 /* shared_memory.h */; };
+		825403940D92D2CF0006B936 /* stack_container.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403770D92D2CF0006B936 /* stack_container.h */; };
+		825403950D92D2CF0006B936 /* stats_counters.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403780D92D2CF0006B936 /* stats_counters.h */; };
+		825403960D92D2CF0006B936 /* stats_table.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403790D92D2CF0006B936 /* stats_table.cc */; };
+		825403970D92D2CF0006B936 /* stats_table.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254037A0D92D2CF0006B936 /* stats_table.h */; };
+		825403980D92D2CF0006B936 /* string_tokenizer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254037B0D92D2CF0006B936 /* string_tokenizer.h */; };
+		825403990D92D2CF0006B936 /* string_util.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8254037C0D92D2CF0006B936 /* string_util.cc */; };
+		8254039A0D92D2CF0006B936 /* string_util.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254037D0D92D2CF0006B936 /* string_util.h */; };
+		8254039B0D92D2CF0006B936 /* task.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254037E0D92D2CF0006B936 /* task.h */; };
+		8254039C0D92D2CF0006B936 /* thread.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8254037F0D92D2CF0006B936 /* thread.cc */; };
+		8254039D0D92D2CF0006B936 /* thread.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403800D92D2CF0006B936 /* thread.h */; };
+		8254039E0D92D2CF0006B936 /* thread_local_storage.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403810D92D2CF0006B936 /* thread_local_storage.cc */; };
+		8254039F0D92D2CF0006B936 /* thread_local_storage.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403820D92D2CF0006B936 /* thread_local_storage.h */; };
+		825403A00D92D2CF0006B936 /* time.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403830D92D2CF0006B936 /* time.cc */; };
+		825403A10D92D2CF0006B936 /* time.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403840D92D2CF0006B936 /* time.h */; };
+		825403A20D92D2CF0006B936 /* timer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403850D92D2CF0006B936 /* timer.cc */; };
+		825403A30D92D2CF0006B936 /* timer.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403860D92D2CF0006B936 /* timer.h */; };
+		825403A40D92D2CF0006B936 /* tuple.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403870D92D2CF0006B936 /* tuple.h */; };
+		825403A50D92D2CF0006B936 /* values.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403880D92D2CF0006B936 /* values.cc */; };
+		825403A60D92D2CF0006B936 /* values.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403890D92D2CF0006B936 /* values.h */; };
+		825403A70D92D2CF0006B936 /* win_util.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8254038A0D92D2CF0006B936 /* win_util.cc */; };
+		825403A80D92D2CF0006B936 /* win_util.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254038B0D92D2CF0006B936 /* win_util.h */; };
+		825403A90D92D2CF0006B936 /* wmi_util.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8254038C0D92D2CF0006B936 /* wmi_util.cc */; };
+		825403AA0D92D2CF0006B936 /* wmi_util.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254038D0D92D2CF0006B936 /* wmi_util.h */; };
+		825403AB0D92D2CF0006B936 /* word_iterator.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8254038E0D92D2CF0006B936 /* word_iterator.cc */; };
+		825403AC0D92D2CF0006B936 /* word_iterator.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254038F0D92D2CF0006B936 /* word_iterator.h */; };
+		825403E10D92D31D0006B936 /* bitmap_header.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403C00D92D31D0006B936 /* bitmap_header.cc */; };
+		825403E20D92D31D0006B936 /* bitmap_header.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403C10D92D31D0006B936 /* bitmap_header.h */; };
+		825403E30D92D31D0006B936 /* bitmap_platform_device.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403C20D92D31D0006B936 /* bitmap_platform_device.cc */; };
+		825403E40D92D31D0006B936 /* bitmap_platform_device.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403C30D92D31D0006B936 /* bitmap_platform_device.h */; };
+		825403E50D92D31D0006B936 /* font_utils.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403C40D92D31D0006B936 /* font_utils.cc */; };
+		825403E60D92D31D0006B936 /* font_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403C50D92D31D0006B936 /* font_utils.h */; };
+		825403E70D92D31D0006B936 /* image_resizer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403C60D92D31D0006B936 /* image_resizer.cc */; };
+		825403E80D92D31D0006B936 /* image_resizer.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403C70D92D31D0006B936 /* image_resizer.h */; };
+		825403E90D92D31D0006B936 /* native_theme.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403C80D92D31D0006B936 /* native_theme.cc */; };
+		825403EA0D92D31D0006B936 /* native_theme.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403C90D92D31D0006B936 /* native_theme.h */; };
+		825403EB0D92D31D0006B936 /* platform_canvas.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403CA0D92D31D0006B936 /* platform_canvas.h */; };
+		825403EC0D92D31D0006B936 /* platform_device.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403CB0D92D31D0006B936 /* platform_device.cc */; };
+		825403ED0D92D31D0006B936 /* platform_device.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403CC0D92D31D0006B936 /* platform_device.h */; };
+		825403EE0D92D31D0006B936 /* png_decoder.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403CD0D92D31D0006B936 /* png_decoder.cc */; };
+		825403EF0D92D31D0006B936 /* png_decoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403CE0D92D31D0006B936 /* png_decoder.h */; };
+		825403F00D92D31D0006B936 /* png_encoder.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403CF0D92D31D0006B936 /* png_encoder.cc */; };
+		825403F10D92D31D0006B936 /* png_encoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403D00D92D31D0006B936 /* png_encoder.h */; };
+		825403F20D92D31D0006B936 /* point.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403D10D92D31D0006B936 /* point.cc */; };
+		825403F30D92D31D0006B936 /* point.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403D20D92D31D0006B936 /* point.h */; };
+		825403F40D92D31D0006B936 /* rect_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403D30D92D31D0006B936 /* rect_unittest.cc */; };
+		825403F50D92D31D0006B936 /* rect.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403D40D92D31D0006B936 /* rect.cc */; };
+		825403F60D92D31D0006B936 /* rect.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403D50D92D31D0006B936 /* rect.h */; };
+		825403F70D92D31D0006B936 /* size.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403D60D92D31D0006B936 /* size.cc */; };
+		825403F80D92D31D0006B936 /* size.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403D70D92D31D0006B936 /* size.h */; };
+		825403F90D92D31D0006B936 /* skia_utils.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403D80D92D31D0006B936 /* skia_utils.cc */; };
+		825403FA0D92D31D0006B936 /* skia_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403D90D92D31D0006B936 /* skia_utils.h */; };
+		825403FB0D92D31D0006B936 /* uniscribe.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403DA0D92D31D0006B936 /* uniscribe.cc */; };
+		825403FC0D92D31D0006B936 /* uniscribe.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403DB0D92D31D0006B936 /* uniscribe.h */; };
+		825403FD0D92D31D0006B936 /* vector_canvas.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403DC0D92D31D0006B936 /* vector_canvas.cc */; };
+		825403FE0D92D31D0006B936 /* vector_canvas.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403DD0D92D31D0006B936 /* vector_canvas.h */; };
+		825403FF0D92D31D0006B936 /* vector_device.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403DE0D92D31D0006B936 /* vector_device.cc */; };
+		825404000D92D31D0006B936 /* vector_device.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403DF0D92D31D0006B936 /* vector_device.h */; };
+		825404010D92D31D0006B936 /* platform_canvas.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403E00D92D31D0006B936 /* platform_canvas.cc */; };
+		82E23FCD0D9C219600F8B40A /* platform_thread.h in Headers */ = {isa = PBXBuildFile; fileRef = 82E23FCB0D9C219600F8B40A /* platform_thread.h */; };
+		82E23FCE0D9C219600F8B40A /* platform_thread.cc in Sources */ = {isa = PBXBuildFile; fileRef = 82E23FCC0D9C219600F8B40A /* platform_thread.cc */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+		825404050D92D33A0006B936 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 825402AA0D92D0C60006B936 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 825402BA0D92D0FA0006B936;
+			remoteInfo = base;
+		};
+		825404070D92D33C0006B936 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 825402AA0D92D0C60006B936 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 825403B00D92D2E50006B936;
+			remoteInfo = base_gfx;
+		};
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+		7B5AD60D0D9DD8050012BCF1 /* scoped_cftyperef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = scoped_cftyperef.h; sourceTree = "<group>"; };
+		7BD9E84E0DA447F800FC7A01 /* singleton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = singleton.h; sourceTree = "<group>"; };
+		7BEB81100D9AD288009BA8DD /* prcpucfg_mac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = prcpucfg_mac.h; path = third_party/nspr/prcpucfg_mac.h; sourceTree = "<group>"; };
+		7BEB81490D9B0F33009BA8DD /* time_mac.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = time_mac.cc; sourceTree = "<group>"; };
+		7BEB834D0D9C4BE0009BA8DD /* string_util_mac.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = string_util_mac.cc; sourceTree = "<group>"; };
+		7BEE52C10D9D84FD0067FF23 /* string_util_mac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = string_util_mac.h; sourceTree = "<group>"; };
+		7BEFC29C0D99832D000829AD /* lock_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lock_impl.h; sourceTree = "<group>"; };
+		7BEFC29D0D99832D000829AD /* lock_impl_mac.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = lock_impl_mac.cc; sourceTree = "<group>"; };
+		821B91680DAABD7F00F350D7 /* string16.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = string16.h; sourceTree = "<group>"; };
+		825402BB0D92D0FA0006B936 /* libbase.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libbase.a; sourceTree = BUILT_PRODUCTS_DIR; };
+		825402C50D92D1390006B936 /* base_drag_source.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = base_drag_source.cc; sourceTree = "<group>"; };
+		825402C60D92D1390006B936 /* base_drag_source.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = base_drag_source.h; sourceTree = "<group>"; };
+		825402C70D92D1390006B936 /* base_drop_target.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = base_drop_target.cc; sourceTree = "<group>"; };
+		825402C80D92D1390006B936 /* base_drop_target.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = base_drop_target.h; sourceTree = "<group>"; };
+		825402C90D92D1390006B936 /* base_paths.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = base_paths.cc; sourceTree = "<group>"; };
+		825402CA0D92D1390006B936 /* base_paths.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = base_paths.h; sourceTree = "<group>"; };
+		825402CB0D92D1390006B936 /* base_switches.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = base_switches.cc; sourceTree = "<group>"; };
+		825402CC0D92D1390006B936 /* base_switches.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = base_switches.h; sourceTree = "<group>"; };
+		825402CD0D92D1390006B936 /* basictypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = basictypes.h; sourceTree = "<group>"; };
+		825402D70D92D15E0006B936 /* blapi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = blapi.h; path = third_party/nss/blapi.h; sourceTree = "<group>"; };
+		825402D80D92D15E0006B936 /* blapit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = blapit.h; path = third_party/nss/blapit.h; sourceTree = "<group>"; };
+		825402DB0D92D1730006B936 /* clipboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = clipboard.h; sourceTree = "<group>"; };
+		825402DC0D92D1730006B936 /* clipboard.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = clipboard.cc; sourceTree = "<group>"; };
+		825402DD0D92D1730006B936 /* clipboard_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = clipboard_util.h; sourceTree = "<group>"; };
+		825402DE0D92D1730006B936 /* clipboard_util.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = clipboard_util.cc; sourceTree = "<group>"; };
+		825402E30D92D1850006B936 /* command_line.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = command_line.cc; sourceTree = "<group>"; };
+		825402E40D92D1850006B936 /* command_line.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = command_line.h; sourceTree = "<group>"; };
+		825402EB0D92D1940006B936 /* condition_variable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = condition_variable.h; sourceTree = "<group>"; };
+		825402EC0D92D1940006B936 /* condition_variable.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = condition_variable.cc; sourceTree = "<group>"; };
+		825402ED0D92D1940006B936 /* condition_variable_events.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = condition_variable_events.h; sourceTree = "<group>"; };
+		825402F10D92D1AC0006B936 /* debug_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = debug_util.h; sourceTree = "<group>"; };
+		825402F20D92D1AC0006B936 /* debug_util.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = debug_util.cc; sourceTree = "<group>"; };
+		825402F30D92D1AC0006B936 /* debug_on_start.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = debug_on_start.h; sourceTree = "<group>"; };
+		825402F40D92D1AC0006B936 /* debug_on_start.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = debug_on_start.cc; sourceTree = "<group>"; };
+		825402FF0D92D1BC0006B936 /* event_recorder.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = event_recorder.cc; sourceTree = "<group>"; };
+		825403000D92D1BC0006B936 /* event_recorder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = event_recorder.h; sourceTree = "<group>"; };
+		825403030D92D1C50006B936 /* file_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file_util.h; sourceTree = "<group>"; };
+		825403040D92D1C50006B936 /* file_util.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = file_util.cc; sourceTree = "<group>"; };
+		825403070D92D1CD0006B936 /* file_version_info.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file_version_info.h; sourceTree = "<group>"; };
+		825403080D92D1CD0006B936 /* file_version_info.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = file_version_info.cc; sourceTree = "<group>"; };
+		8254030B0D92D1D10006B936 /* fix_wp64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fix_wp64.h; sourceTree = "<group>"; };
+		8254030D0D92D1DB0006B936 /* fixed_string.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fixed_string.h; sourceTree = "<group>"; };
+		8254030F0D92D1E80006B936 /* iat_patch.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = iat_patch.cc; sourceTree = "<group>"; };
+		825403100D92D1E80006B936 /* iat_patch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iat_patch.h; sourceTree = "<group>"; };
+		825403110D92D1E80006B936 /* icu_util.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = icu_util.cc; sourceTree = "<group>"; };
+		825403120D92D1E80006B936 /* icu_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = icu_util.h; sourceTree = "<group>"; };
+		825403130D92D1E80006B936 /* id_map.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = id_map.h; sourceTree = "<group>"; };
+		825403190D92D1F40006B936 /* image_util.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = image_util.cc; sourceTree = "<group>"; };
+		8254031A0D92D1F40006B936 /* image_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = image_util.h; sourceTree = "<group>"; };
+		8254031B0D92D1F40006B936 /* json_reader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = json_reader.cc; sourceTree = "<group>"; };
+		8254031C0D92D1F40006B936 /* json_reader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = json_reader.h; sourceTree = "<group>"; };
+		8254031D0D92D1F40006B936 /* json_writer.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = json_writer.cc; sourceTree = "<group>"; };
+		8254031E0D92D1F40006B936 /* json_writer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = json_writer.h; sourceTree = "<group>"; };
+		825403250D92D2090006B936 /* lock.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = lock.cc; sourceTree = "<group>"; };
+		825403260D92D2090006B936 /* lock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lock.h; sourceTree = "<group>"; };
+		825403270D92D2090006B936 /* logging.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = logging.cc; sourceTree = "<group>"; };
+		825403280D92D2090006B936 /* logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = logging.h; sourceTree = "<group>"; };
+		825403290D92D2090006B936 /* md5.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = md5.cc; sourceTree = "<group>"; };
+		8254032A0D92D2090006B936 /* md5.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = md5.h; sourceTree = "<group>"; };
+		8254032B0D92D2090006B936 /* memory_debug.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = memory_debug.cc; sourceTree = "<group>"; };
+		8254032C0D92D2090006B936 /* memory_debug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = memory_debug.h; sourceTree = "<group>"; };
+		825403350D92D2110006B936 /* message_loop.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = message_loop.cc; sourceTree = "<group>"; };
+		825403360D92D2110006B936 /* message_loop.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = message_loop.h; sourceTree = "<group>"; };
+		825403390D92D2210006B936 /* observer_list.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = observer_list.h; sourceTree = "<group>"; };
+		8254033A0D92D2210006B936 /* path_service.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = path_service.cc; sourceTree = "<group>"; };
+		8254033B0D92D2210006B936 /* path_service.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = path_service.h; sourceTree = "<group>"; };
+		8254033C0D92D2210006B936 /* pe_image.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = pe_image.cc; sourceTree = "<group>"; };
+		8254033D0D92D2210006B936 /* pe_image.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pe_image.h; sourceTree = "<group>"; };
+		8254033E0D92D2210006B936 /* pickle.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = pickle.cc; sourceTree = "<group>"; };
+		8254033F0D92D2210006B936 /* pickle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pickle.h; sourceTree = "<group>"; };
+		825403400D92D2210006B936 /* port.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = port.h; sourceTree = "<group>"; };
+		825403490D92D23C0006B936 /* prtypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = prtypes.h; path = third_party/nspr/prtypes.h; sourceTree = "<group>"; };
+		8254034A0D92D23C0006B936 /* prtime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = prtime.h; path = third_party/nspr/prtime.h; sourceTree = "<group>"; };
+		8254034B0D92D23C0006B936 /* prtime.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = prtime.cc; path = third_party/nspr/prtime.cc; sourceTree = "<group>"; };
+		8254034C0D92D23C0006B936 /* prcpucfg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = prcpucfg.h; path = third_party/nspr/prcpucfg.h; sourceTree = "<group>"; };
+		825403510D92D24D0006B936 /* process_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = process_util.h; sourceTree = "<group>"; };
+		825403520D92D24D0006B936 /* process_util.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = process_util.cc; sourceTree = "<group>"; };
+		825403550D92D2580006B936 /* pure.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = pure.h; path = third_party/purify/pure.h; sourceTree = "<group>"; };
+		825403570D92D25E0006B936 /* pure_api.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pure_api.c; path = third_party/purify/pure_api.c; sourceTree = "<group>"; };
+		825403590D92D27C0006B936 /* ref_counted.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ref_counted.h; sourceTree = "<group>"; };
+		8254035A0D92D27C0006B936 /* registry.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = registry.cc; sourceTree = "<group>"; };
+		8254035B0D92D27C0006B936 /* registry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = registry.h; sourceTree = "<group>"; };
+		8254035C0D92D27C0006B936 /* resource_util.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = resource_util.cc; sourceTree = "<group>"; };
+		8254035D0D92D27C0006B936 /* resource_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = resource_util.h; sourceTree = "<group>"; };
+		8254035E0D92D27C0006B936 /* revocable_store.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = revocable_store.cc; sourceTree = "<group>"; };
+		8254035F0D92D27C0006B936 /* revocable_store.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = revocable_store.h; sourceTree = "<group>"; };
+		825403610D92D27C0006B936 /* scoped_ptr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = scoped_ptr.h; sourceTree = "<group>"; };
+		825403620D92D27C0006B936 /* sha2.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sha2.cc; sourceTree = "<group>"; };
+		825403630D92D27C0006B936 /* sha2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sha2.h; sourceTree = "<group>"; };
+		8254036F0D92D2840006B936 /* sha256.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = sha256.h; path = third_party/nss/sha256.h; sourceTree = "<group>"; };
+		825403700D92D2840006B936 /* sha512.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = sha512.cc; path = third_party/nss/sha512.cc; sourceTree = "<group>"; };
+		825403730D92D2CF0006B936 /* shared_event.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = shared_event.cc; sourceTree = "<group>"; };
+		825403740D92D2CF0006B936 /* shared_event.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = shared_event.h; sourceTree = "<group>"; };
+		825403750D92D2CF0006B936 /* shared_memory.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = shared_memory.cc; sourceTree = "<group>"; };
+		825403760D92D2CF0006B936 /* shared_memory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = shared_memory.h; sourceTree = "<group>"; };
+		825403770D92D2CF0006B936 /* stack_container.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stack_container.h; sourceTree = "<group>"; };
+		825403780D92D2CF0006B936 /* stats_counters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stats_counters.h; sourceTree = "<group>"; };
+		825403790D92D2CF0006B936 /* stats_table.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = stats_table.cc; sourceTree = "<group>"; };
+		8254037A0D92D2CF0006B936 /* stats_table.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stats_table.h; sourceTree = "<group>"; };
+		8254037B0D92D2CF0006B936 /* string_tokenizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = string_tokenizer.h; sourceTree = "<group>"; };
+		8254037C0D92D2CF0006B936 /* string_util.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = string_util.cc; sourceTree = "<group>"; };
+		8254037D0D92D2CF0006B936 /* string_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = string_util.h; sourceTree = "<group>"; };
+		8254037E0D92D2CF0006B936 /* task.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = task.h; sourceTree = "<group>"; };
+		8254037F0D92D2CF0006B936 /* thread.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = thread.cc; sourceTree = "<group>"; };
+		825403800D92D2CF0006B936 /* thread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = thread.h; sourceTree = "<group>"; };
+		825403810D92D2CF0006B936 /* thread_local_storage.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = thread_local_storage.cc; sourceTree = "<group>"; };
+		825403820D92D2CF0006B936 /* thread_local_storage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = thread_local_storage.h; sourceTree = "<group>"; };
+		825403830D92D2CF0006B936 /* time.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = time.cc; sourceTree = "<group>"; };
+		825403840D92D2CF0006B936 /* time.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = time.h; sourceTree = "<group>"; };
+		825403850D92D2CF0006B936 /* timer.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = timer.cc; sourceTree = "<group>"; };
+		825403860D92D2CF0006B936 /* timer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = timer.h; sourceTree = "<group>"; };
+		825403870D92D2CF0006B936 /* tuple.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tuple.h; sourceTree = "<group>"; };
+		825403880D92D2CF0006B936 /* values.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = values.cc; sourceTree = "<group>"; };
+		825403890D92D2CF0006B936 /* values.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = values.h; sourceTree = "<group>"; };
+		8254038A0D92D2CF0006B936 /* win_util.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = win_util.cc; sourceTree = "<group>"; };
+		8254038B0D92D2CF0006B936 /* win_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = win_util.h; sourceTree = "<group>"; };
+		8254038C0D92D2CF0006B936 /* wmi_util.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wmi_util.cc; sourceTree = "<group>"; };
+		8254038D0D92D2CF0006B936 /* wmi_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wmi_util.h; sourceTree = "<group>"; };
+		8254038E0D92D2CF0006B936 /* word_iterator.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = word_iterator.cc; sourceTree = "<group>"; };
+		8254038F0D92D2CF0006B936 /* word_iterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = word_iterator.h; sourceTree = "<group>"; };
+		825403B10D92D2E50006B936 /* libbase_gfx.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libbase_gfx.a; sourceTree = BUILT_PRODUCTS_DIR; };
+		825403C00D92D31D0006B936 /* bitmap_header.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bitmap_header.cc; sourceTree = "<group>"; };
+		825403C10D92D31D0006B936 /* bitmap_header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bitmap_header.h; sourceTree = "<group>"; };
+		825403C20D92D31D0006B936 /* bitmap_platform_device.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bitmap_platform_device.cc; sourceTree = "<group>"; };
+		825403C30D92D31D0006B936 /* bitmap_platform_device.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bitmap_platform_device.h; sourceTree = "<group>"; };
+		825403C40D92D31D0006B936 /* font_utils.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = font_utils.cc; sourceTree = "<group>"; };
+		825403C50D92D31D0006B936 /* font_utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = font_utils.h; sourceTree = "<group>"; };
+		825403C60D92D31D0006B936 /* image_resizer.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = image_resizer.cc; sourceTree = "<group>"; };
+		825403C70D92D31D0006B936 /* image_resizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = image_resizer.h; sourceTree = "<group>"; };
+		825403C80D92D31D0006B936 /* native_theme.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = native_theme.cc; sourceTree = "<group>"; };
+		825403C90D92D31D0006B936 /* native_theme.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = native_theme.h; sourceTree = "<group>"; };
+		825403CA0D92D31D0006B936 /* platform_canvas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = platform_canvas.h; sourceTree = "<group>"; };
+		825403CB0D92D31D0006B936 /* platform_device.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = platform_device.cc; sourceTree = "<group>"; };
+		825403CC0D92D31D0006B936 /* platform_device.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = platform_device.h; sourceTree = "<group>"; };
+		825403CD0D92D31D0006B936 /* png_decoder.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = png_decoder.cc; sourceTree = "<group>"; };
+		825403CE0D92D31D0006B936 /* png_decoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = png_decoder.h; sourceTree = "<group>"; };
+		825403CF0D92D31D0006B936 /* png_encoder.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = png_encoder.cc; sourceTree = "<group>"; };
+		825403D00D92D31D0006B936 /* png_encoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = png_encoder.h; sourceTree = "<group>"; };
+		825403D10D92D31D0006B936 /* point.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = point.cc; sourceTree = "<group>"; };
+		825403D20D92D31D0006B936 /* point.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = point.h; sourceTree = "<group>"; };
+		825403D30D92D31D0006B936 /* rect_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rect_unittest.cc; sourceTree = "<group>"; };
+		825403D40D92D31D0006B936 /* rect.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rect.cc; sourceTree = "<group>"; };
+		825403D50D92D31D0006B936 /* rect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rect.h; sourceTree = "<group>"; };
+		825403D60D92D31D0006B936 /* size.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = size.cc; sourceTree = "<group>"; };
+		825403D70D92D31D0006B936 /* size.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = size.h; sourceTree = "<group>"; };
+		825403D80D92D31D0006B936 /* skia_utils.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = skia_utils.cc; sourceTree = "<group>"; };
+		825403D90D92D31D0006B936 /* skia_utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = skia_utils.h; sourceTree = "<group>"; };
+		825403DA0D92D31D0006B936 /* uniscribe.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = uniscribe.cc; sourceTree = "<group>"; };
+		825403DB0D92D31D0006B936 /* uniscribe.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = uniscribe.h; sourceTree = "<group>"; };
+		825403DC0D92D31D0006B936 /* vector_canvas.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = vector_canvas.cc; sourceTree = "<group>"; };
+		825403DD0D92D31D0006B936 /* vector_canvas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vector_canvas.h; sourceTree = "<group>"; };
+		825403DE0D92D31D0006B936 /* vector_device.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = vector_device.cc; sourceTree = "<group>"; };
+		825403DF0D92D31D0006B936 /* vector_device.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vector_device.h; sourceTree = "<group>"; };
+		825403E00D92D31D0006B936 /* platform_canvas.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = platform_canvas.cc; sourceTree = "<group>"; };
+		82E23FCB0D9C219600F8B40A /* platform_thread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = platform_thread.h; sourceTree = "<group>"; };
+		82E23FCC0D9C219600F8B40A /* platform_thread.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = platform_thread.cc; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		825402B90D92D0FA0006B936 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		825403AF0D92D2E50006B936 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		825402A80D92D0C60006B936 = {
+			isa = PBXGroup;
+			children = (
+				825402B60D92D0E20006B936 /* base */,
+				825403B40D92D2EC0006B936 /* base_gfx */,
+				825402BC0D92D0FA0006B936 /* Products */,
+			);
+			sourceTree = "<group>";
+		};
+		825402B60D92D0E20006B936 /* base */ = {
+			isa = PBXGroup;
+			children = (
+				825402C50D92D1390006B936 /* base_drag_source.cc */,
+				825402C60D92D1390006B936 /* base_drag_source.h */,
+				825402C70D92D1390006B936 /* base_drop_target.cc */,
+				825402C80D92D1390006B936 /* base_drop_target.h */,
+				825402C90D92D1390006B936 /* base_paths.cc */,
+				825402CA0D92D1390006B936 /* base_paths.h */,
+				825402CB0D92D1390006B936 /* base_switches.cc */,
+				825402CC0D92D1390006B936 /* base_switches.h */,
+				825402CD0D92D1390006B936 /* basictypes.h */,
+				825402D70D92D15E0006B936 /* blapi.h */,
+				825402D80D92D15E0006B936 /* blapit.h */,
+				825402DB0D92D1730006B936 /* clipboard.h */,
+				825402DC0D92D1730006B936 /* clipboard.cc */,
+				825402DD0D92D1730006B936 /* clipboard_util.h */,
+				825402DE0D92D1730006B936 /* clipboard_util.cc */,
+				825402E40D92D1850006B936 /* command_line.h */,
+				825402E30D92D1850006B936 /* command_line.cc */,
+				825402EB0D92D1940006B936 /* condition_variable.h */,
+				825402EC0D92D1940006B936 /* condition_variable.cc */,
+				825402ED0D92D1940006B936 /* condition_variable_events.h */,
+				825402F30D92D1AC0006B936 /* debug_on_start.h */,
+				825402F40D92D1AC0006B936 /* debug_on_start.cc */,
+				825402F10D92D1AC0006B936 /* debug_util.h */,
+				825402F20D92D1AC0006B936 /* debug_util.cc */,
+				825402FF0D92D1BC0006B936 /* event_recorder.cc */,
+				825403000D92D1BC0006B936 /* event_recorder.h */,
+				825403030D92D1C50006B936 /* file_util.h */,
+				825403040D92D1C50006B936 /* file_util.cc */,
+				825403070D92D1CD0006B936 /* file_version_info.h */,
+				825403080D92D1CD0006B936 /* file_version_info.cc */,
+				8254030B0D92D1D10006B936 /* fix_wp64.h */,
+				8254030D0D92D1DB0006B936 /* fixed_string.h */,
+				8254030F0D92D1E80006B936 /* iat_patch.cc */,
+				825403100D92D1E80006B936 /* iat_patch.h */,
+				825403110D92D1E80006B936 /* icu_util.cc */,
+				825403120D92D1E80006B936 /* icu_util.h */,
+				825403130D92D1E80006B936 /* id_map.h */,
+				825403190D92D1F40006B936 /* image_util.cc */,
+				8254031A0D92D1F40006B936 /* image_util.h */,
+				8254031B0D92D1F40006B936 /* json_reader.cc */,
+				8254031C0D92D1F40006B936 /* json_reader.h */,
+				8254031D0D92D1F40006B936 /* json_writer.cc */,
+				8254031E0D92D1F40006B936 /* json_writer.h */,
+				825403250D92D2090006B936 /* lock.cc */,
+				825403260D92D2090006B936 /* lock.h */,
+				7BEFC29C0D99832D000829AD /* lock_impl.h */,
+				7BEFC29D0D99832D000829AD /* lock_impl_mac.cc */,
+				825403270D92D2090006B936 /* logging.cc */,
+				825403280D92D2090006B936 /* logging.h */,
+				825403290D92D2090006B936 /* md5.cc */,
+				8254032A0D92D2090006B936 /* md5.h */,
+				8254032B0D92D2090006B936 /* memory_debug.cc */,
+				8254032C0D92D2090006B936 /* memory_debug.h */,
+				825403350D92D2110006B936 /* message_loop.cc */,
+				825403360D92D2110006B936 /* message_loop.h */,
+				825403390D92D2210006B936 /* observer_list.h */,
+				8254033A0D92D2210006B936 /* path_service.cc */,
+				8254033B0D92D2210006B936 /* path_service.h */,
+				8254033C0D92D2210006B936 /* pe_image.cc */,
+				8254033D0D92D2210006B936 /* pe_image.h */,
+				8254033E0D92D2210006B936 /* pickle.cc */,
+				8254033F0D92D2210006B936 /* pickle.h */,
+				82E23FCB0D9C219600F8B40A /* platform_thread.h */,
+				82E23FCC0D9C219600F8B40A /* platform_thread.cc */,
+				825403400D92D2210006B936 /* port.h */,
+				825403490D92D23C0006B936 /* prtypes.h */,
+				825403510D92D24D0006B936 /* process_util.h */,
+				825403520D92D24D0006B936 /* process_util.cc */,
+				8254034A0D92D23C0006B936 /* prtime.h */,
+				8254034B0D92D23C0006B936 /* prtime.cc */,
+				8254034C0D92D23C0006B936 /* prcpucfg.h */,
+				7BEB81100D9AD288009BA8DD /* prcpucfg_mac.h */,
+				825403550D92D2580006B936 /* pure.h */,
+				825403570D92D25E0006B936 /* pure_api.c */,
+				825403590D92D27C0006B936 /* ref_counted.h */,
+				8254035A0D92D27C0006B936 /* registry.cc */,
+				8254035B0D92D27C0006B936 /* registry.h */,
+				8254035C0D92D27C0006B936 /* resource_util.cc */,
+				8254035D0D92D27C0006B936 /* resource_util.h */,
+				8254035E0D92D27C0006B936 /* revocable_store.cc */,
+				8254035F0D92D27C0006B936 /* revocable_store.h */,
+				7B5AD60D0D9DD8050012BCF1 /* scoped_cftyperef.h */,
+				825403610D92D27C0006B936 /* scoped_ptr.h */,
+				825403620D92D27C0006B936 /* sha2.cc */,
+				825403630D92D27C0006B936 /* sha2.h */,
+				8254036F0D92D2840006B936 /* sha256.h */,
+				825403700D92D2840006B936 /* sha512.cc */,
+				825403730D92D2CF0006B936 /* shared_event.cc */,
+				825403740D92D2CF0006B936 /* shared_event.h */,
+				825403750D92D2CF0006B936 /* shared_memory.cc */,
+				825403760D92D2CF0006B936 /* shared_memory.h */,
+				7BD9E84E0DA447F800FC7A01 /* singleton.h */,
+				825403770D92D2CF0006B936 /* stack_container.h */,
+				825403780D92D2CF0006B936 /* stats_counters.h */,
+				825403790D92D2CF0006B936 /* stats_table.cc */,
+				8254037A0D92D2CF0006B936 /* stats_table.h */,
+				8254037B0D92D2CF0006B936 /* string_tokenizer.h */,
+				8254037C0D92D2CF0006B936 /* string_util.cc */,
+				8254037D0D92D2CF0006B936 /* string_util.h */,
+				7BEB834D0D9C4BE0009BA8DD /* string_util_mac.cc */,
+				7BEE52C10D9D84FD0067FF23 /* string_util_mac.h */,
+				821B91680DAABD7F00F350D7 /* string16.h */,
+				8254037E0D92D2CF0006B936 /* task.h */,
+				8254037F0D92D2CF0006B936 /* thread.cc */,
+				825403800D92D2CF0006B936 /* thread.h */,
+				825403810D92D2CF0006B936 /* thread_local_storage.cc */,
+				825403820D92D2CF0006B936 /* thread_local_storage.h */,
+				825403830D92D2CF0006B936 /* time.cc */,
+				825403840D92D2CF0006B936 /* time.h */,
+				7BEB81490D9B0F33009BA8DD /* time_mac.cc */,
+				825403850D92D2CF0006B936 /* timer.cc */,
+				825403860D92D2CF0006B936 /* timer.h */,
+				825403870D92D2CF0006B936 /* tuple.h */,
+				825403880D92D2CF0006B936 /* values.cc */,
+				825403890D92D2CF0006B936 /* values.h */,
+				8254038A0D92D2CF0006B936 /* win_util.cc */,
+				8254038B0D92D2CF0006B936 /* win_util.h */,
+				8254038C0D92D2CF0006B936 /* wmi_util.cc */,
+				8254038D0D92D2CF0006B936 /* wmi_util.h */,
+				8254038E0D92D2CF0006B936 /* word_iterator.cc */,
+				8254038F0D92D2CF0006B936 /* word_iterator.h */,
+			);
+			name = base;
+			sourceTree = "<group>";
+		};
+		825402BC0D92D0FA0006B936 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				825402BB0D92D0FA0006B936 /* libbase.a */,
+				825403B10D92D2E50006B936 /* libbase_gfx.a */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		825403B40D92D2EC0006B936 /* base_gfx */ = {
+			isa = PBXGroup;
+			children = (
+				825403C00D92D31D0006B936 /* bitmap_header.cc */,
+				825403C10D92D31D0006B936 /* bitmap_header.h */,
+				825403C20D92D31D0006B936 /* bitmap_platform_device.cc */,
+				825403C30D92D31D0006B936 /* bitmap_platform_device.h */,
+				825403C40D92D31D0006B936 /* font_utils.cc */,
+				825403C50D92D31D0006B936 /* font_utils.h */,
+				825403C60D92D31D0006B936 /* image_resizer.cc */,
+				825403C70D92D31D0006B936 /* image_resizer.h */,
+				825403C80D92D31D0006B936 /* native_theme.cc */,
+				825403C90D92D31D0006B936 /* native_theme.h */,
+				825403CA0D92D31D0006B936 /* platform_canvas.h */,
+				825403CB0D92D31D0006B936 /* platform_device.cc */,
+				825403CC0D92D31D0006B936 /* platform_device.h */,
+				825403CD0D92D31D0006B936 /* png_decoder.cc */,
+				825403CE0D92D31D0006B936 /* png_decoder.h */,
+				825403CF0D92D31D0006B936 /* png_encoder.cc */,
+				825403D00D92D31D0006B936 /* png_encoder.h */,
+				825403D10D92D31D0006B936 /* point.cc */,
+				825403D20D92D31D0006B936 /* point.h */,
+				825403D30D92D31D0006B936 /* rect_unittest.cc */,
+				825403D40D92D31D0006B936 /* rect.cc */,
+				825403D50D92D31D0006B936 /* rect.h */,
+				825403D60D92D31D0006B936 /* size.cc */,
+				825403D70D92D31D0006B936 /* size.h */,
+				825403D80D92D31D0006B936 /* skia_utils.cc */,
+				825403D90D92D31D0006B936 /* skia_utils.h */,
+				825403DA0D92D31D0006B936 /* uniscribe.cc */,
+				825403DB0D92D31D0006B936 /* uniscribe.h */,
+				825403DC0D92D31D0006B936 /* vector_canvas.cc */,
+				825403DD0D92D31D0006B936 /* vector_canvas.h */,
+				825403DE0D92D31D0006B936 /* vector_device.cc */,
+				825403DF0D92D31D0006B936 /* vector_device.h */,
+				825403E00D92D31D0006B936 /* platform_canvas.cc */,
+			);
+			name = base_gfx;
+			path = gfx;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+		825402B70D92D0FA0006B936 /* Headers */ = {
+			isa = PBXHeadersBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				825402CF0D92D1390006B936 /* base_drag_source.h in Headers */,
+				825402D10D92D1390006B936 /* base_drop_target.h in Headers */,
+				825402D30D92D1390006B936 /* base_paths.h in Headers */,
+				825402D50D92D1390006B936 /* base_switches.h in Headers */,
+				825402D60D92D1390006B936 /* basictypes.h in Headers */,
+				825402D90D92D15E0006B936 /* blapi.h in Headers */,
+				825402DA0D92D15E0006B936 /* blapit.h in Headers */,
+				825402DF0D92D1730006B936 /* clipboard.h in Headers */,
+				825402E10D92D1730006B936 /* clipboard_util.h in Headers */,
+				825402E60D92D1850006B936 /* command_line.h in Headers */,
+				825402EE0D92D1940006B936 /* condition_variable.h in Headers */,
+				825402F00D92D1940006B936 /* condition_variable_events.h in Headers */,
+				825402F50D92D1AC0006B936 /* debug_util.h in Headers */,
+				825402F70D92D1AC0006B936 /* debug_on_start.h in Headers */,
+				825403020D92D1BC0006B936 /* event_recorder.h in Headers */,
+				825403050D92D1C50006B936 /* file_util.h in Headers */,
+				825403090D92D1CD0006B936 /* file_version_info.h in Headers */,
+				8254030C0D92D1D10006B936 /* fix_wp64.h in Headers */,
+				8254030E0D92D1DB0006B936 /* fixed_string.h in Headers */,
+				825403150D92D1E80006B936 /* iat_patch.h in Headers */,
+				825403170D92D1E80006B936 /* icu_util.h in Headers */,
+				825403180D92D1E80006B936 /* id_map.h in Headers */,
+				825403200D92D1F40006B936 /* image_util.h in Headers */,
+				825403220D92D1F40006B936 /* json_reader.h in Headers */,
+				825403240D92D1F40006B936 /* json_writer.h in Headers */,
+				8254032E0D92D2090006B936 /* lock.h in Headers */,
+				7BEFC29E0D99832D000829AD /* lock_impl.h in Headers */,
+				825403300D92D2090006B936 /* logging.h in Headers */,
+				825403320D92D2090006B936 /* md5.h in Headers */,
+				825403340D92D2090006B936 /* memory_debug.h in Headers */,
+				825403380D92D2110006B936 /* message_loop.h in Headers */,
+				825403410D92D2210006B936 /* observer_list.h in Headers */,
+				825403430D92D2210006B936 /* path_service.h in Headers */,
+				825403450D92D2210006B936 /* pe_image.h in Headers */,
+				825403470D92D2210006B936 /* pickle.h in Headers */,
+				82E23FCD0D9C219600F8B40A /* platform_thread.h in Headers */,
+				825403480D92D2210006B936 /* port.h in Headers */,
+				8254034D0D92D23C0006B936 /* prtypes.h in Headers */,
+				8254034E0D92D23C0006B936 /* prtime.h in Headers */,
+				825403500D92D23C0006B936 /* prcpucfg.h in Headers */,
+				7BEB81110D9AD288009BA8DD /* prcpucfg_mac.h in Headers */,
+				825403530D92D24D0006B936 /* process_util.h in Headers */,
+				825403560D92D2580006B936 /* pure.h in Headers */,
+				825403640D92D27C0006B936 /* ref_counted.h in Headers */,
+				825403660D92D27C0006B936 /* registry.h in Headers */,
+				825403680D92D27C0006B936 /* resource_util.h in Headers */,
+				8254036A0D92D27C0006B936 /* revocable_store.h in Headers */,
+				7B5AD60E0D9DD8050012BCF1 /* scoped_cftyperef.h in Headers */,
+				8254036C0D92D27C0006B936 /* scoped_ptr.h in Headers */,
+				8254036E0D92D27C0006B936 /* sha2.h in Headers */,
+				825403710D92D2840006B936 /* sha256.h in Headers */,
+				825403910D92D2CF0006B936 /* shared_event.h in Headers */,
+				825403930D92D2CF0006B936 /* shared_memory.h in Headers */,
+				7BD9E84F0DA447F800FC7A01 /* singleton.h in Headers */,
+				825403940D92D2CF0006B936 /* stack_container.h in Headers */,
+				825403950D92D2CF0006B936 /* stats_counters.h in Headers */,
+				825403970D92D2CF0006B936 /* stats_table.h in Headers */,
+				825403980D92D2CF0006B936 /* string_tokenizer.h in Headers */,
+				8254039A0D92D2CF0006B936 /* string_util.h in Headers */,
+				7BEE52C20D9D84FD0067FF23 /* string_util_mac.h in Headers */,
+				821B91690DAABD7F00F350D7 /* string16.h in Headers */,
+				8254039B0D92D2CF0006B936 /* task.h in Headers */,
+				8254039D0D92D2CF0006B936 /* thread.h in Headers */,
+				8254039F0D92D2CF0006B936 /* thread_local_storage.h in Headers */,
+				825403A10D92D2CF0006B936 /* time.h in Headers */,
+				825403A30D92D2CF0006B936 /* timer.h in Headers */,
+				825403A40D92D2CF0006B936 /* tuple.h in Headers */,
+				825403A60D92D2CF0006B936 /* values.h in Headers */,
+				825403A80D92D2CF0006B936 /* win_util.h in Headers */,
+				825403AA0D92D2CF0006B936 /* wmi_util.h in Headers */,
+				825403AC0D92D2CF0006B936 /* word_iterator.h in Headers */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		825403AD0D92D2E50006B936 /* Headers */ = {
+			isa = PBXHeadersBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				825403E20D92D31D0006B936 /* bitmap_header.h in Headers */,
+				825403E40D92D31D0006B936 /* bitmap_platform_device.h in Headers */,
+				825403E60D92D31D0006B936 /* font_utils.h in Headers */,
+				825403E80D92D31D0006B936 /* image_resizer.h in Headers */,
+				825403EA0D92D31D0006B936 /* native_theme.h in Headers */,
+				825403EB0D92D31D0006B936 /* platform_canvas.h in Headers */,
+				825403ED0D92D31D0006B936 /* platform_device.h in Headers */,
+				825403EF0D92D31D0006B936 /* png_decoder.h in Headers */,
+				825403F10D92D31D0006B936 /* png_encoder.h in Headers */,
+				825403F30D92D31D0006B936 /* point.h in Headers */,
+				825403F60D92D31D0006B936 /* rect.h in Headers */,
+				825403F80D92D31D0006B936 /* size.h in Headers */,
+				825403FA0D92D31D0006B936 /* skia_utils.h in Headers */,
+				825403FC0D92D31D0006B936 /* uniscribe.h in Headers */,
+				825403FE0D92D31D0006B936 /* vector_canvas.h in Headers */,
+				825404000D92D31D0006B936 /* vector_device.h in Headers */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+		825402BA0D92D0FA0006B936 /* base */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 825402BF0D92D0FB0006B936 /* Build configuration list for PBXNativeTarget "base" */;
+			buildPhases = (
+				825402B70D92D0FA0006B936 /* Headers */,
+				825402B80D92D0FA0006B936 /* Sources */,
+				825402B90D92D0FA0006B936 /* Frameworks */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = base;
+			productName = base;
+			productReference = 825402BB0D92D0FA0006B936 /* libbase.a */;
+			productType = "com.apple.product-type.library.static";
+		};
+		825403B00D92D2E50006B936 /* base_gfx */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 825403B50D92D2EC0006B936 /* Build configuration list for PBXNativeTarget "base_gfx" */;
+			buildPhases = (
+				825403AD0D92D2E50006B936 /* Headers */,
+				825403AE0D92D2E50006B936 /* Sources */,
+				825403AF0D92D2E50006B936 /* Frameworks */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = base_gfx;
+			productName = base_gfx;
+			productReference = 825403B10D92D2E50006B936 /* libbase_gfx.a */;
+			productType = "com.apple.product-type.library.static";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		825402AA0D92D0C60006B936 /* Project object */ = {
+			isa = PBXProject;
+			buildConfigurationList = 825402AD0D92D0C60006B936 /* Build configuration list for PBXProject "base" */;
+			compatibilityVersion = "Xcode 2.4";
+			hasScannedForEncodings = 0;
+			mainGroup = 825402A80D92D0C60006B936;
+			productRefGroup = 825402BC0D92D0FA0006B936 /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				825402BA0D92D0FA0006B936 /* base */,
+				825403B00D92D2E50006B936 /* base_gfx */,
+				825404020D92D3340006B936 /* All */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXSourcesBuildPhase section */
+		825402B80D92D0FA0006B936 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				825402CE0D92D1390006B936 /* base_drag_source.cc in Sources */,
+				825402D00D92D1390006B936 /* base_drop_target.cc in Sources */,
+				825402D20D92D1390006B936 /* base_paths.cc in Sources */,
+				825402D40D92D1390006B936 /* base_switches.cc in Sources */,
+				825402E00D92D1730006B936 /* clipboard.cc in Sources */,
+				825402E20D92D1730006B936 /* clipboard_util.cc in Sources */,
+				825402E50D92D1850006B936 /* command_line.cc in Sources */,
+				825402EF0D92D1940006B936 /* condition_variable.cc in Sources */,
+				825402F60D92D1AC0006B936 /* debug_util.cc in Sources */,
+				825402F80D92D1AC0006B936 /* debug_on_start.cc in Sources */,
+				825403010D92D1BC0006B936 /* event_recorder.cc in Sources */,
+				825403060D92D1C50006B936 /* file_util.cc in Sources */,
+				8254030A0D92D1CD0006B936 /* file_version_info.cc in Sources */,
+				825403140D92D1E80006B936 /* iat_patch.cc in Sources */,
+				825403160D92D1E80006B936 /* icu_util.cc in Sources */,
+				8254031F0D92D1F40006B936 /* image_util.cc in Sources */,
+				825403210D92D1F40006B936 /* json_reader.cc in Sources */,
+				825403230D92D1F40006B936 /* json_writer.cc in Sources */,
+				8254032D0D92D2090006B936 /* lock.cc in Sources */,
+				7BEFC29F0D99832D000829AD /* lock_impl_mac.cc in Sources */,
+				8254032F0D92D2090006B936 /* logging.cc in Sources */,
+				825403310D92D2090006B936 /* md5.cc in Sources */,
+				825403330D92D2090006B936 /* memory_debug.cc in Sources */,
+				825403370D92D2110006B936 /* message_loop.cc in Sources */,
+				825403420D92D2210006B936 /* path_service.cc in Sources */,
+				825403440D92D2210006B936 /* pe_image.cc in Sources */,
+				825403460D92D2210006B936 /* pickle.cc in Sources */,
+				82E23FCE0D9C219600F8B40A /* platform_thread.cc in Sources */,
+				8254034F0D92D23C0006B936 /* prtime.cc in Sources */,
+				825403540D92D24D0006B936 /* process_util.cc in Sources */,
+				825403580D92D25E0006B936 /* pure_api.c in Sources */,
+				825403650D92D27C0006B936 /* registry.cc in Sources */,
+				825403670D92D27C0006B936 /* resource_util.cc in Sources */,
+				825403690D92D27C0006B936 /* revocable_store.cc in Sources */,
+				8254036D0D92D27C0006B936 /* sha2.cc in Sources */,
+				825403720D92D2840006B936 /* sha512.cc in Sources */,
+				825403900D92D2CF0006B936 /* shared_event.cc in Sources */,
+				825403920D92D2CF0006B936 /* shared_memory.cc in Sources */,
+				825403960D92D2CF0006B936 /* stats_table.cc in Sources */,
+				825403990D92D2CF0006B936 /* string_util.cc in Sources */,
+				7BEB834E0D9C4BE0009BA8DD /* string_util_mac.cc in Sources */,
+				8254039C0D92D2CF0006B936 /* thread.cc in Sources */,
+				8254039E0D92D2CF0006B936 /* thread_local_storage.cc in Sources */,
+				825403A00D92D2CF0006B936 /* time.cc in Sources */,
+				7BEB814A0D9B0F33009BA8DD /* time_mac.cc in Sources */,
+				825403A20D92D2CF0006B936 /* timer.cc in Sources */,
+				825403A50D92D2CF0006B936 /* values.cc in Sources */,
+				825403A70D92D2CF0006B936 /* win_util.cc in Sources */,
+				825403A90D92D2CF0006B936 /* wmi_util.cc in Sources */,
+				825403AB0D92D2CF0006B936 /* word_iterator.cc in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		825403AE0D92D2E50006B936 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				825403E10D92D31D0006B936 /* bitmap_header.cc in Sources */,
+				825403E30D92D31D0006B936 /* bitmap_platform_device.cc in Sources */,
+				825403E50D92D31D0006B936 /* font_utils.cc in Sources */,
+				825403E70D92D31D0006B936 /* image_resizer.cc in Sources */,
+				825403E90D92D31D0006B936 /* native_theme.cc in Sources */,
+				825403EC0D92D31D0006B936 /* platform_device.cc in Sources */,
+				825403EE0D92D31D0006B936 /* png_decoder.cc in Sources */,
+				825403F00D92D31D0006B936 /* png_encoder.cc in Sources */,
+				825403F20D92D31D0006B936 /* point.cc in Sources */,
+				825403F40D92D31D0006B936 /* rect_unittest.cc in Sources */,
+				825403F50D92D31D0006B936 /* rect.cc in Sources */,
+				825403F70D92D31D0006B936 /* size.cc in Sources */,
+				825403F90D92D31D0006B936 /* skia_utils.cc in Sources */,
+				825403FB0D92D31D0006B936 /* uniscribe.cc in Sources */,
+				825403FD0D92D31D0006B936 /* vector_canvas.cc in Sources */,
+				825403FF0D92D31D0006B936 /* vector_device.cc in Sources */,
+				825404010D92D31D0006B936 /* platform_canvas.cc in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+		825404060D92D33A0006B936 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 825402BA0D92D0FA0006B936 /* base */;
+			targetProxy = 825404050D92D33A0006B936 /* PBXContainerItemProxy */;
+		};
+		825404080D92D33C0006B936 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 825403B00D92D2E50006B936 /* base_gfx */;
+			targetProxy = 825404070D92D33C0006B936 /* PBXContainerItemProxy */;
+		};
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+		825402AB0D92D0C60006B936 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				COPY_PHASE_STRIP = NO;
+				GCC_CW_ASM_SYNTAX = NO;
+				GCC_ENABLE_PASCAL_STRINGS = NO;
+				GCC_PREPROCESSOR_DEFINITIONS = DEBUG;
+				USE_HEADERMAP = NO;
+				WARNING_CFLAGS = "-Wall";
+			};
+			name = Debug;
+		};
+		825402AC0D92D0C60006B936 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				COPY_PHASE_STRIP = YES;
+				GCC_CW_ASM_SYNTAX = NO;
+				GCC_ENABLE_PASCAL_STRINGS = NO;
+				GCC_PREPROCESSOR_DEFINITIONS = NDEBUG;
+				USE_HEADERMAP = NO;
+				WARNING_CFLAGS = "-Wall";
+			};
+			name = Release;
+		};
+		825402BD0D92D0FB0006B936 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				COPY_PHASE_STRIP = NO;
+				GCC_DYNAMIC_NO_PIC = YES;
+				GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_SYMBOLS_PRIVATE_EXTERN = YES;
+				HEADER_SEARCH_PATHS = (
+					..,
+					../third_party/icu38/public/common,
+					../third_party/icu38/public/i18n,
+				);
+				PREBINDING = NO;
+				PRODUCT_NAME = base;
+			};
+			name = Debug;
+		};
+		825402BE0D92D0FB0006B936 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				COPY_PHASE_STRIP = YES;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				GCC_DYNAMIC_NO_PIC = YES;
+				GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
+				GCC_SYMBOLS_PRIVATE_EXTERN = YES;
+				HEADER_SEARCH_PATHS = (
+					..,
+					../third_party/icu38/public/common,
+					../third_party/icu38/public/i18n,
+				);
+				PREBINDING = NO;
+				PRODUCT_NAME = base;
+			};
+			name = Release;
+		};
+		825403B20D92D2E50006B936 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				COPY_PHASE_STRIP = NO;
+				GCC_DYNAMIC_NO_PIC = YES;
+				GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_SYMBOLS_PRIVATE_EXTERN = YES;
+				PREBINDING = NO;
+				PRODUCT_NAME = base_gfx;
+			};
+			name = Debug;
+		};
+		825403B30D92D2E50006B936 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				COPY_PHASE_STRIP = YES;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				GCC_DYNAMIC_NO_PIC = YES;
+				GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
+				GCC_SYMBOLS_PRIVATE_EXTERN = YES;
+				PREBINDING = NO;
+				PRODUCT_NAME = base_gfx;
+			};
+			name = Release;
+		};
+		825404030D92D3340006B936 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				COPY_PHASE_STRIP = NO;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				PRODUCT_NAME = All;
+			};
+			name = Debug;
+		};
+		825404040D92D3340006B936 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				COPY_PHASE_STRIP = YES;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				GCC_ENABLE_FIX_AND_CONTINUE = NO;
+				PRODUCT_NAME = All;
+				ZERO_LINK = NO;
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		825402AD0D92D0C60006B936 /* Build configuration list for PBXProject "base" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				825402AB0D92D0C60006B936 /* Debug */,
+				825402AC0D92D0C60006B936 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		825402BF0D92D0FB0006B936 /* Build configuration list for PBXNativeTarget "base" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				825402BD0D92D0FB0006B936 /* Debug */,
+				825402BE0D92D0FB0006B936 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		825403B50D92D2EC0006B936 /* Build configuration list for PBXNativeTarget "base_gfx" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				825403B20D92D2E50006B936 /* Debug */,
+				825403B30D92D2E50006B936 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		825404110D92D35C0006B936 /* Build configuration list for PBXAggregateTarget "All" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				825404030D92D3340006B936 /* Debug */,
+				825404040D92D3340006B936 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 825402AA0D92D0C60006B936 /* Project object */;
+}
diff --git a/base/base_drag_source.cc b/base/base_drag_source.cc
new file mode 100644
index 0000000..daf40c7
--- /dev/null
+++ b/base/base_drag_source.cc
@@ -0,0 +1,88 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <atlbase.h>
+
+#include "base/base_drag_source.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// BaseDragSource, public:
+
+BaseDragSource::BaseDragSource() : ref_count_(0) {
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// BaseDragSource, IDropSource implementation:
+
+HRESULT BaseDragSource::QueryContinueDrag(BOOL escape_pressed,
+                                          DWORD key_state) {
+  if (escape_pressed) {
+    OnDragSourceCancel();
+    return DRAGDROP_S_CANCEL;
+  }
+
+  if (!(key_state & MK_LBUTTON)) {
+    OnDragSourceDrop();
+    return DRAGDROP_S_DROP;
+  }
+
+  OnDragSourceMove();
+  return S_OK;
+}
+
+HRESULT BaseDragSource::GiveFeedback(DWORD effect) {
+  return DRAGDROP_S_USEDEFAULTCURSORS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// BaseDragSource, IUnknown implementation:
+
+HRESULT BaseDragSource::QueryInterface(const IID& iid, void** object) {
+  *object = NULL;
+  if (IsEqualIID(iid, IID_IUnknown) || IsEqualIID(iid, IID_IDropSource)) {
+    *object = this;
+  } else {
+    return E_NOINTERFACE;
+  }
+  AddRef();
+  return S_OK;
+}
+
+ULONG BaseDragSource::AddRef() {
+  return InterlockedIncrement(&ref_count_);
+}
+
+ULONG BaseDragSource::Release() {
+  if (InterlockedDecrement(&ref_count_) == 0) {
+    ULONG copied_refcnt = ref_count_;
+    delete this;
+    return copied_refcnt;
+  }
+  return ref_count_;
+}
diff --git a/base/base_drag_source.h b/base/base_drag_source.h
new file mode 100644
index 0000000..6c5b25d
--- /dev/null
+++ b/base/base_drag_source.h
@@ -0,0 +1,71 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_BASE_DRAG_SOURCE_H__
+#define BASE_BASE_DRAG_SOURCE_H__
+
+#include <objidl.h>
+
+#include "base/basictypes.h"
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// BaseDragSource
+//
+//  A base IDropSource implementation. Handles notifications sent by an active
+//  drag-drop operation as the user mouses over other drop targets on their
+//  system. This object tells Windows whether or not the drag should continue,
+//  and supplies the appropriate cursors.
+//
+class BaseDragSource : public IDropSource {
+ public:
+  BaseDragSource();
+  virtual ~BaseDragSource() { }
+
+  // IDropSource implementation:
+  HRESULT __stdcall QueryContinueDrag(BOOL escape_pressed, DWORD key_state);
+  HRESULT __stdcall GiveFeedback(DWORD effect);
+
+  // IUnknown implementation:
+  HRESULT __stdcall QueryInterface(const IID& iid, void** object);
+  ULONG __stdcall AddRef();
+  ULONG __stdcall Release();
+
+ protected:
+  virtual void OnDragSourceCancel() { }
+  virtual void OnDragSourceDrop() { }
+  virtual void OnDragSourceMove() { }
+
+ private:
+  LONG ref_count_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(BaseDragSource);
+};
+
+#endif  // #ifndef BASE_DRAG_SOURCE_H__
diff --git a/base/base_drop_target.cc b/base/base_drop_target.cc
new file mode 100644
index 0000000..f210381
--- /dev/null
+++ b/base/base_drop_target.cc
@@ -0,0 +1,187 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+#include <shlobj.h>
+
+#include "base/base_drop_target.h"
+
+#include "base/logging.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+IDropTargetHelper* BaseDropTarget::cached_drop_target_helper_ = NULL;
+
+BaseDropTarget::BaseDropTarget(HWND hwnd)
+    : suspend_(false),
+      ref_count_(0),
+      hwnd_(hwnd) {
+  DCHECK(hwnd);
+  HRESULT result = RegisterDragDrop(hwnd, this);
+}
+
+BaseDropTarget::~BaseDropTarget() {
+}
+
+// static
+IDropTargetHelper* BaseDropTarget::DropHelper() {
+  if (!cached_drop_target_helper_) {
+    CoCreateInstance(CLSID_DragDropHelper, 0, CLSCTX_INPROC_SERVER,
+                     IID_IDropTargetHelper,
+                     reinterpret_cast<void**>(&cached_drop_target_helper_));
+  }
+  return cached_drop_target_helper_;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// BaseDropTarget, IDropTarget implementation:
+
+HRESULT BaseDropTarget::DragEnter(IDataObject* data_object,
+                                  DWORD key_state,
+                                  POINTL cursor_position,
+                                  DWORD* effect) {
+  // Tell the helper that we entered so it can update the drag image.
+  IDropTargetHelper* drop_helper = DropHelper();
+  if (drop_helper) {
+    drop_helper->DragEnter(GetHWND(), data_object,
+                           reinterpret_cast<POINT*>(&cursor_position), *effect);
+  }
+
+  // You can't drag and drop within the same HWND.
+  if (suspend_) {
+    *effect = DROPEFFECT_NONE;
+    return S_OK;
+  }
+  current_data_object_ = data_object;
+  POINT screen_pt = { cursor_position.x, cursor_position.y };
+  *effect = OnDragEnter(current_data_object_, key_state, screen_pt, *effect);
+  return S_OK;
+}
+
+HRESULT BaseDropTarget::DragOver(DWORD key_state,
+                                 POINTL cursor_position,
+                                 DWORD* effect) {
+  // Tell the helper that we moved over it so it can update the drag image.
+  IDropTargetHelper* drop_helper = DropHelper();
+  if (drop_helper)
+    drop_helper->DragOver(reinterpret_cast<POINT*>(&cursor_position), *effect);
+
+  if (suspend_) {
+    *effect = DROPEFFECT_NONE;
+    return S_OK;
+  }
+
+  POINT screen_pt = { cursor_position.x, cursor_position.y };
+  *effect = OnDragOver(current_data_object_, key_state, screen_pt, *effect);
+  return S_OK;
+}
+
+HRESULT BaseDropTarget::DragLeave() {
+  // Tell the helper that we moved out of it so it can update the drag image.
+  IDropTargetHelper* drop_helper = DropHelper();
+  if (drop_helper)
+    drop_helper->DragLeave();
+
+  OnDragLeave(current_data_object_);
+
+  current_data_object_ = NULL;
+  return S_OK;
+}
+
+HRESULT BaseDropTarget::Drop(IDataObject* data_object,
+                             DWORD key_state,
+                             POINTL cursor_position,
+                             DWORD* effect) {
+  // Tell the helper that we dropped onto it so it can update the drag image.
+  IDropTargetHelper* drop_helper = DropHelper();
+  if (drop_helper) {
+    drop_helper->Drop(current_data_object_,
+                      reinterpret_cast<POINT*>(&cursor_position), *effect);
+  }
+
+  if (suspend_) {
+    *effect = DROPEFFECT_NONE;
+    return S_OK;
+  }
+
+  POINT screen_pt = { cursor_position.x, cursor_position.y };
+  *effect = OnDrop(current_data_object_, key_state, screen_pt, *effect);
+  return S_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// BaseDropTarget, IUnknown implementation:
+
+HRESULT BaseDropTarget::QueryInterface(const IID& iid, void** object) {
+  *object = NULL;
+  if (IsEqualIID(iid, IID_IUnknown) || IsEqualIID(iid, IID_IDropTarget)) {
+    *object = this;
+  } else {
+    return E_NOINTERFACE;
+  }
+  AddRef();
+  return S_OK;
+}
+
+ULONG BaseDropTarget::AddRef() {
+  return InterlockedIncrement(&ref_count_);
+}
+
+ULONG BaseDropTarget::Release() {
+  if (InterlockedDecrement(&ref_count_) == 0) {
+    ULONG copied_refcnt = ref_count_;
+    delete this;
+    return copied_refcnt;
+  }
+  return ref_count_;
+}
+
+DWORD BaseDropTarget::OnDragEnter(IDataObject* data_object,
+                                  DWORD key_state,
+                                  POINT cursor_position,
+                                  DWORD effect) {
+  return DROPEFFECT_NONE;
+}
+
+DWORD BaseDropTarget::OnDragOver(IDataObject* data_object,
+                                 DWORD key_state,
+                                 POINT cursor_position,
+                                 DWORD effect) {
+  return DROPEFFECT_NONE;
+}
+
+void BaseDropTarget::OnDragLeave(IDataObject* data_object) {
+}
+
+DWORD BaseDropTarget::OnDrop(IDataObject* data_object,
+                             DWORD key_state,
+                             POINT cursor_position,
+                             DWORD effect) {
+  return DROPEFFECT_NONE;
+}
diff --git a/base/base_drop_target.h b/base/base_drop_target.h
new file mode 100644
index 0000000..24e8ee5
--- /dev/null
+++ b/base/base_drop_target.h
@@ -0,0 +1,142 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_BASE_DROP_TARGET_H__
+#define BASE_BASE_DROP_TARGET_H__
+
+#include <atlbase.h>
+#include <objidl.h>
+#include <shobjidl.h>
+
+#include "base/basictypes.h"
+
+// A DropTarget implementation that takes care of the nitty gritty
+// of dnd. While this class is concrete, subclasses will most likely
+// want to override various OnXXX methods.
+//
+// Because BaseDropTarget is ref counted you shouldn't delete it directly,
+// rather wrap it in a scoped_refptr. Be sure and invoke RevokeDragDrop(m_hWnd)
+// before the HWND is deleted too.
+class BaseDropTarget : public IDropTarget {
+ public:
+  // Create a new BaseDropTarget associating it with the given HWND.
+  explicit BaseDropTarget(HWND hwnd);
+  virtual ~BaseDropTarget();
+
+  // When suspend is set to |true|, the drop target does not receive drops from
+  // drags initiated within the owning HWND.
+  // TODO(beng): (http://b/1085385) figure out how we will handle legitimate
+  //             drag-drop operations within the same HWND, such as dragging
+  //             selected text to an edit field.
+  void set_suspend(bool suspend) { suspend_ = suspend; }
+
+  // IDropTarget implementation:
+  HRESULT __stdcall DragEnter(IDataObject* data_object,
+                              DWORD key_state,
+                              POINTL cursor_position,
+                              DWORD* effect);
+  HRESULT __stdcall DragOver(DWORD key_state,
+                             POINTL cursor_position,
+                             DWORD* effect);
+  HRESULT __stdcall DragLeave();
+  HRESULT __stdcall Drop(IDataObject* data_object,
+                         DWORD key_state,
+                         POINTL cursor_position,
+                         DWORD* effect);
+
+  // IUnknown implementation:
+  HRESULT __stdcall QueryInterface(const IID& iid, void** object);
+  ULONG __stdcall AddRef();
+  ULONG __stdcall Release();
+
+ protected:
+  // Returns the hosting HWND.
+  HWND GetHWND() { return hwnd_; }
+
+  // Invoked when the cursor first moves over the hwnd during a dnd session.
+  // This should return a bitmask of the supported drop operations:
+  // DROPEFFECT_NONE, DROPEFFECT_COPY, DROPEFFECT_LINK and/or
+  // DROPEFFECT_MOVE.
+  virtual DWORD OnDragEnter(IDataObject* data_object,
+                            DWORD key_state,
+                            POINT cursor_position,
+                            DWORD effect);
+
+  // Invoked when the cursor moves over the window during a dnd session.
+  // This should return a bitmask of the supported drop operations:
+  // DROPEFFECT_NONE, DROPEFFECT_COPY, DROPEFFECT_LINK and/or
+  // DROPEFFECT_MOVE.
+  virtual DWORD OnDragOver(IDataObject* data_object,
+                           DWORD key_state,
+                           POINT cursor_position,
+                           DWORD effect);
+
+  // Invoked when the cursor moves outside the bounds of the hwnd during a
+  // dnd session.
+  virtual void OnDragLeave(IDataObject* data_object);
+
+  // Invoked when the drop ends on the window. This should return the operation
+  // that was taken.
+  virtual DWORD OnDrop(IDataObject* data_object,
+                       DWORD key_state,
+                       POINT cursor_position,
+                       DWORD effect);
+
+ private:
+  // Returns the cached drop helper, creating one if necessary. The returned
+  // object is not addrefed. May return NULL if the object couldn't be created.
+  static IDropTargetHelper* DropHelper();
+
+  // The data object currently being dragged over this drop target.
+  CComPtr<IDataObject> current_data_object_;
+
+  // A helper object that is used to provide drag image support while the mouse
+  // is dragging over the content area.
+  //
+  // DO NOT ACCESS DIRECTLY! Use DropHelper() instead, which will lazily create
+  // this if it doesn't exist yet. This object can take tens of milliseconds to
+  // create, and we don't want to block any window opening for this, especially
+  // since often, DnD will never be used. Instead, we force this penalty to the
+  // first time it is actually used.
+  static IDropTargetHelper* cached_drop_target_helper_;
+
+  // The HWND of the source. This HWND is used to determine coordinates for
+  // mouse events that are sent to the renderer notifying various drag states.
+  HWND hwnd_;
+
+  // Whether or not we are currently processing drag notifications for drags
+  // initiated in this window.
+  bool suspend_;
+
+  LONG ref_count_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(BaseDropTarget);
+};
+
+#endif  // BASE_BASE_DROP_TARGET_H__
diff --git a/base/base_paths.cc b/base/base_paths.cc
new file mode 100644
index 0000000..389b0bb
--- /dev/null
+++ b/base/base_paths.cc
@@ -0,0 +1,151 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/base_paths.h"
+
+#include <shlobj.h>
+
+#include "base/file_util.h"
+#include "base/path_service.h"
+
+// This is here for the sole purpose of looking up the corresponding HMODULE.
+static int handle_lookup = 0;
+
+namespace base {
+
+bool PathProvider(int key, std::wstring* result) {
+  // NOTE: DIR_CURRENT is a special cased in PathService::Get
+
+  // We need to go compute the value. It would be nice to support paths with
+  // names longer than MAX_PATH, but the system functions don't seem to be
+  // designed for it either, with the exception of GetTempPath (but other
+  // things will surely break if the temp path is too long, so we don't bother
+  // handling it.
+  wchar_t system_buffer[MAX_PATH];
+  system_buffer[0] = 0;
+
+  std::wstring cur;
+  switch (key) {
+    case base::FILE_EXE:
+      GetModuleFileName(NULL, system_buffer, MAX_PATH);
+      cur = system_buffer;
+      break;
+    case base::FILE_MODULE: {
+      // the resource containing module is assumed to be the one that
+      // this code lives in, whether that's a dll or exe
+      MEMORY_BASIC_INFORMATION info = { 0 };
+      VirtualQuery(reinterpret_cast<void*>(&handle_lookup),
+                   &info, sizeof(info));
+      // Module handles are just the allocation base address of the module.
+      HMODULE this_module = reinterpret_cast<HMODULE>(info.AllocationBase);
+      GetModuleFileName(this_module, system_buffer, MAX_PATH);
+      cur = system_buffer;
+      break;
+    }
+    case base::DIR_EXE:
+      PathProvider(base::FILE_EXE, &cur);
+      file_util::TrimFilename(&cur);
+      break;
+    case base::DIR_MODULE:
+      PathProvider(base::FILE_MODULE, &cur);
+      file_util::TrimFilename(&cur);
+      break;
+    case base::DIR_TEMP:
+      if (!file_util::GetTempDir(&cur))
+        return false;
+      break;
+    case base::DIR_WINDOWS:
+      GetWindowsDirectory(system_buffer, MAX_PATH);
+      cur = system_buffer;
+      break;
+    case base::DIR_SYSTEM:
+      GetSystemDirectory(system_buffer, MAX_PATH);
+      cur = system_buffer;
+      break;
+    case base::DIR_PROGRAM_FILES:
+      if (FAILED(SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES, NULL,
+                                 SHGFP_TYPE_CURRENT, system_buffer)))
+        return false;
+      cur = system_buffer;
+      break;
+    case base::DIR_SOURCE_ROOT:
+      // By default, unit tests execute two levels deep from the source root.
+      // For example:  chrome/{Debug|Release}/ui_tests.exe
+      PathProvider(base::DIR_EXE, &cur);
+      file_util::UpOneDirectory(&cur);
+      file_util::UpOneDirectory(&cur);
+      break;
+    case base::DIR_APP_DATA:
+      if (FAILED(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT,
+                                 system_buffer)))
+        return false;
+      cur = system_buffer;
+      break;
+    case base::DIR_LOCAL_APP_DATA_LOW:
+      // TODO(nsylvain): We should use SHGetKnownFolderPath instead. Bug 1281128
+      if (FAILED(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT,
+                                 system_buffer)))
+        return false;
+      cur = system_buffer;
+      file_util::UpOneDirectory(&cur);
+      file_util::AppendToPath(&cur, L"LocalLow");
+      break;
+    case base::DIR_LOCAL_APP_DATA:
+      if (FAILED(SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL,
+                                 SHGFP_TYPE_CURRENT, system_buffer)))
+        return false;
+      cur = system_buffer;
+      break;
+    case base::DIR_IE_INTERNET_CACHE:
+      if (FAILED(SHGetFolderPath(NULL, CSIDL_INTERNET_CACHE, NULL,
+                                 SHGFP_TYPE_CURRENT, system_buffer)))
+        return false;
+      cur = system_buffer;
+      break;
+    case base::DIR_COMMON_START_MENU:
+      if (FAILED(SHGetFolderPath(NULL, CSIDL_COMMON_PROGRAMS, NULL,
+                                 SHGFP_TYPE_CURRENT, system_buffer)))
+        return false;
+      cur = system_buffer;
+      break;
+    case base::DIR_START_MENU:
+      if (FAILED(SHGetFolderPath(NULL, CSIDL_PROGRAMS, NULL,
+                                 SHGFP_TYPE_CURRENT, system_buffer)))
+        return false;
+      cur = system_buffer;
+      break;
+    default:
+      return false;
+  }
+
+  result->swap(cur);
+  return true;
+}
+
+}  // namespace base
diff --git a/base/base_paths.h b/base/base_paths.h
new file mode 100644
index 0000000..60a59ca
--- /dev/null
+++ b/base/base_paths.h
@@ -0,0 +1,70 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_BASE_PATHS_H__
+#define BASE_BASE_PATHS_H__
+
+// This file declares path keys for the base module.  These can be used with
+// the PathService to access various special directories and files.
+
+namespace base {
+
+enum {
+  PATH_START = 0,
+
+  DIR_CURRENT,  // current directory
+  DIR_EXE,      // directory containing FILE_EXE
+  DIR_MODULE,   // directory containing FILE_MODULE
+  FILE_EXE,     // path and filename of the current executable
+  FILE_MODULE,  // path and filename of the module containing the code for the
+                // PathService (which could differ from FILE_EXE if the
+                // PathService were compiled into a DLL, for example)
+  DIR_TEMP,     // temporary directory
+  DIR_WINDOWS,  // Windows directory, usually "c:\windows"
+  DIR_SYSTEM,   // Usually c:\windows\system32"
+  DIR_PROGRAM_FILES, // Usually c:\program files
+
+  DIR_SOURCE_ROOT,  // Returns the root of the source tree.  This key is useful
+                    // for tests that need to locate various resources.  It
+                    // should not be used outside of test code.
+  DIR_APP_DATA,  // Application Data directory under the user profile.
+  DIR_LOCAL_APP_DATA_LOW,  // Local AppData directory for low integrity level.
+  DIR_LOCAL_APP_DATA,  // "Local Settings\Application Data" directory under the
+                       // user profile.
+  DIR_IE_INTERNET_CACHE,  // Temporary Internet Files directory.
+  DIR_COMMON_START_MENU,  // Usually "C:\Documents and Settings\All Users\
+                          // Start Menu\Programs"
+  DIR_START_MENU,         // Usually "C:\Documents and Settings\<user>\
+                          // Start Menu\Programs"
+  PATH_END
+};
+
+}  // namespace base
+
+#endif  // BASE_BASE_PATHS_H__
diff --git a/base/base_switches.cc b/base/base_switches.cc
new file mode 100644
index 0000000..8a587da
--- /dev/null
+++ b/base/base_switches.cc
@@ -0,0 +1,58 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/base_switches.h"
+
+namespace switches {
+
+// If the program includes chrome/common/debug_on_start.h, the process will
+// start the JIT system-registered debugger on itself and will wait for 60
+// seconds for the debugger to attach to itself. Then a break point will be hit.
+const wchar_t kDebugOnStart[]                  = L"debug-on-start";
+
+// Will wait for 60 seconds for a debugger to come to attach to the process.
+const wchar_t kWaitForDebugger[]               = L"wait-for-debugger";
+
+// Suppresses all error dialogs when present.
+const wchar_t kNoErrorDialogs[]                = L"noerrdialogs";
+
+// Disables the crash reporting.
+const wchar_t kDisableBreakpad[]               = L"disable-breakpad";
+
+// Generates full memory crash dump.
+const wchar_t kFullMemoryCrashReport[]         = L"full-memory-crash-report";
+
+// The value of this switch determines whether the process is started as a
+// renderer or plugin host.  If it's empty, it's the browser.
+const wchar_t kProcessType[]                   = L"type";
+
+// Enable DCHECKs in release mode.
+const wchar_t kEnableDCHECK[]                  = L"enable-dcheck";
+
+}  // namespace switches
diff --git a/base/base_switches.h b/base/base_switches.h
new file mode 100644
index 0000000..bf64a56
--- /dev/null
+++ b/base/base_switches.h
@@ -0,0 +1,47 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Defines all the "base" command-line switches.
+
+#ifndef BASE_SWITCHES_H__
+#define BASE_SWITCHES_H__
+
+namespace switches {
+
+extern const wchar_t kDebugOnStart[];
+extern const wchar_t kWaitForDebugger[];
+extern const wchar_t kDisableBreakpad[];
+extern const wchar_t kFullMemoryCrashReport[];
+extern const wchar_t kNoErrorDialogs[];
+extern const wchar_t kProcessType[];
+extern const wchar_t kEnableDCHECK[];
+
+}  // namespace switches
+
+#endif  // CHROME_COMMON_CHROME_SWITCHES_H__
diff --git a/base/basictypes.h b/base/basictypes.h
new file mode 100644
index 0000000..174fb3e
--- /dev/null
+++ b/base/basictypes.h
@@ -0,0 +1,403 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Copied from base/basictypes.h with some modifications
+
+#ifndef BASE_BASICTYPES_H__
+#define BASE_BASICTYPES_H__
+
+#include <assert.h>         // for use with down_cast<>
+#include <limits.h>         // So we can set the bounds of our types
+#include <stddef.h>         // For size_t
+#include <string.h>         // for memcpy
+
+#include "base/port.h"    // Types that only need exist on certain systems
+
+typedef signed char         schar;
+typedef signed char         int8;
+typedef short               int16;
+// TODO(mbelshe) Remove these type guards.  These are
+//               temporary to avoid conflicts with npapi.h.
+#ifndef _INT32
+#define _INT32
+typedef int                 int32;
+#endif
+typedef long long           int64;
+
+// NOTE: unsigned types are DANGEROUS in loops and other arithmetical
+// places.  Use the signed types unless your variable represents a bit
+// pattern (eg a hash value) or you really need the extra bit.  Do NOT
+// use 'unsigned' to express "this value should always be positive";
+// use assertions for this.
+
+typedef unsigned char      uint8;
+typedef unsigned short     uint16;
+// TODO(mbelshe) Remove these type guards.  These are
+//               temporary to avoid conflicts with npapi.h.
+#ifndef _UINT32
+#define _UINT32
+typedef unsigned int       uint32;
+#endif
+typedef unsigned long long uint64;
+
+// A type to represent a Unicode code-point value. As of Unicode 4.0,
+// such values require up to 21 bits.
+// (For type-checking on pointers, make this explicitly signed,
+// and it should always be the signed version of whatever int32 is.)
+typedef signed int         char32;
+
+const uint8  kuint8max  = UCHAR_MAX;
+const uint16 kuint16max = USHRT_MAX;
+const uint32 kuint32max = UINT_MAX;
+const uint64 kuint64max = ULLONG_MAX;
+const  int8  kint8min   = SCHAR_MIN;
+const  int8  kint8max   = SCHAR_MAX;
+const  int16 kint16min  = SHRT_MIN;
+const  int16 kint16max  = SHRT_MAX;
+const  int32 kint32min  = INT_MIN;
+const  int32 kint32max  = INT_MAX;
+const  int64 kint64min  = LLONG_MIN;
+const  int64 kint64max  = LLONG_MAX;
+
+// id for odp categories
+typedef uint32 CatId;
+const CatId kIllegalCatId = static_cast<CatId>(0);
+
+typedef uint32 TermId;
+const TermId kIllegalTermId = static_cast<TermId>(0);
+
+typedef uint32 HostId;
+const HostId kIllegalHostId = static_cast<HostId>(0);
+
+typedef uint32 DomainId;
+const DomainId kIllegalDomainId = static_cast<DomainId>(0);
+
+// A macro to disallow the copy constructor and operator= functions
+// This should be used in the private: declarations for a class
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+  TypeName(const TypeName&);               \
+  void operator=(const TypeName&)
+
+// An older, deprecated, politically incorrect name for the above.
+#define DISALLOW_EVIL_CONSTRUCTORS(TypeName) DISALLOW_COPY_AND_ASSIGN(TypeName)
+
+// A macro to disallow all the implicit constructors, namely the
+// default constructor, copy constructor and operator= functions.
+//
+// This should be used in the private: declarations for a class
+// that wants to prevent anyone from instantiating it. This is
+// especially useful for classes containing only static methods.
+#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
+  TypeName();                                    \
+  DISALLOW_COPY_AND_ASSIGN(TypeName)
+
+// The arraysize(arr) macro returns the # of elements in an array arr.
+// The expression is a compile-time constant, and therefore can be
+// used in defining new arrays, for example.  If you use arraysize on
+// a pointer by mistake, you will get a compile-time error.
+//
+// One caveat is that arraysize() doesn't accept any array of an
+// anonymous type or a type defined inside a function.  In these rare
+// cases, you have to use the unsafe ARRAYSIZE() macro below.  This is
+// due to a limitation in C++'s template system.  The limitation might
+// eventually be removed, but it hasn't happened yet.
+
+// This template function declaration is used in defining arraysize.
+// Note that the function doesn't need an implementation, as we only
+// use its type.
+template <typename T, size_t N>
+char (&ArraySizeHelper(T (&array)[N]))[N];
+
+// That gcc wants both of these prototypes seems mysterious. VC, for
+// its part, can't decide which to use (another mystery). Matching of
+// template overloads: the final frontier.
+#ifndef _MSC_VER
+template <typename T, size_t N>
+char (&ArraySizeHelper(const T (&array)[N]))[N];
+#endif
+
+#define arraysize(array) (sizeof(ArraySizeHelper(array)))
+
+// ARRAYSIZE performs essentially the same calculation as arraysize,
+// but can be used on anonymous types or types defined inside
+// functions.  It's less safe than arraysize as it accepts some
+// (although not all) pointers.  Therefore, you should use arraysize
+// whenever possible.
+//
+// The expression ARRAYSIZE(a) is a compile-time constant of type
+// size_t.
+//
+// ARRAYSIZE catches a few type errors.  If you see a compiler error
+//
+//   "warning: division by zero in ..."
+//
+// when using ARRAYSIZE, you are (wrongfully) giving it a pointer.
+// You should only use ARRAYSIZE on statically allocated arrays.
+//
+// The following comments are on the implementation details, and can
+// be ignored by the users.
+//
+// ARRAYSIZE(arr) works by inspecting sizeof(arr) (the # of bytes in
+// the array) and sizeof(*(arr)) (the # of bytes in one array
+// element).  If the former is divisible by the latter, perhaps arr is
+// indeed an array, in which case the division result is the # of
+// elements in the array.  Otherwise, arr cannot possibly be an array,
+// and we generate a compiler error to prevent the code from
+// compiling.
+//
+// Since the size of bool is implementation-defined, we need to cast
+// !(sizeof(a) & sizeof(*(a))) to size_t in order to ensure the final
+// result has type size_t.
+//
+// This macro is not perfect as it wrongfully accepts certain
+// pointers, namely where the pointer size is divisible by the pointee
+// size.  Since all our code has to go through a 32-bit compiler,
+// where a pointer is 4 bytes, this means all pointers to a type whose
+// size is 3 or greater than 4 will be (righteously) rejected.
+
+#define ARRAYSIZE_UNSAFE(a) \
+  ((sizeof(a) / sizeof(*(a))) / \
+   static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
+
+
+// Use implicit_cast as a safe version of static_cast or const_cast
+// for upcasting in the type hierarchy (i.e. casting a pointer to Foo
+// to a pointer to SuperclassOfFoo or casting a pointer to Foo to
+// a const pointer to Foo).
+// When you use implicit_cast, the compiler checks that the cast is safe.
+// Such explicit implicit_casts are necessary in surprisingly many
+// situations where C++ demands an exact type match instead of an
+// argument type convertable to a target type.
+//
+// The From type can be inferred, so the preferred syntax for using
+// implicit_cast is the same as for static_cast etc.:
+//
+//   implicit_cast<ToType>(expr)
+//
+// implicit_cast would have been part of the C++ standard library,
+// but the proposal was submitted too late.  It will probably make
+// its way into the language in the future.
+template<typename To, typename From>
+inline To implicit_cast(From const &f) {
+  return f;
+}
+
+
+// When you upcast (that is, cast a pointer from type Foo to type
+// SuperclassOfFoo), it's fine to use implicit_cast<>, since upcasts
+// always succeed.  When you downcast (that is, cast a pointer from
+// type Foo to type SubclassOfFoo), static_cast<> isn't safe, because
+// how do you know the pointer is really of type SubclassOfFoo?  It
+// could be a bare Foo, or of type DifferentSubclassOfFoo.  Thus,
+// when you downcast, you should use this macro.  In debug mode, we
+// use dynamic_cast<> to double-check the downcast is legal (we die
+// if it's not).  In normal mode, we do the efficient static_cast<>
+// instead.  Thus, it's important to test in debug mode to make sure
+// the cast is legal!
+//    This is the only place in the code we should use dynamic_cast<>.
+// In particular, you SHOULDN'T be using dynamic_cast<> in order to
+// do RTTI (eg code like this:
+//    if (dynamic_cast<Subclass1>(foo)) HandleASubclass1Object(foo);
+//    if (dynamic_cast<Subclass2>(foo)) HandleASubclass2Object(foo);
+// You should design the code some other way not to need this.
+
+template<typename To, typename From>     // use like this: down_cast<T*>(foo);
+inline To down_cast(From* f) {                   // so we only accept pointers
+  // Ensures that To is a sub-type of From *.  This test is here only
+  // for compile-time type checking, and has no overhead in an
+  // optimized build at run-time, as it will be optimized away
+  // completely.
+  if (false) {
+    implicit_cast<From*, To>(0);
+  }
+
+  assert(f == NULL || dynamic_cast<To>(f) != NULL);  // RTTI: debug mode only!
+  return static_cast<To>(f);
+}
+
+// The COMPILE_ASSERT macro can be used to verify that a compile time
+// expression is true. For example, you could use it to verify the
+// size of a static array:
+//
+//   COMPILE_ASSERT(ARRAYSIZE(content_type_names) == CONTENT_NUM_TYPES,
+//                  content_type_names_incorrect_size);
+//
+// or to make sure a struct is smaller than a certain size:
+//
+//   COMPILE_ASSERT(sizeof(foo) < 128, foo_too_large);
+//
+// The second argument to the macro is the name of the variable. If
+// the expression is false, most compilers will issue a warning/error
+// containing the name of the variable.
+
+template <bool>
+struct CompileAssert {
+};
+
+#undef COMPILE_ASSERT
+#define COMPILE_ASSERT(expr, msg) \
+  typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1]
+
+// Implementation details of COMPILE_ASSERT:
+//
+// - COMPILE_ASSERT works by defining an array type that has -1
+//   elements (and thus is invalid) when the expression is false.
+//
+// - The simpler definition
+//
+//     #define COMPILE_ASSERT(expr, msg) typedef char msg[(expr) ? 1 : -1]
+//
+//   does not work, as gcc supports variable-length arrays whose sizes
+//   are determined at run-time (this is gcc's extension and not part
+//   of the C++ standard).  As a result, gcc fails to reject the
+//   following code with the simple definition:
+//
+//     int foo;
+//     COMPILE_ASSERT(foo, msg); // not supposed to compile as foo is
+//                               // not a compile-time constant.
+//
+// - By using the type CompileAssert<(bool(expr))>, we ensures that
+//   expr is a compile-time constant.  (Template arguments must be
+//   determined at compile-time.)
+//
+// - The outter parentheses in CompileAssert<(bool(expr))> are necessary
+//   to work around a bug in gcc 3.4.4 and 4.0.1.  If we had written
+//
+//     CompileAssert<bool(expr)>
+//
+//   instead, these compilers will refuse to compile
+//
+//     COMPILE_ASSERT(5 > 0, some_message);
+//
+//   (They seem to think the ">" in "5 > 0" marks the end of the
+//   template argument list.)
+//
+// - The array size is (bool(expr) ? 1 : -1), instead of simply
+//
+//     ((expr) ? 1 : -1).
+//
+//   This is to avoid running into a bug in MS VC 7.1, which
+//   causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1.
+
+
+// MetatagId refers to metatag-id that we assign to
+// each metatag <name, value> pair..
+typedef uint32 MetatagId;
+
+// Argument type used in interfaces that can optionally take ownership
+// of a passed in argument.  If TAKE_OWNERSHIP is passed, the called
+// object takes ownership of the argument.  Otherwise it does not.
+enum Ownership {
+  DO_NOT_TAKE_OWNERSHIP,
+  TAKE_OWNERSHIP
+};
+
+// Use these as the mlock_bytes parameter to MLock and MLockGeneral
+enum { MLOCK_ALL = -1, MLOCK_NONE = 0 };
+
+// Helper routine to avoid buggy code like the following:
+//      if (pos + N < end) ...
+// If pos is large enough, "pos + N" may overflow.  For example,
+// pos==0xfffff000 and N==1MB.
+//
+// This often happens on Nacona's in 32-bit mode, because the
+// main thread's stack is put very close to address 0xffffffff.
+//
+// PointerRangeSize(a,b) returns the size of the range [a,b-1]
+inline size_t PointerRangeSize(const char* start, const char* end) {
+  assert(start <= end);
+  return end - start;
+}
+
+// bit_cast<Dest,Source> is a template function that implements the
+// equivalent of "*reinterpret_cast<Dest*>(&source)".  We need this in
+// very low-level functions like the protobuf library and fast math
+// support.
+//
+//   float f = 3.14159265358979;
+//   int i = bit_cast<int32>(f);
+//   // i = 0x40490fdb
+//
+// The classical address-casting method is:
+//
+//   // WRONG
+//   float f = 3.14159265358979;            // WRONG
+//   int i = * reinterpret_cast<int*>(&f);  // WRONG
+//
+// The address-casting method actually produces undefined behavior
+// according to ISO C++ specification section 3.10 -15 -.  Roughly, this
+// section says: if an object in memory has one type, and a program
+// accesses it with a different type, then the result is undefined
+// behavior for most values of "different type".
+//
+// This is true for any cast syntax, either *(int*)&f or
+// *reinterpret_cast<int*>(&f).  And it is particularly true for
+// conversions betweeen integral lvalues and floating-point lvalues.
+//
+// The purpose of 3.10 -15- is to allow optimizing compilers to assume
+// that expressions with different types refer to different memory.  gcc
+// 4.0.1 has an optimizer that takes advantage of this.  So a
+// non-conforming program quietly produces wildly incorrect output.
+//
+// The problem is not the use of reinterpret_cast.  The problem is type
+// punning: holding an object in memory of one type and reading its bits
+// back using a different type.
+//
+// The C++ standard is more subtle and complex than this, but that
+// is the basic idea.
+//
+// Anyways ...
+//
+// bit_cast<> calls memcpy() which is blessed by the standard,
+// especially by the example in section 3.9 .  Also, of course,
+// bit_cast<> wraps up the nasty logic in one place.
+//
+// Fortunately memcpy() is very fast.  In optimized mode, with a
+// constant size, gcc 2.95.3, gcc 4.0.1, and msvc 7.1 produce inline
+// code with the minimal amount of data movement.  On a 32-bit system,
+// memcpy(d,s,4) compiles to one load and one store, and memcpy(d,s,8)
+// compiles to two loads and two stores.
+//
+// I tested this code with gcc 2.95.3, gcc 4.0.1, icc 8.1, and msvc 7.1.
+//
+// WARNING: if Dest or Source is a non-POD type, the result of the memcpy
+// is likely to surprise you.
+
+template <class Dest, class Source>
+inline Dest bit_cast(const Source& source) {
+  // Compile time assertion: sizeof(Dest) == sizeof(Source)
+  // A compile error here means your Dest and Source have different sizes.
+  typedef char VerifySizesAreEqual [sizeof(Dest) == sizeof(Source) ? 1 : -1];
+
+  Dest dest;
+  memcpy(&dest, &source, sizeof(dest));
+  return dest;
+}
+
+
+#endif  // BASE_BASICTYPES_H__
diff --git a/base/build/base.vcproj b/base/build/base.vcproj
new file mode 100644
index 0000000..0847bbc
--- /dev/null
+++ b/base/build/base.vcproj
@@ -0,0 +1,719 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="8.00"
+	Name="base"
+	ProjectGUID="{1832A374-8A74-4F9E-B536-69A699B3E165}"
+	RootNamespace="base"
+	Keyword="Win32Proj"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			ConfigurationType="4"
+			InheritedPropertySheets="$(SolutionDir)..\build\debug.vsprops;.\base.vsprops"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			ConfigurationType="4"
+			InheritedPropertySheets="$(SolutionDir)..\build\release.vsprops;.\base.vsprops"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<File
+			RelativePath="..\atomic.h"
+			>
+		</File>
+		<File
+			RelativePath="..\base_drag_source.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\base_drag_source.h"
+			>
+		</File>
+		<File
+			RelativePath="..\base_drop_target.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\base_drop_target.h"
+			>
+		</File>
+		<File
+			RelativePath="..\base_paths.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\base_paths.h"
+			>
+		</File>
+		<File
+			RelativePath="..\base_switches.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\base_switches.h"
+			>
+		</File>
+		<File
+			RelativePath="..\basictypes.h"
+			>
+		</File>
+		<File
+			RelativePath="..\third_party\nss\blapi.h"
+			>
+		</File>
+		<File
+			RelativePath="..\third_party\nss\blapit.h"
+			>
+		</File>
+		<File
+			RelativePath="..\clipboard.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\clipboard.h"
+			>
+		</File>
+		<File
+			RelativePath="..\clipboard_util.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\clipboard_util.h"
+			>
+		</File>
+		<File
+			RelativePath="..\command_line.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\command_line.h"
+			>
+		</File>
+		<File
+			RelativePath="..\condition_variable.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\condition_variable.h"
+			>
+		</File>
+		<File
+			RelativePath="..\debug_on_start.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\debug_on_start.h"
+			>
+		</File>
+		<File
+			RelativePath="..\debug_util.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\debug_util.h"
+			>
+		</File>
+		<File
+			RelativePath="..\event_recorder.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\event_recorder.h"
+			>
+		</File>
+		<File
+			RelativePath="..\file_util.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\file_util.h"
+			>
+		</File>
+		<File
+			RelativePath="..\file_version_info.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\file_version_info.h"
+			>
+		</File>
+		<File
+			RelativePath="..\fix_wp64.h"
+			>
+		</File>
+		<File
+			RelativePath="..\fixed_string.h"
+			>
+		</File>
+		<File
+			RelativePath="..\histogram.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\histogram.h"
+			>
+		</File>
+		<File
+			RelativePath="..\hmac.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\hmac.h"
+			>
+		</File>
+		<File
+			RelativePath="..\iat_patch.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\iat_patch.h"
+			>
+		</File>
+		<File
+			RelativePath="..\icu_util.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\icu_util.h"
+			>
+		</File>
+		<File
+			RelativePath="..\id_map.h"
+			>
+		</File>
+		<File
+			RelativePath="..\idle_timer.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\idle_timer.h"
+			>
+		</File>
+		<File
+			RelativePath="..\image_util.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\image_util.h"
+			>
+		</File>
+		<File
+			RelativePath="..\json_reader.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\json_reader.h"
+			>
+		</File>
+		<File
+			RelativePath="..\json_writer.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\json_writer.h"
+			>
+		</File>
+		<File
+			RelativePath="..\linked_ptr.h"
+			>
+		</File>
+		<File
+			RelativePath="..\lock.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\lock.h"
+			>
+		</File>
+		<File
+			RelativePath="..\lock_impl.h"
+			>
+		</File>
+		<File
+			RelativePath="..\lock_impl_win.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\logging.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\logging.h"
+			>
+		</File>
+		<File
+			RelativePath="..\md5.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\md5.h"
+			>
+		</File>
+		<File
+			RelativePath="..\memory_debug.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\memory_debug.h"
+			>
+		</File>
+		<File
+			RelativePath="..\message_loop.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\message_loop.h"
+			>
+		</File>
+		<File
+			RelativePath="..\non_thread_safe.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\non_thread_safe.h"
+			>
+		</File>
+		<File
+			RelativePath="..\observer_list.h"
+			>
+		</File>
+		<File
+			RelativePath="..\path_service.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\path_service.h"
+			>
+		</File>
+		<File
+			RelativePath="..\pe_image.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\pe_image.h"
+			>
+		</File>
+		<File
+			RelativePath="..\pickle.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\pickle.h"
+			>
+		</File>
+		<File
+			RelativePath="..\platform_thread.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\platform_thread.h"
+			>
+		</File>
+		<File
+			RelativePath="..\port.h"
+			>
+		</File>
+		<File
+			RelativePath="..\third_party\nspr\prcpucfg.h"
+			>
+		</File>
+		<File
+			RelativePath="..\third_party\nspr\prcpucfg_win.h"
+			>
+		</File>
+		<File
+			RelativePath="..\process.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\process.h"
+			>
+		</File>
+		<File
+			RelativePath="..\process_util.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\process_util.h"
+			>
+		</File>
+		<File
+			RelativePath="..\third_party\nspr\prtime.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\third_party\nspr\prtime.h"
+			>
+		</File>
+		<File
+			RelativePath="..\third_party\nspr\prtypes.h"
+			>
+		</File>
+		<File
+			RelativePath="..\third_party\purify\pure.h"
+			>
+		</File>
+		<File
+			RelativePath="..\third_party\purify\pure_api.c"
+			>
+		</File>
+		<File
+			RelativePath="..\ref_counted.h"
+			>
+		</File>
+		<File
+			RelativePath="..\registry.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\registry.h"
+			>
+		</File>
+		<File
+			RelativePath="..\resource_util.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\resource_util.h"
+			>
+		</File>
+		<File
+			RelativePath="..\revocable_store.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\revocable_store.h"
+			>
+		</File>
+		<File
+			RelativePath="..\scoped_handle.h"
+			>
+		</File>
+		<File
+			RelativePath="..\scoped_ptr.h"
+			>
+		</File>
+		<File
+			RelativePath="..\sha2.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\sha2.h"
+			>
+		</File>
+		<File
+			RelativePath="..\third_party\nss\sha256.h"
+			>
+		</File>
+		<File
+			RelativePath="..\third_party\nss\sha512.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\shared_event.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\shared_event.h"
+			>
+		</File>
+		<File
+			RelativePath="..\shared_memory.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\shared_memory.h"
+			>
+		</File>
+		<File
+			RelativePath="..\singleton.h"
+			>
+		</File>
+		<File
+			RelativePath="..\singleton_internal.h"
+			>
+		</File>
+		<File
+			RelativePath="..\spin_wait.h"
+			>
+		</File>
+		<File
+			RelativePath="..\stack_container.h"
+			>
+		</File>
+		<File
+			RelativePath="..\stats_counters.h"
+			>
+		</File>
+		<File
+			RelativePath="..\stats_table.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\stats_table.h"
+			>
+		</File>
+		<File
+			RelativePath="..\string16.h"
+			>
+		</File>
+		<File
+			RelativePath="..\string_escape.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\string_escape.h"
+			>
+		</File>
+		<File
+			RelativePath="..\string_piece.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\string_piece.h"
+			>
+		</File>
+		<File
+			RelativePath="..\string_tokenizer.h"
+			>
+		</File>
+		<File
+			RelativePath="..\string_util.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\string_util.h"
+			>
+		</File>
+		<File
+			RelativePath="..\string_util_icu.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\string_util_win.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\string_util_win.h"
+			>
+		</File>
+		<File
+			RelativePath="..\task.h"
+			>
+		</File>
+		<File
+			RelativePath="..\thread.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\thread.h"
+			>
+		</File>
+		<File
+			RelativePath="..\thread_local_storage.h"
+			>
+		</File>
+		<File
+			RelativePath="..\thread_local_storage_win.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\time.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\time.h"
+			>
+		</File>
+		<File
+			RelativePath="..\time_win.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\timer.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\timer.h"
+			>
+		</File>
+		<File
+			RelativePath="..\tracked.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\tracked.h"
+			>
+		</File>
+		<File
+			RelativePath="..\tracked_objects.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\tracked_objects.h"
+			>
+		</File>
+		<File
+			RelativePath="..\tuple.h"
+			>
+		</File>
+		<File
+			RelativePath="..\values.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\values.h"
+			>
+		</File>
+		<File
+			RelativePath="..\watchdog.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\watchdog.h"
+			>
+		</File>
+		<File
+			RelativePath="..\win_util.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\win_util.h"
+			>
+		</File>
+		<File
+			RelativePath="..\windows_message_list.h"
+			>
+		</File>
+		<File
+			RelativePath="..\wmi_util.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\wmi_util.h"
+			>
+		</File>
+		<File
+			RelativePath="..\word_iterator.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\word_iterator.h"
+			>
+		</File>
+		<File
+			RelativePath="..\worker_pool.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\worker_pool.h"
+			>
+		</File>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
diff --git a/base/build/base.vsprops b/base/build/base.vsprops
new file mode 100644
index 0000000..63e2723
--- /dev/null
+++ b/base/build/base.vsprops
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+	ProjectType="Visual C++"
+	Version="8.00"
+	Name="base"
+	InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\third_party\icu38\build\using_icu.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops"
+	>
+</VisualStudioPropertySheet>
diff --git a/base/build/base_gfx.vcproj b/base/build/base_gfx.vcproj
new file mode 100644
index 0000000..ac60c50
--- /dev/null
+++ b/base/build/base_gfx.vcproj
@@ -0,0 +1,263 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="8.00"
+	Name="base_gfx"
+	ProjectGUID="{A508ADD3-CECE-4E0F-8448-2F5E454DF551}"
+	RootNamespace="base_gfx"
+	Keyword="Win32Proj"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			ConfigurationType="4"
+			InheritedPropertySheets="$(SolutionDir)..\build\debug.vsprops;.\base_gfx.vsprops;..\..\skia\using_skia.vsprops"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			ConfigurationType="4"
+			InheritedPropertySheets="$(SolutionDir)..\build\release.vsprops;.\base_gfx.vsprops;..\..\skia\using_skia.vsprops"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<File
+			RelativePath="..\gfx\bitmap_header.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\gfx\bitmap_header.h"
+			>
+		</File>
+		<File
+			RelativePath="..\gfx\bitmap_platform_device.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\gfx\bitmap_platform_device.h"
+			>
+		</File>
+		<File
+			RelativePath="..\gfx\convolver.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\gfx\convolver.h"
+			>
+		</File>
+		<File
+			RelativePath="..\gfx\font_utils.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\gfx\font_utils.h"
+			>
+		</File>
+		<File
+			RelativePath="..\gfx\image_operations.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\gfx\image_operations.h"
+			>
+		</File>
+		<File
+			RelativePath="..\gfx\native_theme.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\gfx\native_theme.h"
+			>
+		</File>
+		<File
+			RelativePath="..\gfx\platform_canvas.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\gfx\platform_canvas.h"
+			>
+		</File>
+		<File
+			RelativePath="..\gfx\platform_device.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\gfx\platform_device.h"
+			>
+		</File>
+		<File
+			RelativePath="..\gfx\png_decoder.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\gfx\png_decoder.h"
+			>
+		</File>
+		<File
+			RelativePath="..\gfx\png_encoder.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\gfx\png_encoder.h"
+			>
+		</File>
+		<File
+			RelativePath="..\gfx\point.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\gfx\point.h"
+			>
+		</File>
+		<File
+			RelativePath="..\gfx\rect.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\gfx\rect.h"
+			>
+		</File>
+		<File
+			RelativePath="..\gfx\size.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\gfx\size.h"
+			>
+		</File>
+		<File
+			RelativePath="..\gfx\skia_utils.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\gfx\skia_utils.h"
+			>
+		</File>
+		<File
+			RelativePath="..\gfx\uniscribe.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\gfx\uniscribe.h"
+			>
+		</File>
+		<File
+			RelativePath="..\gfx\vector_canvas.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\gfx\vector_canvas.h"
+			>
+		</File>
+		<File
+			RelativePath="..\gfx\vector_device.cc"
+			>
+		</File>
+		<File
+			RelativePath="..\gfx\vector_device.h"
+			>
+		</File>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
diff --git a/base/build/base_gfx.vsprops b/base/build/base_gfx.vsprops
new file mode 100644
index 0000000..9dc5341
--- /dev/null
+++ b/base/build/base_gfx.vsprops
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+	ProjectType="Visual C++"
+	Version="8.00"
+	Name="base_gfx"
+	InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\third_party\icu38\build\using_icu.vsprops;$(SolutionDir)..\third_party\libpng\using_libpng.vsprops;$(SolutionDir)..\third_party\zlib\using_zlib.vsprops;..\..\skia\using_skia.vsprops"
+	>
+</VisualStudioPropertySheet>
diff --git a/base/build/base_unittests.vcproj b/base/build/base_unittests.vcproj
new file mode 100644
index 0000000..f9e6787
--- /dev/null
+++ b/base/build/base_unittests.vcproj
@@ -0,0 +1,370 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="8.00"
+	Name="base_unittests"
+	ProjectGUID="{27A30967-4BBA-48D1-8522-CDE95F7B1CEC}"
+	RootNamespace="base_unittests"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			ConfigurationType="1"
+			InheritedPropertySheets="$(SolutionDir)..\build\debug.vsprops;.\base_unittests.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				PreprocessorDefinitions="UNIT_TEST"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCWebDeploymentTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			ConfigurationType="1"
+			InheritedPropertySheets="$(SolutionDir)..\build\release.vsprops;.\base_unittests.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				PreprocessorDefinitions="UNIT_TEST"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCWebDeploymentTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="support"
+			>
+			<File
+				RelativePath="..\check_handler.h"
+				>
+			</File>
+			<File
+				RelativePath="..\multiprocess_test.h"
+				>
+			</File>
+			<File
+				RelativePath="..\no_windows2000_unittest.h"
+				>
+			</File>
+			<File
+				RelativePath="..\run_all_unittests.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\test_suite.h"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="base_tests"
+			>
+			<File
+				RelativePath="..\atomic_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\check_handler_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\clipboard_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\command_line_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\condition_variable_test.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\file_util_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\file_version_info_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\fixed_string_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\histogram_test.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\hmac_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\idletimer_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\json_reader_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\json_writer_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\linked_ptr_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\message_loop_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\gfx\native_theme_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\path_service_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\pe_image_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\pickle_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\pr_time_test.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\process_util_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\gfx\rect_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\ref_counted_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\sha2_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\shared_event_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\shared_memory_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\singleton_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\stack_container_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\stats_table_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\string_escape_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\string_piece_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\string_tokenizer_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\string_util_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\thread_local_storage_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\thread_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\time_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\timer_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\tracked_objects_test.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\values_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\watchdog_test.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\win_util_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\wmi_util_unittest.cc"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="gfx_tests"
+			>
+			<File
+				RelativePath="..\gfx\convolver_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\gfx\image_operations_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\gfx\platform_canvas_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\gfx\png_codec_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\gfx\uniscribe_unittest.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\gfx\vector_canvas_unittest.cc"
+				>
+			</File>
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
diff --git a/base/build/base_unittests.vsprops b/base/build/base_unittests.vsprops
new file mode 100644
index 0000000..047887d
--- /dev/null
+++ b/base/build/base_unittests.vsprops
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+	ProjectType="Visual C++"
+	Version="8.00"
+	Name="base_unittests"
+	InheritedPropertySheets=".\base_gfx.vsprops"
+	>
+</VisualStudioPropertySheet>
diff --git a/base/build/debug_message.vcproj b/base/build/debug_message.vcproj
new file mode 100644
index 0000000..8dac295
--- /dev/null
+++ b/base/build/debug_message.vcproj
@@ -0,0 +1,151 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="8.00"
+	Name="debug_message"
+	ProjectGUID="{0E5474AC-5996-4B13-87C0-4AE931EE0815}"
+	RootNamespace="DebugMessage"
+	Keyword="Win32Proj"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			ConfigurationType="1"
+			InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\debug.vsprops"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				SubSystem="2"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCWebDeploymentTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			ConfigurationType="1"
+			InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\release.vsprops"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				SubSystem="2"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCWebDeploymentTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<File
+			RelativePath="..\debug_message.cc"
+			>
+		</File>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
diff --git a/base/build/singleton_dll_unittest.def b/base/build/singleton_dll_unittest.def
new file mode 100644
index 0000000..6ad4d90
--- /dev/null
+++ b/base/build/singleton_dll_unittest.def
@@ -0,0 +1,9 @@
+EXPORTS
+	SingletonInt1
+	SingletonInt2
+	SingletonInt3
+	SingletonInt4
+	SingletonInt5
+	SingletonNoLeak
+	SingletonLeak
+	GetLeakySingleton
diff --git a/base/build/singleton_dll_unittest.vsprops b/base/build/singleton_dll_unittest.vsprops
new file mode 100644
index 0000000..a3eb79b
--- /dev/null
+++ b/base/build/singleton_dll_unittest.vsprops
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+	ProjectType="Visual C++"
+	Version="8.00"
+	Name="singleton_dll_unittest"
+	>
+	<Tool
+		Name="VCCLCompilerTool"
+		PreprocessorDefinitions="SINGLETON_UNITTEST_EXPORTS"
+	/>
+	<Tool
+		Name="VCLinkerTool"
+		ModuleDefinitionFile="singleton_dll_unittest.def"
+	/>
+</VisualStudioPropertySheet>
diff --git a/base/build/singleton_unittest.vcproj b/base/build/singleton_unittest.vcproj
new file mode 100644
index 0000000..6fcb715
--- /dev/null
+++ b/base/build/singleton_unittest.vcproj
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="8.00"
+	Name="singleton_dll_unittest"
+	ProjectGUID="{E457F2FB-4708-4001-9B1C-275D7BD7F2A8}"
+	RootNamespace="singleton_unittest"
+	Keyword="Win32Proj"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			ConfigurationType="2"
+			InheritedPropertySheets="$(SolutionDir)..\build\debug.vsprops;.\singleton_dll_unittest.vsprops"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCWebDeploymentTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			ConfigurationType="2"
+			InheritedPropertySheets="$(SolutionDir)..\build\release.vsprops;.\singleton_dll_unittest.vsprops"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCWebDeploymentTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<File
+			RelativePath="..\singleton_dll_unittest.cc"
+			>
+		</File>
+		<File
+			RelativePath=".\singleton_dll_unittest.def"
+			>
+		</File>
+		<File
+			RelativePath="..\singleton_dll_unittest.h"
+			>
+		</File>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
diff --git a/base/check_handler.h b/base/check_handler.h
new file mode 100644
index 0000000..cf6dd67
--- /dev/null
+++ b/base/check_handler.h
@@ -0,0 +1,112 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_CHECK_HANDLER_H__
+#define BASE_CHECK_HANDLER_H__
+
+#include <windows.h>
+
+#include "base/logging.h"
+
+// This class allows temporary handling of assert firing. When a CHECK()
+// or DCHECK() assertion happens it will in turn generate a SEH exception
+// which can be can captured using a windows SEH hander __try .. _except
+// block. One practical use of this class is for unit tests that make sure
+// CHECK conditions are appropriately handled. For example:
+//
+//  TEST(TestGroup, VerifyAssert) {
+//    CheckAssertHandler expect_exception;
+//    __try {
+//      MyClass object;                  // MyClass dtor will not be called.
+//      Param some_bad_param;
+//      object.Method(some_bad_param);   //  Should triggers a CHECK().
+//      ADD_FAILURE();                   // If we get here the test failed.
+//    } __except(EXCEPTION_EXECUTE_HANDLER) {
+//      DWORD ecode = GetExceptionCode();
+//      EXPECT_EQ(CheckAssertHandler::seh_exception_code(), ecode);
+//    }
+//  }
+//
+// You can put MyClass outside the __try block so its destructor will be
+// called which could lead to a crash if the state of the object is
+// corrupted by the CHECK you are testing. If that is the case you should
+// fix the state of the object inside the __except block.
+//
+// Since the above code is Windows specific, two helper macros are provided
+// that hide the implementation details. Using the macros the code becomes:
+//
+//  TEST(TestGroup, VerifyAssert) {
+//    CHECK_HANDLER_BEGIN
+//      MyClass object;                  // MyClass dtor will not be called.
+//      Param some_bad_param;
+//      object.Method(some_bad_param);   //  Should triggers a CHECK().
+//    CHECK_HANDLER_END
+//  }
+//
+// Depending on the compiler settings you might have issue this pragma arround
+// the code that uses this class:
+//   #pragma warning(disable: 4509)
+// Which tells the compiler that is ok that some dtors will not be called.
+//
+// Create this object on the stack always.Do not create it inside the
+// __try block itself or the dtor will never be called. Create only one
+// on each scope.
+//
+// The key detail here is the RaiseException() call which transfers
+// program control away from the code that caused the assertion and back
+// into the _except block.
+
+class CheckAssertHandler {
+ public:
+  // Installs the assert handler. The dtor will remove the handler.
+  CheckAssertHandler() {
+    logging::SetLogAssertHandler(&CheckAssertHandler::LogAssertHandler);
+  }
+  ~CheckAssertHandler() {
+    logging::SetLogAssertHandler(NULL);
+  }
+  static DWORD seh_exception_code() { return 0x1765413; }
+ private:
+  static void LogAssertHandler(const std::string&) {
+    ::RaiseException(seh_exception_code(), 0, 0, NULL);
+  }
+};
+
+#define CHECK_HANDLER_BEGIN \
+  CheckAssertHandler chk_ex_handler; \
+  __try {
+
+#define CHECK_HANDLER_END \
+    ADD_FAILURE(); \
+  } __except(EXCEPTION_EXECUTE_HANDLER) { \
+    DWORD ecode = GetExceptionCode(); \
+    EXPECT_EQ(CheckAssertHandler::seh_exception_code(), ecode); \
+  }
+
+#endif  // BASE_CHECK_HANDLER_H__
diff --git a/base/check_handler_unittest.cc b/base/check_handler_unittest.cc
new file mode 100644
index 0000000..ba03038
--- /dev/null
+++ b/base/check_handler_unittest.cc
@@ -0,0 +1,72 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/check_handler.h"
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class SimpleTestClass {
+ public:
+  SimpleTestClass() {
+  }
+  ~SimpleTestClass() {
+    ADD_FAILURE();
+  }
+  void ThisMethodAsserts() {
+    CHECK(false);
+    ADD_FAILURE();
+  }
+};
+
+void ThisFunctionAsserts() {
+  CHECK(false);
+  ADD_FAILURE();
+}
+
+}  // namespace
+
+#pragma warning(push)
+#pragma warning(disable: 4509)
+
+TEST(CheckHandlerTest, TestMacroCheckObj) {
+  CHECK_HANDLER_BEGIN
+    SimpleTestClass object;
+    object.ThisMethodAsserts();
+  CHECK_HANDLER_END
+}
+
+TEST(CheckHandlerTest, TestMacroCheckFunc) {
+  CHECK_HANDLER_BEGIN
+    ThisFunctionAsserts();
+  CHECK_HANDLER_END
+}
+
+#pragma warning(pop)
diff --git a/base/clipboard.cc b/base/clipboard.cc
new file mode 100644
index 0000000..b85ec00
--- /dev/null
+++ b/base/clipboard.cc
@@ -0,0 +1,658 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Many of these functions are based on those found in
+// webkit/port/platform/PasteboardWin.cpp
+
+#include <shlobj.h>
+#include <shellapi.h>
+
+#include "base/clipboard.h"
+
+#include "base/clipboard_util.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+
+namespace {
+
+// A small object to ensure we close the clipboard after opening it.
+class ClipboardLock {
+ public:
+  ClipboardLock() : we_own_the_lock_(false) { }
+
+  ~ClipboardLock() {
+    if (we_own_the_lock_)
+      Release();
+  }
+
+  bool Acquire(HWND owner) {
+    // We shouldn't be calling this if we already own the clipbard lock.
+    DCHECK(!we_own_the_lock_);
+
+    // We already have the lock.  We don't want to stomp on the other use.
+    if (we_own_the_lock_)
+      return false;
+
+    const int kMaxAttemptsToOpenClipboard = 5;
+
+    // Attempt to acquire the clipboard lock.  This may fail if another process
+    // currently holds the lock.  We're willing to try a few times in the hopes
+    // of acquiring it.
+    //
+    // This turns out to be an issue when using remote desktop because the
+    // rdpclip.exe process likes to read what we've written to the clipboard and
+    // send it to the RDP client.  If we open and close the clipboard in quick
+    // succession, we might be trying to open it while rdpclip.exe has it open,
+    // See Bug 815425.
+    //
+    // In fact, we believe we'll only spin this loop over remote desktop.  In
+    // normal situations, the user is initiating clipboard operations and there
+    // shouldn't be lock contention.
+
+    for (int attempts = 0; attempts < kMaxAttemptsToOpenClipboard; ++attempts) {
+      if (::OpenClipboard(owner)) {
+        we_own_the_lock_ = true;
+        return we_own_the_lock_;
+      }
+
+      // Having failed, we yeild our timeslice to other processes. ::Yield seems
+      // to be insufficient here, so we sleep for 5 ms.
+      if (attempts < (kMaxAttemptsToOpenClipboard - 1))
+        ::Sleep(5);
+    }
+
+    // We failed to acquire the clipboard.
+    return false;
+  }
+
+  void Release() {
+    // We should only be calling this if we already own the clipbard lock.
+    DCHECK(we_own_the_lock_);
+
+    // We we don't have the lock, there is nothing to release.
+    if (!we_own_the_lock_)
+      return;
+
+    ::CloseClipboard();
+    we_own_the_lock_ = false;
+  }
+
+ private:
+  bool we_own_the_lock_;
+};
+
+LRESULT CALLBACK ClipboardOwnerWndProc(HWND hwnd,
+                                       UINT message,
+                                       WPARAM wparam,
+                                       LPARAM lparam) {
+  LRESULT lresult = 0;
+
+  switch(message) {
+  case WM_RENDERFORMAT:
+    // This message comes when SetClipboardData was sent a null data handle
+    // and now it's come time to put the data on the clipboard.
+    // We always set data, so there isn't a need to actually do anything here.
+    break;
+  case WM_RENDERALLFORMATS:
+    // This message comes when SetClipboardData was sent a null data handle
+    // and now this application is about to quit, so it must put data on
+    // the clipboard before it exits.
+    // We always set data, so there isn't a need to actually do anything here.
+    break;
+  case WM_DRAWCLIPBOARD:
+    break;
+  case WM_DESTROY:
+    break;
+  case WM_CHANGECBCHAIN:
+    break;
+  default:
+    lresult = DefWindowProc(hwnd, message, wparam, lparam);
+    break;
+  }
+  return lresult;
+}
+
+template <typename charT>
+HGLOBAL CreateGlobalData(const std::basic_string<charT>& str) {
+  HGLOBAL data =
+    ::GlobalAlloc(GMEM_MOVEABLE, ((str.size() + 1) * sizeof(charT)));
+  if (data) {
+    charT* raw_data = static_cast<charT*>(::GlobalLock(data));
+    memcpy(raw_data, str.data(), str.size() * sizeof(charT));
+    raw_data[str.size()] = '\0';
+    ::GlobalUnlock(data);
+  }
+  return data;
+};
+
+} // namespace
+
+Clipboard::Clipboard() {
+  // make a dummy HWND to be the clipboard's owner
+  WNDCLASSEX wcex = {0};
+  wcex.cbSize = sizeof(WNDCLASSEX);
+  wcex.lpfnWndProc = ClipboardOwnerWndProc;
+  wcex.hInstance = GetModuleHandle(NULL);
+  wcex.lpszClassName = L"ClipboardOwnerWindowClass";
+  ::RegisterClassEx(&wcex);
+
+  clipboard_owner_ = ::CreateWindow(L"ClipboardOwnerWindowClass",
+                                    L"ClipboardOwnerWindow",
+                                    0, 0, 0, 0, 0,
+                                    HWND_MESSAGE,
+                                    0, 0, 0);
+}
+
+Clipboard::~Clipboard() {
+  ::DestroyWindow(clipboard_owner_);
+  clipboard_owner_ = NULL;
+}
+
+void Clipboard::Clear() const {
+  // Acquire the clipboard.
+  ClipboardLock lock;
+  if (!lock.Acquire(clipboard_owner_))
+    return;
+
+  ::EmptyClipboard();
+}
+
+void Clipboard::WriteText(const std::wstring& text) const {
+  ClipboardLock lock;
+  if (!lock.Acquire(clipboard_owner_))
+    return;
+
+  HGLOBAL glob = CreateGlobalData(text);
+  if (glob && !::SetClipboardData(CF_UNICODETEXT, glob))
+    ::GlobalFree(glob);
+}
+
+void Clipboard::WriteHTML(const std::wstring& markup,
+                          const std::string& url) const {
+  // Acquire the clipboard.
+  ClipboardLock lock;
+  if (!lock.Acquire(clipboard_owner_))
+    return;
+
+  std::string html_fragment;
+  MarkupToHTMLClipboardFormat(markup, url, &html_fragment);
+  HGLOBAL glob = CreateGlobalData(html_fragment);
+  if (glob && !::SetClipboardData(ClipboardUtil::GetHtmlFormat()->cfFormat,
+                                  glob)) {
+    ::GlobalFree(glob);
+  }
+}
+
+void Clipboard::WriteBookmark(const std::wstring& title,
+                              const std::string& url) const {
+  // Acquire the clipboard.
+  ClipboardLock lock;
+  if (!lock.Acquire(clipboard_owner_))
+    return;
+
+  std::wstring bookmark(title);
+  bookmark.append(1, L'\n');
+  bookmark.append(UTF8ToWide(url));
+  HGLOBAL glob = CreateGlobalData(bookmark);
+  if (glob && !::SetClipboardData(ClipboardUtil::GetUrlWFormat()->cfFormat,
+                                  glob)) {
+    ::GlobalFree(glob);
+  }
+}
+
+void Clipboard::WriteHyperlink(const std::wstring& title,
+                               const std::string& url) const {
+  // Write as a bookmark.
+  WriteBookmark(title, url);
+
+  // Build the HTML link.
+  std::wstring link(L"<a href=\"");
+  link.append(UTF8ToWide(url));
+  link.append(L"\">");
+  link.append(title);
+  link.append(L"</a>");
+
+  // Write as an HTML link.
+  WriteHTML(link, std::string());
+}
+
+void Clipboard::WriteWebSmartPaste() const {
+  // Acquire the clipboard.
+  ClipboardLock lock;
+  if (!lock.Acquire(clipboard_owner_))
+    return;
+
+  SetClipboardData(ClipboardUtil::GetWebKitSmartPasteFormat()->cfFormat, NULL);
+}
+
+void Clipboard::WriteBitmap(const void* pixels, const gfx::Size& size) const {
+  HDC dc = ::GetDC(NULL);
+
+  // This doesn't actually cost us a memcpy when the bitmap comes from the
+  // renderer as we load it into the bitmap using setPixels which just sets a
+  // pointer.  Someone has to memcpy it into GDI, it might as well be us here.
+
+  // TODO(darin): share data in gfx/bitmap_header.cc somehow
+  BITMAPINFO bm_info = {0};
+  bm_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+  bm_info.bmiHeader.biWidth = size.width();
+  bm_info.bmiHeader.biHeight = -size.height();  // sets vertical orientation
+  bm_info.bmiHeader.biPlanes = 1;
+  bm_info.bmiHeader.biBitCount = 32;
+  bm_info.bmiHeader.biCompression = BI_RGB;
+
+  // ::CreateDIBSection allocates memory for us to copy our bitmap into.
+  // Unfortunately, we can't write the created bitmap to the clipboard,
+  // (see http://msdn2.microsoft.com/en-us/library/ms532292.aspx)
+  void *bits;
+  HBITMAP source_hbitmap =
+      ::CreateDIBSection(dc, &bm_info, DIB_RGB_COLORS, &bits, NULL, 0);
+
+  if (bits && source_hbitmap) {
+    // Copy the bitmap out of shared memory and into GDI
+    memcpy(bits, pixels, 4 * size.width() * size.height());
+
+    // Now we have an HBITMAP, we can write it to the clipboard
+    WriteBitmapFromHandle(source_hbitmap, size);
+  }
+
+  ::DeleteObject(source_hbitmap);
+  ::ReleaseDC(NULL, dc);
+}
+
+void Clipboard::WriteBitmapFromSharedMemory(const SharedMemory& bitmap,
+                                            const gfx::Size& size) const {
+  // TODO(darin): share data in gfx/bitmap_header.cc somehow
+  BITMAPINFO bm_info = {0};
+  bm_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+  bm_info.bmiHeader.biWidth = size.width();
+  bm_info.bmiHeader.biHeight = -size.height();  // Sets the vertical orientation
+  bm_info.bmiHeader.biPlanes = 1;
+  bm_info.bmiHeader.biBitCount = 32;
+  bm_info.bmiHeader.biCompression = BI_RGB;
+
+  HDC dc = ::GetDC(NULL);
+
+  // We can create an HBITMAP directly using the shared memory handle, saving
+  // a memcpy.
+  HBITMAP source_hbitmap =
+      ::CreateDIBSection(dc, &bm_info, DIB_RGB_COLORS, NULL, bitmap.handle(), 0);
+
+  if (source_hbitmap) {
+    // Now we can write the HBITMAP to the clipboard
+    WriteBitmapFromHandle(source_hbitmap, size);
+  }
+
+  ::DeleteObject(source_hbitmap);
+  ::ReleaseDC(NULL, dc);
+}
+
+void Clipboard::WriteBitmapFromHandle(HBITMAP source_hbitmap,
+                                      const gfx::Size& size) const {
+  // Acquire the clipboard.
+  ClipboardLock lock;
+  if (!lock.Acquire(clipboard_owner_))
+    return;
+
+  // We would like to just call ::SetClipboardData on the source_hbitmap,
+  // but that bitmap might not be of a sort we can write to the clipboard.
+  // For this reason, we create a new bitmap, copy the bits over, and then
+  // write that to the clipboard.
+
+  HDC dc = ::GetDC(NULL);
+  HDC compatible_dc = ::CreateCompatibleDC(NULL);
+  HDC source_dc = ::CreateCompatibleDC(NULL);
+
+  // This is the HBITMAP we will eventually write to the clipboard
+  HBITMAP hbitmap = ::CreateCompatibleBitmap(dc, size.width(), size.height());
+  if (!hbitmap) {
+    // Failed to create the bitmap
+    ::DeleteDC(compatible_dc);
+    ::DeleteDC(source_dc);
+    ::ReleaseDC(NULL, dc);
+    return;
+  }
+
+  HBITMAP old_hbitmap = (HBITMAP)SelectObject(compatible_dc, hbitmap);
+  HBITMAP old_source = (HBITMAP)SelectObject(source_dc, source_hbitmap);
+
+  // Now we need to blend it into an HBITMAP we can place on the clipboard
+  BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
+  ::AlphaBlend(compatible_dc, 0, 0, size.width(), size.height(),
+               source_dc, 0, 0, size.width(), size.height(), bf);
+
+  // Clean up all the handles we just opened
+  ::SelectObject(compatible_dc, old_hbitmap);
+  ::SelectObject(source_dc, old_source);
+  ::DeleteObject(old_hbitmap);
+  ::DeleteObject(old_source);
+  ::DeleteDC(compatible_dc);
+  ::DeleteDC(source_dc);
+  ::ReleaseDC(NULL, dc);
+
+  // Actually write the bitmap to the clipboard
+  ::SetClipboardData(CF_BITMAP, hbitmap);
+}
+
+// Write a file or set of files to the clipboard in HDROP format. When the user
+// invokes a paste command (in a Windows explorer shell, for example), the files
+// will be copied to the paste location.
+void Clipboard::WriteFile(const std::wstring& file) const {
+  std::vector<std::wstring> files;
+  files.push_back(file);
+  WriteFiles(files);
+}
+
+void Clipboard::WriteFiles(const std::vector<std::wstring>& files) const {
+  ClipboardLock lock;
+  if (!lock.Acquire(clipboard_owner_))
+    return;
+
+  // Calculate the amount of space we'll need store the strings: require
+  // NULL terminator between strings, and double null terminator at the end.
+  size_t bytes = sizeof(DROPFILES);
+  for (size_t i = 0; i < files.size(); ++i)
+    bytes += (files[i].length() + 1) * sizeof(wchar_t);
+  bytes += sizeof(wchar_t);
+
+  HANDLE hdata = ::GlobalAlloc(GMEM_MOVEABLE, bytes);
+  if (!hdata)
+    return;
+
+  DROPFILES* drop_files = static_cast<DROPFILES*>(::GlobalLock(hdata));
+  drop_files->pFiles = sizeof(DROPFILES);
+  drop_files->fWide = TRUE;
+  BYTE* data = reinterpret_cast<BYTE*>(drop_files) + sizeof(DROPFILES);
+
+  // Copy the strings stored in 'files' with proper NULL separation.
+  wchar_t* data_pos = reinterpret_cast<wchar_t*>(data);
+  for (size_t i = 0; i < files.size(); ++i) {
+    size_t offset = files[i].length() + 1;
+    memcpy(data_pos, files[i].c_str(), offset * sizeof(wchar_t));
+    data_pos += offset;
+  }
+  data_pos[0] = L'\0';  // Double NULL termination after the last string.
+
+  ::GlobalUnlock(hdata);
+  if (!::SetClipboardData(CF_HDROP, hdata))
+    ::GlobalFree(hdata);
+}
+
+bool Clipboard::IsFormatAvailable(unsigned int format) const {
+  return ::IsClipboardFormatAvailable(format) != FALSE;
+}
+
+void Clipboard::ReadText(std::wstring* result) const {
+  if (!result) {
+    NOTREACHED();
+    return;
+  }
+
+  result->clear();
+
+  // Acquire the clipboard.
+  ClipboardLock lock;
+  if (!lock.Acquire(clipboard_owner_))
+    return;
+
+  HANDLE data = ::GetClipboardData(CF_UNICODETEXT);
+  if (!data)
+    return;
+
+  result->assign(static_cast<const wchar_t*>(::GlobalLock(data)));
+  ::GlobalUnlock(data);
+}
+
+void Clipboard::ReadAsciiText(std::string* result) const {
+  if (!result) {
+    NOTREACHED();
+    return;
+  }
+
+  result->clear();
+
+  // Acquire the clipboard.
+  ClipboardLock lock;
+  if (!lock.Acquire(clipboard_owner_))
+    return;
+
+  HANDLE data = ::GetClipboardData(CF_TEXT);
+  if (!data)
+    return;
+
+  result->assign(static_cast<const char*>(::GlobalLock(data)));
+  ::GlobalUnlock(data);
+}
+
+void Clipboard::ReadHTML(std::wstring* markup, std::string* src_url) const {
+  if (markup)
+    markup->clear();
+
+  if (src_url)
+    src_url->clear();
+
+  // Acquire the clipboard.
+  ClipboardLock lock;
+  if (!lock.Acquire(clipboard_owner_))
+    return;
+
+  HANDLE data = ::GetClipboardData(ClipboardUtil::GetHtmlFormat()->cfFormat);
+  if (!data)
+    return;
+
+  std::string html_fragment(static_cast<const char*>(::GlobalLock(data)));
+  ::GlobalUnlock(data);
+
+  ParseHTMLClipboardFormat(html_fragment, markup, src_url);
+}
+
+void Clipboard::ReadBookmark(std::wstring* title, std::string* url) const {
+  if (title)
+    title->clear();
+
+  if (url)
+    url->clear();
+
+  // Acquire the clipboard.
+  ClipboardLock lock;
+  if (!lock.Acquire(clipboard_owner_))
+    return;
+
+  HANDLE data = ::GetClipboardData(ClipboardUtil::GetUrlWFormat()->cfFormat);
+  if (!data)
+    return;
+
+  std::wstring bookmark(static_cast<const wchar_t*>(::GlobalLock(data)));
+  ::GlobalUnlock(data);
+
+  ParseBookmarkClipboardFormat(bookmark, title, url);
+}
+
+// Read a file in HDROP format from the clipboard.
+void Clipboard::ReadFile(std::wstring* file) const {
+  if (!file) {
+    NOTREACHED();
+    return;
+  }
+
+  file->clear();
+  std::vector<std::wstring> files;
+  ReadFiles(&files);
+
+  // Take the first file, if available.
+  if (!files.empty())
+    file->assign(files[0]);
+}
+
+// Read a set of files in HDROP format from the clipboard.
+void Clipboard::ReadFiles(std::vector<std::wstring>* files) const {
+  if (!files) {
+    NOTREACHED();
+    return;
+  }
+
+  files->clear();
+
+  ClipboardLock lock;
+  if (!lock.Acquire(clipboard_owner_))
+    return;
+
+  HDROP drop = static_cast<HDROP>(::GetClipboardData(CF_HDROP));
+  if (!drop)
+    return;
+
+  // Count of files in the HDROP.
+  int count = ::DragQueryFile(drop, 0xffffffff, NULL, 0);
+
+  if (count) {
+    for (int i = 0; i < count; ++i) {
+      int size = ::DragQueryFile(drop, i, NULL, 0) + 1;
+      std::wstring file;
+      ::DragQueryFile(drop, i, WriteInto(&file, size), size);
+      files->push_back(file);
+    }
+  }
+}
+
+// static
+void Clipboard::MarkupToHTMLClipboardFormat(const std::wstring& markup,
+                                            const std::string& src_url,
+                                            std::string* html_fragment) {
+  DCHECK(html_fragment);
+  // Documentation for the CF_HTML format is available at
+  // http://msdn.microsoft.com/workshop/networking/clipboard/htmlclipboard.asp
+
+  if (markup.empty()) {
+    html_fragment->clear();
+    return;
+  }
+
+  std::string markup_utf8 = WideToUTF8(markup);
+
+  html_fragment->assign("Version:0.9");
+
+  std::string start_html("\nStartHTML:");
+  std::string end_html("\nEndHTML:");
+  std::string start_fragment("\nStartFragment:");
+  std::string end_fragment("\nEndFragment:");
+  std::string source_url("\nSourceURL:");
+
+  bool has_source_url = !src_url.empty() &&
+                        !StartsWithASCII(src_url, "about:", false);
+  if (has_source_url)
+    source_url.append(src_url);
+
+  std::string start_markup("\n<HTML>\n<BODY>\n<!--StartFragment-->\n");
+  std::string end_markup("\n<!--EndFragment-->\n</BODY>\n</HTML>");
+
+  // calculate offsets
+  const size_t kMaxDigits = 10; // number of digits in UINT_MAX in base 10
+
+  size_t start_html_offset, start_fragment_offset;
+  size_t end_fragment_offset, end_html_offset;
+
+  start_html_offset = html_fragment->length() +
+                      start_html.length() + end_html.length() +
+                      start_fragment.length() + end_fragment.length() +
+                      (has_source_url ? source_url.length() : 0) +
+                      (4*kMaxDigits);
+
+  start_fragment_offset = start_html_offset + start_markup.length();
+  end_fragment_offset = start_fragment_offset + markup_utf8.length();
+  end_html_offset = end_fragment_offset + end_markup.length();
+
+  // fill in needed data
+  start_html.append(StringPrintf("%010u", start_html_offset));
+  end_html.append(StringPrintf("%010u", end_html_offset));
+  start_fragment.append(StringPrintf("%010u", start_fragment_offset));
+  end_fragment.append(StringPrintf("%010u", end_fragment_offset));
+  start_markup.append(markup_utf8);
+
+  // create full html_fragment string from the fragments
+  html_fragment->append(start_html);
+  html_fragment->append(end_html);
+  html_fragment->append(start_fragment);
+  html_fragment->append(end_fragment);
+  if (has_source_url)
+    html_fragment->append(source_url);
+  html_fragment->append(start_markup);
+  html_fragment->append(end_markup);
+}
+
+// static
+void Clipboard::ParseHTMLClipboardFormat(const std::string& html_frag,
+                                         std::wstring* markup,
+                                         std::string* src_url) {
+  if (src_url) {
+    // Obtain SourceURL, if present
+    std::string src_url_str("SourceURL:");
+    size_t line_start = html_frag.find(src_url_str, 0);
+    if (line_start != std::string::npos) {
+      size_t src_start = line_start+src_url_str.length();
+      size_t src_end = html_frag.find("\n", line_start);
+
+      if (src_end != std::string::npos)
+        *src_url = html_frag.substr(src_start, src_end - src_start);
+    }
+  }
+
+  if (markup) {
+    // Find the markup between "<!--StartFragment -->" and
+    // "<!--EndFragment -->", accounting for browser quirks
+    size_t markup_start = html_frag.find('<', 0);
+    size_t tag_start = html_frag.find("StartFragment", markup_start);
+    size_t frag_start = html_frag.find('>', tag_start) + 1;
+    // Here we do something slightly differently than WebKit.  Webkit does a
+    // forward find for EndFragment, but that seems to be a bug if the html
+    // fragment actually includes the string "EndFragment"
+    size_t tag_end = html_frag.rfind("EndFragment", std::string::npos);
+    size_t frag_end = html_frag.rfind('<', tag_end);
+
+    TrimWhitespace(UTF8ToWide(html_frag.substr(frag_start,
+                                               frag_end - frag_start)),
+                   TRIM_ALL, markup);
+  }
+}
+
+// static
+void Clipboard::ParseBookmarkClipboardFormat(const std::wstring& bookmark,
+                                             std::wstring* title,
+                                             std::string* url) {
+  const wchar_t* const kDelim = L"\r\n";
+
+  const size_t title_end = bookmark.find_first_of(kDelim);
+  if (title)
+    title->assign(bookmark.substr(0, title_end));
+
+  if (url) {
+    const size_t url_start = bookmark.find_first_not_of(kDelim, title_end);
+    if (url_start != std::wstring::npos)
+      *url = WideToUTF8(bookmark.substr(url_start, std::wstring::npos));
+  }
+}
diff --git a/base/clipboard.h b/base/clipboard.h
new file mode 100644
index 0000000..3599241
--- /dev/null
+++ b/base/clipboard.h
@@ -0,0 +1,126 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_CLIPBOARD_H__
+#define BASE_CLIPBOARD_H__
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/gfx/size.h"
+#include "base/shared_memory.h"
+
+class Clipboard {
+ public:
+  Clipboard();
+  ~Clipboard();
+
+  // Clears the clipboard.  It is usually a good idea to clear the clipboard
+  // before writing content to the clipboard.
+  void Clear() const;
+
+  // Adds UNICODE and ASCII text to the clipboard.
+  void WriteText(const std::wstring& text) const;
+
+  // Adds HTML to the clipboard.  The url parameter is optional, but especially
+  // useful if the HTML fragment contains relative links
+  void WriteHTML(const std::wstring& markup, const std::string& src_url) const;
+
+  // Adds a bookmark to the clipboard
+  void WriteBookmark(const std::wstring& title, const std::string& url) const;
+
+  // Adds both a bookmark and an HTML hyperlink to the clipboard.  It is a
+  // convenience wrapper around WriteBookmark and WriteHTML.
+  void WriteHyperlink(const std::wstring& title, const std::string& url) const;
+
+  // Adds a bitmap to the clipboard
+  // This is the slowest way to copy a bitmap to the clipboard as we must first
+  // memcpy the pixels into GDI and the blit the bitmap to the clipboard.
+  // Pixel format is assumed to be 32-bit BI_RGB.
+  void WriteBitmap(const void* pixels, const gfx::Size& size) const;
+
+  // Adds a bitmap to the clipboard
+  // This function requires read and write access to the bitmap, but does not
+  // actually modify the shared memory region.
+  // Pixel format is assumed to be 32-bit BI_RGB.
+  void WriteBitmapFromSharedMemory(const SharedMemory& bitmap,
+                                   const gfx::Size& size) const;
+
+  // Adds a bitmap to the clipboard
+  // This is the fastest way to copy a bitmap to the clipboard.  The HBITMAP
+  // may either be device-dependent or device-independent.
+  void WriteBitmapFromHandle(HBITMAP hbitmap, const gfx::Size& size) const;
+
+  // Used by WebKit to determine whether WebKit wrote the clipboard last
+  void WriteWebSmartPaste() const;
+
+  // Adds a file or group of files to the clipboard.
+  void WriteFile(const std::wstring& file) const;
+  void WriteFiles(const std::vector<std::wstring>& files) const;
+
+  // Tests whether the clipboard contains a certain format
+  bool IsFormatAvailable(unsigned int format) const;
+
+  // Reads UNICODE text from the clipboard, if available.
+  void ReadText(std::wstring* result) const;
+
+  // Reads ASCII text from the clipboard, if available.
+  void ReadAsciiText(std::string* result) const;
+
+  // Reads HTML from the clipboard, if available.
+  void ReadHTML(std::wstring* markup, std::string* src_url) const;
+
+  // Reads a bookmark from the clipboard, if available.
+  void ReadBookmark(std::wstring* title, std::string* url) const;
+
+  // Reads a file or group of files from the clipboard, if available, into the
+  // out paramter.
+  void ReadFile(std::wstring* file) const;
+  void ReadFiles(std::vector<std::wstring>* files) const;
+
+ private:
+  static void MarkupToHTMLClipboardFormat(const std::wstring& markup,
+                                          const std::string& src_url,
+                                          std::string* html_fragment);
+
+  static void ParseHTMLClipboardFormat(const std::string& html_fragment,
+                                       std::wstring* markup,
+                                       std::string* src_url);
+
+  static void ParseBookmarkClipboardFormat(const std::wstring& bookmark,
+                                           std::wstring* title,
+                                           std::string* url);
+
+  HWND clipboard_owner_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(Clipboard);
+};
+
+#endif  // BASE_CLIPBOARD_H__
diff --git a/base/clipboard_unittest.cc b/base/clipboard_unittest.cc
new file mode 100644
index 0000000..219d0a5
--- /dev/null
+++ b/base/clipboard_unittest.cc
@@ -0,0 +1,211 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/clipboard.h"
+#include "base/clipboard_util.h"
+#include "base/string_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+  class ClipboardTest : public testing::Test {
+  };
+}
+
+TEST(ClipboardTest, ClearTest) {
+  Clipboard clipboard;
+
+  clipboard.Clear();
+  EXPECT_EQ(false, clipboard.IsFormatAvailable(CF_TEXT));
+  EXPECT_EQ(false, clipboard.IsFormatAvailable(
+      ClipboardUtil::GetHtmlFormat()->cfFormat));
+}
+
+TEST(ClipboardTest, TextTest) {
+  Clipboard clipboard;
+
+  std::wstring text(L"This is a wstring!#$"), text_result;
+  std::string ascii_text;
+
+  clipboard.Clear();
+  clipboard.WriteText(text);
+  EXPECT_EQ(true, clipboard.IsFormatAvailable(CF_UNICODETEXT));
+  EXPECT_EQ(true, clipboard.IsFormatAvailable(CF_TEXT));
+  clipboard.ReadText(&text_result);
+  EXPECT_EQ(text, text_result);
+  clipboard.ReadAsciiText(&ascii_text);
+  EXPECT_EQ(WideToUTF8(text), ascii_text);
+}
+
+TEST(ClipboardTest, HTMLTest) {
+  Clipboard clipboard;
+
+  std::wstring markup(L"<strong>Hi!</string>"), markup_result;
+  std::string url("http://www.example.com/"), url_result;
+
+  clipboard.Clear();
+  clipboard.WriteHTML(markup, url);
+  EXPECT_EQ(true, clipboard.IsFormatAvailable(
+      ClipboardUtil::GetHtmlFormat()->cfFormat));
+  clipboard.ReadHTML(&markup_result, &url_result);
+  EXPECT_EQ(markup, markup_result);
+  EXPECT_EQ(url, url_result);
+}
+
+TEST(ClipboardTest, TrickyHTMLTest) {
+  Clipboard clipboard;
+
+  std::wstring markup(L"<em>Bye!<!--EndFragment --></em>"), markup_result;
+  std::string url, url_result;
+
+  clipboard.Clear();
+  clipboard.WriteHTML(markup, url);
+  EXPECT_EQ(true, clipboard.IsFormatAvailable(
+      ClipboardUtil::GetHtmlFormat()->cfFormat));
+  clipboard.ReadHTML(&markup_result, &url_result);
+  EXPECT_EQ(markup, markup_result);
+  EXPECT_EQ(url, url_result);
+}
+
+TEST(ClipboardTest, BookmarkTest) {
+  Clipboard clipboard;
+
+  std::wstring title(L"The Example Company"), title_result;
+  std::string url("http://www.example.com/"), url_result;
+
+  clipboard.Clear();
+  clipboard.WriteBookmark(title, url);
+  EXPECT_EQ(true,
+      clipboard.IsFormatAvailable(ClipboardUtil::GetUrlWFormat()->cfFormat));
+  clipboard.ReadBookmark(&title_result, &url_result);
+  EXPECT_EQ(title, title_result);
+  EXPECT_EQ(url, url_result);
+}
+
+TEST(ClipboardTest, HyperlinkTest) {
+  Clipboard clipboard;
+
+  std::wstring title(L"The Example Company"), title_result;
+  std::string url("http://www.example.com/"), url_result;
+  std::wstring html(L"<a href=\"http://www.example.com/\">"
+                    L"The Example Company</a>"), html_result;
+
+  clipboard.Clear();
+  clipboard.WriteHyperlink(title, url);
+  EXPECT_EQ(true,
+      clipboard.IsFormatAvailable(ClipboardUtil::GetUrlWFormat()->cfFormat));
+  EXPECT_EQ(true,
+      clipboard.IsFormatAvailable(ClipboardUtil::GetHtmlFormat()->cfFormat));
+  clipboard.ReadBookmark(&title_result, &url_result);
+  EXPECT_EQ(title, title_result);
+  EXPECT_EQ(url, url_result);
+  clipboard.ReadHTML(&html_result, &url_result);
+  EXPECT_EQ(html, html_result);
+  //XXX EXPECT_FALSE(url_result.is_valid());
+}
+
+TEST(ClipboardTest, MultiFormatTest) {
+  Clipboard clipboard;
+
+  std::wstring text(L"Hi!"), text_result;
+  std::wstring markup(L"<strong>Hi!</string>"), markup_result;
+  std::string url("http://www.example.com/"), url_result;
+  std::string ascii_text;
+
+  clipboard.Clear();
+  clipboard.WriteHTML(markup, url);
+  clipboard.WriteText(text);
+  EXPECT_EQ(true,
+      clipboard.IsFormatAvailable(ClipboardUtil::GetHtmlFormat()->cfFormat));
+  EXPECT_EQ(true, clipboard.IsFormatAvailable(CF_UNICODETEXT));
+  EXPECT_EQ(true, clipboard.IsFormatAvailable(CF_TEXT));
+  clipboard.ReadHTML(&markup_result, &url_result);
+  EXPECT_EQ(markup, markup_result);
+  EXPECT_EQ(url, url_result);
+  clipboard.ReadText(&text_result);
+  EXPECT_EQ(text, text_result);
+  clipboard.ReadAsciiText(&ascii_text);
+  EXPECT_EQ(WideToUTF8(text), ascii_text);
+}
+
+TEST(ClipboardTest, WebSmartPasteTest) {
+  Clipboard clipboard;
+
+  clipboard.Clear();
+  clipboard.WriteWebSmartPaste();
+  EXPECT_EQ(true, clipboard.IsFormatAvailable(
+      ClipboardUtil::GetWebKitSmartPasteFormat()->cfFormat));
+}
+
+TEST(ClipboardTest, BitmapTest) {
+  unsigned int fake_bitmap[] = {
+    0x46155189, 0xF6A55C8D, 0x79845674, 0xFA57BD89,
+    0x78FD46AE, 0x87C64F5A, 0x36EDC5AF, 0x4378F568,
+    0x91E9F63A, 0xC31EA14F, 0x69AB32DF, 0x643A3FD1,
+  };
+
+  Clipboard clipboard;
+
+  clipboard.Clear();
+  clipboard.WriteBitmap(fake_bitmap, gfx::Size(3, 4));
+  EXPECT_EQ(true, clipboard.IsFormatAvailable(CF_BITMAP));
+}
+
+// Files for this test don't actually need to exist on the file system, just
+// don't try to use a non-existent file you've retrieved from the clipboard.
+TEST(ClipboardTest, FileTest) {
+  Clipboard clipboard;
+  clipboard.Clear();
+
+  std::wstring file = L"C:\\Downloads\\My Downloads\\A Special File.txt";
+  clipboard.WriteFile(file);
+  std::wstring out_file;
+  clipboard.ReadFile(&out_file);
+  EXPECT_EQ(file, out_file);
+}
+
+TEST(ClipboardTest, MultipleFilesTest) {
+  Clipboard clipboard;
+  clipboard.Clear();
+
+  std::vector<std::wstring> files;
+  files.push_back(L"C:\\Downloads\\My Downloads\\File 1.exe");
+  files.push_back(L"C:\\Downloads\\My Downloads\\File 2.pdf");
+  files.push_back(L"C:\\Downloads\\My Downloads\\File 3.doc");
+  clipboard.WriteFiles(files);
+
+  std::vector<std::wstring> out_files;
+  clipboard.ReadFiles(&out_files);
+
+  EXPECT_EQ(files.size(), out_files.size());
+  for (size_t i = 0; i < out_files.size(); ++i)
+    EXPECT_EQ(files[i], out_files[i]);
+}
diff --git a/base/clipboard_util.cc b/base/clipboard_util.cc
new file mode 100644
index 0000000..3042e76
--- /dev/null
+++ b/base/clipboard_util.cc
@@ -0,0 +1,400 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/clipboard_util.h"
+
+#include <shellapi.h>
+#include <shlwapi.h>
+#include <wininet.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/scoped_handle.h"
+#include "base/string_util.h"
+
+namespace {
+
+bool GetUrlFromHDrop(IDataObject* data_object, std::wstring* url,
+                     std::wstring* title) {
+  DCHECK(data_object && url && title);
+
+  STGMEDIUM medium;
+  if (FAILED(data_object->GetData(ClipboardUtil::GetCFHDropFormat(), &medium)))
+    return false;
+
+  HDROP hdrop = static_cast<HDROP>(GlobalLock(medium.hGlobal));
+
+  if (!hdrop)
+    return false;
+
+  bool success = false;
+  wchar_t filename[MAX_PATH];
+  if (DragQueryFileW(hdrop, 0, filename, arraysize(filename))) {
+    wchar_t url_buffer[INTERNET_MAX_URL_LENGTH];
+    if (0 == _wcsicmp(PathFindExtensionW(filename), L".url") &&
+        GetPrivateProfileStringW(L"InternetShortcut", L"url", 0, url_buffer,
+                                 arraysize(url_buffer), filename)) {
+      *url = url_buffer;
+      PathRemoveExtension(filename);
+      title->assign(PathFindFileName(filename));
+      success = true;
+    }
+  }
+
+  DragFinish(hdrop);
+  GlobalUnlock(medium.hGlobal);
+  // We don't need to call ReleaseStgMedium here because as far as I can tell,
+  // DragFinish frees the hGlobal for us.
+  return success;
+}
+
+bool SplitUrlAndTitle(const std::wstring& str, std::wstring* url,
+    std::wstring* title) {
+  DCHECK(url && title);
+  size_t newline_pos = str.find('\n');
+  bool success = false;
+  if (newline_pos != std::string::npos) {
+    *url = str.substr(0, newline_pos);
+    title->assign(str.substr(newline_pos + 1));
+    success = true;
+  } else {
+    *url = str;
+    title->assign(str);
+    success = true;
+  }
+  return success;
+}
+
+}  // namespace
+
+
+FORMATETC* ClipboardUtil::GetUrlFormat() {
+  static UINT cf = RegisterClipboardFormat(CFSTR_INETURLA);
+  static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+  return &format;
+}
+
+FORMATETC* ClipboardUtil::GetUrlWFormat() {
+  static UINT cf = RegisterClipboardFormat(CFSTR_INETURLW);
+  static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+  return &format;
+}
+
+FORMATETC* ClipboardUtil::GetMozUrlFormat() {
+  // The format is "URL\nTitle"
+  static UINT cf = RegisterClipboardFormat(L"text/x-moz-url");
+  static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+  return &format;
+}
+
+FORMATETC* ClipboardUtil::GetPlainTextFormat() {
+  // We don't need to register this format since it's a built in format.
+  static FORMATETC format = {CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+  return &format;
+}
+
+FORMATETC* ClipboardUtil::GetPlainTextWFormat() {
+  // We don't need to register this format since it's a built in format.
+  static FORMATETC format = {CF_UNICODETEXT, 0, DVASPECT_CONTENT, -1,
+                             TYMED_HGLOBAL};
+  return &format;
+}
+
+FORMATETC* ClipboardUtil::GetFilenameWFormat() {
+  static UINT cf = RegisterClipboardFormat(CFSTR_FILENAMEW);
+  static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+  return &format;
+}
+
+FORMATETC* ClipboardUtil::GetFilenameFormat()
+{
+  static UINT cf = RegisterClipboardFormat(CFSTR_FILENAMEA);
+  static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+  return &format;
+}
+
+FORMATETC* ClipboardUtil::GetHtmlFormat() {
+  static UINT cf = RegisterClipboardFormat(L"HTML Format");
+  static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+  return &format;
+}
+
+FORMATETC* ClipboardUtil::GetTextHtmlFormat() {
+  static UINT cf = RegisterClipboardFormat(L"text/html");
+  static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+  return &format;
+}
+
+FORMATETC* ClipboardUtil::GetCFHDropFormat() {
+  // We don't need to register this format since it's a built in format.
+  static FORMATETC format = {CF_HDROP, 0, DVASPECT_CONTENT, -1,
+                             TYMED_HGLOBAL};
+  return &format;
+}
+
+FORMATETC* ClipboardUtil::GetFileDescriptorFormat() {
+  static UINT cf = RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR);
+  static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+  return &format;
+}
+
+FORMATETC* ClipboardUtil::GetFileContentFormatZero() {
+  static UINT cf = RegisterClipboardFormat(CFSTR_FILECONTENTS);
+  static FORMATETC format = {cf, 0, DVASPECT_CONTENT, 0, TYMED_HGLOBAL};
+  return &format;
+}
+
+FORMATETC* ClipboardUtil::GetWebKitSmartPasteFormat() {
+  static UINT cf = RegisterClipboardFormat(L"WebKit Smart Paste Format");
+  static FORMATETC format = {cf, 0, DVASPECT_CONTENT, 0, TYMED_HGLOBAL};
+  return &format;
+}
+
+
+bool ClipboardUtil::HasUrl(IDataObject* data_object) {
+  DCHECK(data_object);
+  return SUCCEEDED(data_object->QueryGetData(GetMozUrlFormat())) ||
+      SUCCEEDED(data_object->QueryGetData(GetUrlWFormat())) ||
+      SUCCEEDED(data_object->QueryGetData(GetUrlFormat())) ||
+      SUCCEEDED(data_object->QueryGetData(GetFilenameWFormat())) ||
+      SUCCEEDED(data_object->QueryGetData(GetFilenameFormat()));
+}
+
+bool ClipboardUtil::HasFilenames(IDataObject* data_object) {
+  DCHECK(data_object);
+  return SUCCEEDED(data_object->QueryGetData(GetCFHDropFormat()));
+}
+
+bool ClipboardUtil::HasPlainText(IDataObject* data_object) {
+  DCHECK(data_object);
+  return SUCCEEDED(data_object->QueryGetData(GetPlainTextWFormat())) ||
+      SUCCEEDED(data_object->QueryGetData(GetPlainTextFormat()));
+}
+
+
+bool ClipboardUtil::GetUrl(IDataObject* data_object,
+    std::wstring* url, std::wstring* title) {
+  DCHECK(data_object && url && title);
+  if (!HasUrl(data_object))
+    return false;
+
+  // Try to extract a URL from |data_object| in a variety of formats.
+  STGMEDIUM store;
+  if (GetUrlFromHDrop(data_object, url, title)) {
+    return true;
+  }
+
+  if (SUCCEEDED(data_object->GetData(GetMozUrlFormat(), &store)) ||
+      SUCCEEDED(data_object->GetData(GetUrlWFormat(), &store))) {
+    // Mozilla URL format or unicode URL
+    ScopedHGlobal<wchar_t> data(store.hGlobal);
+    bool success = SplitUrlAndTitle(data.get(), url, title);
+    ReleaseStgMedium(&store);
+    if (success)
+      return true;
+  }
+
+  if (SUCCEEDED(data_object->GetData(GetUrlFormat(), &store))) {
+    // URL using ascii
+    ScopedHGlobal<char> data(store.hGlobal);
+    bool success = SplitUrlAndTitle(UTF8ToWide(data.get()), url, title);
+    ReleaseStgMedium(&store);
+    if (success)
+      return true;
+  }
+
+  if (SUCCEEDED(data_object->GetData(GetFilenameWFormat(), &store))) {
+    // filename using unicode
+    ScopedHGlobal<wchar_t> data(store.hGlobal);
+    bool success = false;
+    if (data.get() && data.get()[0] && (PathFileExists(data.get()) ||
+                                        PathIsUNC(data.get()))) {
+      wchar_t file_url[INTERNET_MAX_URL_LENGTH];
+      DWORD file_url_len = sizeof(file_url) / sizeof(file_url[0]);
+      if (SUCCEEDED(::UrlCreateFromPathW(data.get(), file_url, &file_url_len,
+                                         0))) {
+        *url = file_url;
+        title->assign(file_url);
+        success = true;
+      }
+    }
+    ReleaseStgMedium(&store);
+    if (success)
+      return true;
+  }
+
+  if (SUCCEEDED(data_object->GetData(GetFilenameFormat(), &store))) {
+    // filename using ascii
+    ScopedHGlobal<char> data(store.hGlobal);
+    bool success = false;
+    if (data.get() && data.get()[0] && (PathFileExistsA(data.get()) ||
+                                        PathIsUNCA(data.get()))) {
+      char file_url[INTERNET_MAX_URL_LENGTH];
+      DWORD file_url_len = sizeof(file_url) / sizeof(file_url[0]);
+      if (SUCCEEDED(::UrlCreateFromPathA(data.get(), file_url, &file_url_len, 0))) {
+        *url = UTF8ToWide(file_url);
+        title->assign(*url);
+        success = true;
+      }
+    }
+    ReleaseStgMedium(&store);
+    if (success)
+      return true;
+  }
+
+  return false;
+}
+
+bool ClipboardUtil::GetFilenames(IDataObject* data_object,
+                                 std::vector<std::wstring>* filenames) {
+  DCHECK(data_object && filenames);
+  if (!HasFilenames(data_object))
+    return false;
+
+  STGMEDIUM medium;
+  if (FAILED(data_object->GetData(GetCFHDropFormat(), &medium)))
+    return false;
+
+  HDROP hdrop = static_cast<HDROP>(GlobalLock(medium.hGlobal));
+  if (!hdrop)
+    return false;
+
+  const int kMaxFilenameLen = 4096;
+  const unsigned num_files = DragQueryFileW(hdrop, 0xffffffff, 0, 0);
+  for (unsigned int i = 0; i < num_files; ++i) {
+    wchar_t filename[kMaxFilenameLen];
+    if (!DragQueryFileW(hdrop, i, filename, kMaxFilenameLen))
+      continue;
+    filenames->push_back(filename);
+  }
+
+  DragFinish(hdrop);
+  GlobalUnlock(medium.hGlobal);
+  // We don't need to call ReleaseStgMedium here because as far as I can tell,
+  // DragFinish frees the hGlobal for us.
+  return true;
+}
+
+bool ClipboardUtil::GetPlainText(IDataObject* data_object,
+                                 std::wstring* plain_text) {
+  DCHECK(data_object && plain_text);
+  if (!HasPlainText(data_object))
+    return false;
+
+  STGMEDIUM store;
+  bool success = false;
+  if (SUCCEEDED(data_object->GetData(GetPlainTextWFormat(), &store))) {
+    // Unicode text
+    ScopedHGlobal<wchar_t> data(store.hGlobal);
+    plain_text->assign(data.get());
+    ReleaseStgMedium(&store);
+    success = true;
+  } else if (SUCCEEDED(data_object->GetData(GetPlainTextFormat(), &store))) {
+    // ascii text
+    ScopedHGlobal<char> data(store.hGlobal);
+    plain_text->assign(UTF8ToWide(data.get()));
+    ReleaseStgMedium(&store);
+    success = true;
+  } else {
+    //If a file is dropped on the window, it does not provide either of the
+    //plain text formats, so here we try to forcibly get a url.
+    std::wstring title;
+    success = GetUrl(data_object, plain_text, &title);
+  }
+
+  return success;
+}
+
+bool ClipboardUtil::GetCFHtml(IDataObject* data_object,
+                              std::wstring* cf_html) {
+  DCHECK(data_object && cf_html);
+  if (FAILED(data_object->QueryGetData(GetHtmlFormat())))
+    return false;
+
+  STGMEDIUM store;
+  if (FAILED(data_object->GetData(GetHtmlFormat(), &store)))
+    return false;
+
+  // MS CF html
+  ScopedHGlobal<char> data(store.hGlobal);
+  cf_html->assign(UTF8ToWide(std::string(data.get(), data.Size())));
+  ReleaseStgMedium(&store);
+  return true;
+}
+
+bool ClipboardUtil::GetTextHtml(IDataObject* data_object,
+                                std::wstring* text_html) {
+  DCHECK(data_object && text_html);
+  if (FAILED(data_object->QueryGetData(GetTextHtmlFormat())))
+    return false;
+
+  STGMEDIUM store;
+  if (FAILED(data_object->GetData(GetTextHtmlFormat(), &store)))
+    return false;
+
+  // raw html
+  ScopedHGlobal<wchar_t> data(store.hGlobal);
+  text_html->assign(data.get());
+  ReleaseStgMedium(&store);
+  return true;
+}
+
+bool ClipboardUtil::GetFileContents(IDataObject* data_object,
+    std::wstring* filename, std::string* file_contents) {
+  DCHECK(data_object && filename && file_contents);
+  bool has_data =
+      SUCCEEDED(data_object->QueryGetData(GetFileContentFormatZero())) ||
+      SUCCEEDED(data_object->QueryGetData(GetFileDescriptorFormat()));
+
+  if (!has_data)
+    return false;
+
+  STGMEDIUM content;
+  // The call to GetData can be very slow depending on what is in
+  // |data_object|.
+  if (SUCCEEDED(data_object->GetData(GetFileContentFormatZero(), &content))) {
+    if (TYMED_HGLOBAL == content.tymed) {
+      ScopedHGlobal<char> data(content.hGlobal);
+      // The size includes the trailing NULL byte.  We don't want it.
+      file_contents->assign(data.get(), data.Size() - 1);
+    }
+    ReleaseStgMedium(&content);
+  }
+
+  STGMEDIUM description;
+  if (SUCCEEDED(data_object->GetData(GetFileDescriptorFormat(),
+                                     &description))) {
+    ScopedHGlobal<FILEGROUPDESCRIPTOR> fgd(description.hGlobal);
+    // We expect there to be at least one file in here.
+    DCHECK(fgd->cItems >= 1);
+    filename->assign(fgd->fgd[0].cFileName);
+    ReleaseStgMedium(&description);
+  }
+  return true;
+}
diff --git a/base/clipboard_util.h b/base/clipboard_util.h
new file mode 100644
index 0000000..8daffbb
--- /dev/null
+++ b/base/clipboard_util.h
@@ -0,0 +1,76 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Some helper functions for working with the clipboard and IDataObjects.
+
+#include <shlobj.h>
+#include <string>
+#include <vector>
+
+class ClipboardUtil {
+ public:
+  /////////////////////////////////////////////////////////////////////////////
+  // Clipboard formats.
+  static FORMATETC* GetUrlFormat();
+  static FORMATETC* GetUrlWFormat();
+  static FORMATETC* GetMozUrlFormat();
+  static FORMATETC* GetPlainTextFormat();
+  static FORMATETC* GetPlainTextWFormat();
+  static FORMATETC* GetFilenameFormat();
+  static FORMATETC* GetFilenameWFormat();
+  // MS HTML Format
+  static FORMATETC* GetHtmlFormat();
+  // Firefox text/html
+  static FORMATETC* GetTextHtmlFormat();
+  static FORMATETC* GetCFHDropFormat();
+  static FORMATETC* GetFileDescriptorFormat();
+  static FORMATETC* GetFileContentFormatZero();
+  static FORMATETC* GetWebKitSmartPasteFormat();
+
+  /////////////////////////////////////////////////////////////////////////////
+  // These methods check to see if |data_object| has the requested type.
+  // Returns true if it does.
+  static bool HasUrl(IDataObject* data_object);
+  static bool HasFilenames(IDataObject* data_object);
+  static bool HasPlainText(IDataObject* data_object);
+
+  /////////////////////////////////////////////////////////////////////////////
+  // Helper methods to extract information from an IDataObject.  These methods
+  // return true if the requested data type is found in |data_object|.
+  static bool GetUrl(IDataObject* data_object,
+      std::wstring* url, std::wstring* title);
+  static bool GetFilenames(IDataObject* data_object,
+                           std::vector<std::wstring>* filenames);
+  static bool GetPlainText(IDataObject* data_object, std::wstring* plain_text);
+  static bool GetCFHtml(IDataObject* data_object, std::wstring* cf_html);
+  static bool GetTextHtml(IDataObject* data_object, std::wstring* text_html);
+  static bool GetFileContents(IDataObject* data_object,
+                              std::wstring* filename,
+                              std::string* file_contents);
+};
diff --git a/base/command_line.cc b/base/command_line.cc
new file mode 100644
index 0000000..7755578
--- /dev/null
+++ b/base/command_line.cc
@@ -0,0 +1,246 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+#include <shellapi.h>
+
+#include <algorithm>
+
+#include "base/command_line.h"
+
+#include "base/logging.h"
+#include "base/singleton.h"
+#include "base/string_util.h"
+
+using namespace std;
+
+// Since we use a lazy match, make sure that longer versions (like L"--")
+// are listed before shorter versions (like L"-") of similar prefixes.
+const wchar_t* const CommandLine::kSwitchPrefixes[] = {L"--", L"-", L"/"};
+
+const wchar_t CommandLine::kSwitchValueSeparator[] = L"=";
+
+static void Lowercase(wstring* parameter) {
+  transform(parameter->begin(), parameter->end(), parameter->begin(), tolower);
+}
+
+// CommandLine::Data
+//
+// This object holds the parsed data for a command line.  We hold this in a
+// separate object from |CommandLine| so that we can share the parsed data
+// across multiple |CommandLine| objects.  When we share |Data|, we might be
+// accessing this object on multiple threads.  To ensure thread safety, the
+// public interface of this object is const only.
+//
+// Do NOT add any non-const methods to this object.  You have been warned.
+class CommandLine::Data {
+ public:
+  Data() {
+    Init(GetCommandLineW());
+  }
+
+  Data(const wstring& command_line) {
+    Init(command_line);
+  }
+
+  const std::wstring& command_line_string() const {
+    return command_line_string_;
+  }
+
+  const std::wstring& program() const {
+    return program_;
+  }
+
+  const std::map<std::wstring, std::wstring>& switches() const {
+    return switches_;
+  }
+
+  const std::vector<std::wstring>& loose_values() const {
+    return loose_values_;
+  }
+
+ private:
+  // Returns true if parameter_string represents a switch.  If true,
+  // switch_string and switch_value are set.  (If false, both are
+  // set to the empty string.)
+  static bool IsSwitch(const wstring& parameter_string,
+                       wstring* switch_string,
+                       wstring* switch_value) {
+
+    *switch_string = L"";
+    *switch_value = L"";
+
+    for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) {
+      std::wstring prefix(kSwitchPrefixes[i]);
+      if (parameter_string.find(prefix) != 0)  // check prefix
+        continue;
+
+      const size_t switch_start = prefix.length();
+      const size_t equals_position = parameter_string.find(
+          kSwitchValueSeparator, switch_start);
+      if (equals_position == wstring::npos) {
+        *switch_string = parameter_string.substr(switch_start);
+      } else {
+        *switch_string = parameter_string.substr(
+            switch_start, equals_position - switch_start);
+        *switch_value = parameter_string.substr(equals_position + 1);
+      }
+      Lowercase(switch_string);
+
+      return true;
+    }
+
+    return false;
+  }
+
+  // Does the actual parsing of the command line.
+  void Init(const std::wstring& command_line) {
+    TrimWhitespace(command_line, TRIM_ALL, &command_line_string_);
+
+    if (command_line_string_.empty())
+      return;
+
+    int num_args = 0;
+    wchar_t** args = NULL;
+
+    args = CommandLineToArgvW(command_line_string_.c_str(), &num_args);
+
+    // Populate program_ with the trimmed version of the first arg.
+    TrimWhitespace(args[0], TRIM_ALL, &program_);
+
+    for (int i = 1; i < num_args; ++i) {
+      wstring arg;
+      TrimWhitespace(args[i], TRIM_ALL, &arg);
+
+      wstring switch_string;
+      wstring switch_value;
+      if (IsSwitch(arg, &switch_string, &switch_value)) {
+        switches_[switch_string] = switch_value;
+      } else {
+        loose_values_.push_back(arg);
+      }
+    }
+
+    if (args)
+      LocalFree(args);
+  }
+
+  std::wstring command_line_string_;
+  std::wstring program_;
+  std::map<std::wstring, std::wstring> switches_;
+  std::vector<std::wstring> loose_values_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(CommandLine::Data);
+};
+
+CommandLine::CommandLine()
+    : we_own_data_(false),  // The Singleton class will manage it for us.
+      data_(Singleton<Data>::get()) {
+}
+
+CommandLine::CommandLine(const wstring& command_line)
+    : we_own_data_(true),
+      data_(new Data(command_line)) {
+}
+
+CommandLine::~CommandLine() {
+  if (we_own_data_)
+    delete data_;
+}
+
+bool CommandLine::HasSwitch(const wstring& switch_string) const {
+  wstring lowercased_switch(switch_string);
+  Lowercase(&lowercased_switch);
+  return data_->switches().find(lowercased_switch) != data_->switches().end();
+}
+
+wstring CommandLine::GetSwitchValue(const wstring& switch_string) const {
+  wstring lowercased_switch(switch_string);
+  Lowercase(&lowercased_switch);
+
+  const map<wstring, wstring>::const_iterator result =
+    data_->switches().find(lowercased_switch);
+
+  if (result == data_->switches().end()) {
+    return L"";
+  } else {
+    return result->second;
+  }
+}
+
+size_t CommandLine::GetLooseValueCount() const {
+  return data_->loose_values().size();
+}
+
+CommandLine::LooseValueIterator CommandLine::GetLooseValuesBegin() const {
+  return data_->loose_values().begin();
+}
+
+CommandLine::LooseValueIterator CommandLine::GetLooseValuesEnd() const {
+  return data_->loose_values().end();
+}
+
+std::wstring CommandLine::command_line_string() const {
+  return data_->command_line_string();
+}
+
+std::wstring CommandLine::program() const {
+  return data_->program();
+}
+
+// static
+void CommandLine::AppendSwitch(wstring* command_line_string,
+                               const wstring& switch_string) {
+  DCHECK(command_line_string);
+  command_line_string->append(L" ");
+  command_line_string->append(kSwitchPrefixes[0]);
+  command_line_string->append(switch_string);
+}
+
+// static
+void CommandLine::AppendSwitchWithValue(wstring* command_line_string,
+                                        const wstring& switch_string,
+                                        const wstring& value_string) {
+  AppendSwitch(command_line_string, switch_string);
+
+  if (value_string.empty())
+    return;
+
+  command_line_string->append(kSwitchValueSeparator);
+  // NOTE(jhughes): If the value contains a quotation mark at one
+  //                end but not both, you may get unusable output.
+  if ((value_string.find(L" ") != std::wstring::npos) &&
+      (value_string[0] != L'"') &&
+      (value_string[value_string.length() - 1] != L'"')) {
+    // need to provide quotes
+    StringAppendF(command_line_string, L"\"%s\"", value_string.c_str());
+  } else {
+    command_line_string->append(value_string);
+  }
+}
diff --git a/base/command_line.h b/base/command_line.h
new file mode 100644
index 0000000..f1b7ade
--- /dev/null
+++ b/base/command_line.h
@@ -0,0 +1,119 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// This file contains a class that can be used to extract the salient
+// elements of a command line in a relatively lightweight manner.
+// Switches can optionally have a value attached using an equals sign,
+// as in "-switch=value".  Arguments that aren't prefixed with a
+// switch prefix are considered "loose parameters".  Switch names
+// are case-insensitive.
+
+#ifndef BASE_COMMAND_LINE_H__
+#define BASE_COMMAND_LINE_H__
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+
+class CommandLine {
+ public:
+  // Creates a parsed version of the command line used to launch
+  // the current process.
+  CommandLine();
+
+  // Creates a parsed version of the given command-line string.
+  // The program name is assumed to be the first item in the string.
+  CommandLine(const std::wstring& command_line);
+
+  ~CommandLine();
+
+  // Returns true if this command line contains the given switch.
+  // (Switch names are case-insensitive.)
+  bool HasSwitch(const std::wstring& switch_string) const;
+
+  // Returns the value associated with the given switch.  If the
+  // switch has no value or isn't present, this method returns
+  // the empty string.
+  std::wstring GetSwitchValue(const std::wstring& switch_string) const;
+
+  // Returns the number of "loose values" found in the command line.
+  // Loose values are arguments that aren't switches.
+  // (The program name is also excluded from the set of loose values.)
+  size_t GetLooseValueCount() const;
+
+  typedef std::vector<std::wstring>::const_iterator LooseValueIterator;
+
+  // Returns a const_iterator to the list of loose values.
+  LooseValueIterator GetLooseValuesBegin() const;
+
+  // Returns the end const_iterator for the list of loose values.
+  LooseValueIterator GetLooseValuesEnd() const;
+
+  // Simply returns the original command line string.
+  std::wstring command_line_string() const;
+
+  // Returns the program part of the command line string (the first item).
+  std::wstring program() const;
+
+  // An array containing the prefixes that identify an argument as
+  // a switch.
+  static const wchar_t* const kSwitchPrefixes[];
+
+  // The string that's used to separate switches from their values.
+  static const wchar_t kSwitchValueSeparator[];
+
+  // Appends the given switch string (preceded by a space and a switch
+  // prefix) to the given string.
+  static void AppendSwitch(std::wstring* command_line_string,
+                           const std::wstring& switch_string);
+
+  // Appends the given switch string (preceded by a space and a switch
+  // prefix) to the given string, with the given value attached.
+  static void AppendSwitchWithValue(std::wstring* command_line_string,
+                                    const std::wstring& switch_string,
+                                    const std::wstring& value_string);
+
+ private:
+  class Data;
+
+  // True if we are responsible for deleting our |data_| pointer.  In some cases
+  // we cache the result of parsing the command line and |data_|'s lifetime is
+  // managed by someone else (e.g., the |Singleton| class).
+  bool we_own_data_;
+
+  // A pointer to the parsed version of the command line.
+  Data* data_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(CommandLine);
+};
+
+#endif  // BASE_COMMAND_LINE_H__
diff --git a/base/command_line_unittest.cc b/base/command_line_unittest.cc
new file mode 100644
index 0000000..dbcf06f
--- /dev/null
+++ b/base/command_line_unittest.cc
@@ -0,0 +1,125 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <string>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+  class CommandLineTest : public testing::Test {
+  };
+};
+
+TEST(CommandLineTest, CommandLineConstructor) {
+  CommandLine cl(L"program --foo= -bAr  /Spaetzel=pierogi /Baz flim "
+                 L"--other-switches=\"--dog=canine --cat=feline\" "
+                 L"-spaetzle=Crepe   -=loosevalue  flan "
+                 L"--input-translation=\"45\"--output-rotation "
+                 L"\"in the time of submarines...\"");
+
+  EXPECT_FALSE(cl.command_line_string().empty());
+  EXPECT_FALSE(cl.HasSwitch(L"cruller"));
+  EXPECT_FALSE(cl.HasSwitch(L"flim"));
+  EXPECT_FALSE(cl.HasSwitch(L"program"));
+  EXPECT_FALSE(cl.HasSwitch(L"dog"));
+  EXPECT_FALSE(cl.HasSwitch(L"cat"));
+  EXPECT_FALSE(cl.HasSwitch(L"output-rotation"));
+
+  EXPECT_EQ(L"program", cl.program());
+
+  EXPECT_TRUE(cl.HasSwitch(L"foo"));
+  EXPECT_TRUE(cl.HasSwitch(L"bar"));
+  EXPECT_TRUE(cl.HasSwitch(L"baz"));
+  EXPECT_TRUE(cl.HasSwitch(L"spaetzle"));
+  EXPECT_TRUE(cl.HasSwitch(L"SPAETZLE"));
+  EXPECT_TRUE(cl.HasSwitch(L"other-switches"));
+  EXPECT_TRUE(cl.HasSwitch(L"input-translation"));
+
+  EXPECT_EQ(L"Crepe", cl.GetSwitchValue(L"spaetzle"));
+  EXPECT_EQ(L"", cl.GetSwitchValue(L"Foo"));
+  EXPECT_EQ(L"", cl.GetSwitchValue(L"bar"));
+  EXPECT_EQ(L"", cl.GetSwitchValue(L"cruller"));
+  EXPECT_EQ(L"--dog=canine --cat=feline", cl.GetSwitchValue(L"other-switches"));
+  EXPECT_EQ(L"45--output-rotation", cl.GetSwitchValue(L"input-translation"));
+
+  EXPECT_EQ(3, cl.GetLooseValueCount());
+
+  CommandLine::LooseValueIterator iter = cl.GetLooseValuesBegin();
+  EXPECT_EQ(L"flim", *iter);
+  ++iter;
+  EXPECT_EQ(L"flan", *iter);
+  ++iter;
+  EXPECT_EQ(L"in the time of submarines...", *iter);
+  ++iter;
+  EXPECT_TRUE(iter == cl.GetLooseValuesEnd());
+}
+
+// These test the command line used to invoke the unit test.
+TEST(CommandLineTest, DefaultConstructor) {
+  CommandLine cl;
+  EXPECT_FALSE(cl.command_line_string().empty());
+  EXPECT_FALSE(cl.program().empty());
+}
+
+// Tests behavior with an empty input string.
+TEST(CommandLineTest, EmptyString) {
+  CommandLine cl(L"");
+  EXPECT_TRUE(cl.command_line_string().empty());
+  EXPECT_TRUE(cl.program().empty());
+  EXPECT_EQ(0, cl.GetLooseValueCount());
+}
+
+// Test static functions for appending switches to a command line.
+TEST(CommandLineTest, AppendSwitches) {
+  std::wstring cl_string = L"Program";
+  std::wstring switch1 = L"switch1";
+  std::wstring switch2 = L"switch2";
+  std::wstring value = L"value";
+  std::wstring switch3 = L"switch3";
+  std::wstring value3 = L"a value with spaces";
+  std::wstring switch4 = L"switch4";
+  std::wstring value4 = L"\"a value with quotes\"";
+
+  CommandLine::AppendSwitch(&cl_string, switch1);
+  CommandLine::AppendSwitchWithValue(&cl_string, switch2, value);
+  CommandLine::AppendSwitchWithValue(&cl_string, switch3, value3);
+  CommandLine::AppendSwitchWithValue(&cl_string, switch4, value4);
+  CommandLine cl(cl_string);
+
+  EXPECT_TRUE(cl.HasSwitch(switch1));
+  EXPECT_TRUE(cl.HasSwitch(switch2));
+  EXPECT_EQ(value, cl.GetSwitchValue(switch2));
+  EXPECT_TRUE(cl.HasSwitch(switch3));
+  EXPECT_EQ(value3, cl.GetSwitchValue(switch3));
+  EXPECT_TRUE(cl.HasSwitch(switch2));
+  EXPECT_EQ(value4.substr(1, value4.length() - 2), cl.GetSwitchValue(switch4));
+}
diff --git a/base/condition_variable.cc b/base/condition_variable.cc
new file mode 100644
index 0000000..0e4f7c8
--- /dev/null
+++ b/base/condition_variable.cc
@@ -0,0 +1,464 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/condition_variable.h"
+
+#include <stack>
+
+#include "base/lock.h"
+#include "base/logging.h"
+
+ConditionVariable::ConditionVariable(Lock* user_lock)
+  : user_lock_(*user_lock),
+    run_state_(RUNNING),
+    allocation_counter_(0),
+    recycling_list_size_(0) {
+  DCHECK(user_lock);
+}
+
+ConditionVariable::~ConditionVariable() {
+  AutoLock auto_lock(internal_lock_);
+  run_state_ = SHUTDOWN;  // Prevent any more waiting.
+
+  DCHECK_EQ(recycling_list_size_, allocation_counter_);
+  if (recycling_list_size_ != allocation_counter_) {  // Rare shutdown problem.
+    // There are threads of execution still in this->TimedWait() and yet the
+    // caller has instigated the destruction of this instance :-/.
+    // A common reason for such "overly hasty" destruction is that the caller
+    // was not willing to wait for all the threads to terminate.  Such hasty
+    // actions are a violation of our usage contract, but we'll give the
+    // waiting thread(s) one last chance to exit gracefully (prior to our
+    // destruction).
+    // Note: waiting_list_ *might* be empty, but recycling is still pending.
+    AutoUnlock auto_unlock(internal_lock_);
+    Broadcast();  // Make sure all waiting threads have been signaled.
+    Sleep(10);  // Give threads a chance to grab internal_lock_.
+    // All contained threads should be blocked on user_lock_ by now :-).
+  }  // Reacquire internal_lock_.
+
+  DCHECK_EQ(recycling_list_size_, allocation_counter_);
+}
+
+// Wait() atomically releases the caller's lock as it starts to Wait, and then
+// reacquires it when it is signaled.
+void ConditionVariable::TimedWait(const TimeDelta& max_time) {
+  Event* waiting_event;
+  HANDLE handle;
+  {
+    AutoLock auto_lock(internal_lock_);
+    if (RUNNING != run_state_) return;  // Destruction in progress.
+    waiting_event = GetEventForWaiting();
+    handle = waiting_event->handle();
+    DCHECK(handle);
+  }  // Release internal_lock.
+
+  {
+    AutoUnlock unlock(user_lock_);  // Release caller's lock
+    WaitForSingleObject(handle, static_cast<DWORD>(max_time.InMilliseconds()));
+    // Minimize spurious signal creation window by recycling asap.
+    AutoLock auto_lock(internal_lock_);
+    RecycleEvent(waiting_event);
+    // Release internal_lock_
+  }  // Reacquire callers lock to depth at entry.
+}
+
+// Broadcast() is guaranteed to signal all threads that were waiting (i.e., had
+// a cv_event internally allocated for them) before Broadcast() was called.
+void ConditionVariable::Broadcast() {
+  std::stack<HANDLE> handles;  // See FAQ-question-10.
+  {
+    AutoLock auto_lock(internal_lock_);
+    if (waiting_list_.IsEmpty())
+      return;
+    while (!waiting_list_.IsEmpty())
+      // This is not a leak from waiting_list_.  See FAQ-question 12.
+      handles.push(waiting_list_.PopBack()->handle());
+  }  // Release internal_lock_.
+  while (!handles.empty()) {
+    SetEvent(handles.top());
+    handles.pop();
+  }
+}
+
+// Signal() will select one of the waiting threads, and signal it (signal its
+// cv_event).  For better performance we signal the thread that went to sleep
+// most recently (LIFO).  If we want fairness, then we wake the thread that has
+// been sleeping the longest (FIFO).
+void ConditionVariable::Signal() {
+  HANDLE handle;
+  {
+    AutoLock auto_lock(internal_lock_);
+    if (waiting_list_.IsEmpty())
+      return;  // No one to signal.
+    // Only performance option should be used.
+    // This is not a leak from waiting_list.  See FAQ-question 12.
+     handle = waiting_list_.PopBack()->handle();  // LIFO.
+  }  // Release internal_lock_.
+  SetEvent(handle);
+}
+
+// GetEventForWaiting() provides a unique cv_event for any caller that needs to
+// wait.  This means that (worst case) we may over time create as many cv_event
+// objects as there are threads simultaneously using this instance's Wait()
+// functionality.
+ConditionVariable::Event* ConditionVariable::GetEventForWaiting() {
+  // We hold internal_lock, courtesy of Wait().
+  Event* cv_event;
+  if (0 == recycling_list_size_) {
+    DCHECK(recycling_list_.IsEmpty());
+    cv_event = new Event();
+    cv_event->InitListElement();
+    allocation_counter_++;
+    // CHECK_NE is not defined in our codebase, so we have to use CHECK
+    CHECK(cv_event->handle());
+  } else {
+    cv_event = recycling_list_.PopFront();
+    recycling_list_size_--;
+  }
+  waiting_list_.PushBack(cv_event);
+  return cv_event;
+}
+
+// RecycleEvent() takes a cv_event that was previously used for Wait()ing, and
+// recycles it for use in future Wait() calls for this or other threads.
+// Note that there is a tiny chance that the cv_event is still signaled when we
+// obtain it, and that can cause spurious signals (if/when we re-use the
+// cv_event), but such is quite rare (see FAQ-question-5).
+void ConditionVariable::RecycleEvent(Event* used_event) {
+  // We hold internal_lock, courtesy of Wait().
+  // If the cv_event timed out, then it is necessary to remove it from
+  // waiting_list_.  If it was selected by Broadcast() or Signal(), then it is
+  // already gone.
+  used_event->Extract();  // Possibly redundant
+  recycling_list_.PushBack(used_event);
+  recycling_list_size_++;
+}
+//------------------------------------------------------------------------------
+// The next section provides the implementation for the private Event class.
+//------------------------------------------------------------------------------
+
+// Event provides a doubly-linked-list of events for use exclusively by the
+// ConditionVariable class.
+
+// This custom container was crafted because no simple combination of STL
+// classes appeared to support the functionality required.  The specific
+// unusual requirement for a linked-list-class is support for the Extract()
+// method, which can remove an element from a list, potentially for insertion
+// into a second list.  Most critically, the Extract() method is idempotent,
+// turning the indicated element into an extracted singleton whether it was
+// contained in a list or not.  This functionality allows one (or more) of
+// threads to do the extraction.  The iterator that identifies this extractable
+// element (in this case, a pointer to the list element) can be used after
+// arbitrary manipulation of the (possibly) enclosing list container.  In
+// general, STL containers do not provide iterators that can be used across
+// modifications (insertions/extractions) of the enclosing containers, and
+// certainly don't provide iterators that can be used if the identified
+// element is *deleted* (removed) from the container.
+
+// It is possible to use multiple redundant containers, such as an STL list,
+// and an STL map, to achieve similar container semantics.  This container has
+// only O(1) methods, while the corresponding (multiple) STL container approach
+// would have more complex O(log(N)) methods (yeah... N isn't that large).
+// Multiple containers also makes correctness more difficult to assert, as
+// data is redundantly stored and maintained, which is generally evil.
+
+ConditionVariable::Event::Event() : handle_(0) {
+  next_ = prev_ = this;  // Self referencing circular.
+}
+
+ConditionVariable::Event::~Event() {
+  if (0 == handle_) {
+    // This is the list holder
+    while (!IsEmpty()) {
+      Event* cv_event = PopFront();
+      DCHECK(cv_event->ValidateAsItem());
+      delete cv_event;
+    }
+  }
+  DCHECK(IsSingleton());
+  if (0 != handle_) {
+    int ret_val = CloseHandle(handle_);
+    DCHECK(ret_val);
+  }
+}
+
+// Change a container instance permanently into an element of a list.
+void ConditionVariable::Event::InitListElement() {
+  DCHECK(!handle_);
+  handle_ = CreateEvent(NULL, false, false, NULL);
+  CHECK(handle_);
+}
+
+// Methods for use on lists.
+bool ConditionVariable::Event::IsEmpty() const {
+  DCHECK(ValidateAsList());
+  return IsSingleton();
+}
+
+void ConditionVariable::Event::PushBack(Event* other) {
+  DCHECK(ValidateAsList());
+  DCHECK(other->ValidateAsItem());
+  DCHECK(other->IsSingleton());
+  // Prepare other for insertion.
+  other->prev_ = prev_;
+  other->next_ = this;
+  // Cut into list.
+  prev_->next_ = other;
+  prev_ = other;
+  DCHECK(ValidateAsDistinct(other));
+}
+
+ConditionVariable::Event* ConditionVariable::Event::PopFront() {
+  DCHECK(ValidateAsList());
+  DCHECK(!IsSingleton());
+  return next_->Extract();
+}
+
+ConditionVariable::Event* ConditionVariable::Event::PopBack() {
+  DCHECK(ValidateAsList());
+  DCHECK(!IsSingleton());
+  return prev_->Extract();
+}
+
+// Methods for use on list elements.
+// Accessor method.
+HANDLE ConditionVariable::Event::handle() const {
+  DCHECK(ValidateAsItem());
+  return handle_;
+}
+
+// Pull an element from a list (if it's in one).
+ConditionVariable::Event* ConditionVariable::Event::Extract() {
+  DCHECK(ValidateAsItem());
+  if (!IsSingleton()) {
+    // Stitch neighbors together.
+    next_->prev_ = prev_;
+    prev_->next_ = next_;
+    // Make extractee into a singleton.
+    prev_ = next_ = this;
+  }
+  DCHECK(IsSingleton());
+  return this;
+}
+
+// Method for use on a list element or on a list.
+bool ConditionVariable::Event::IsSingleton() const {
+  DCHECK(ValidateLinks());
+  return next_ == this;
+}
+
+// Provide pre/post conditions to validate correct manipulations.
+bool ConditionVariable::Event::ValidateAsDistinct(Event* other) const {
+  return ValidateLinks() && other->ValidateLinks() && (this != other);
+}
+
+bool ConditionVariable::Event::ValidateAsItem() const {
+  return (0 != handle_) && ValidateLinks();
+}
+
+bool ConditionVariable::Event::ValidateAsList() const {
+  return (0 == handle_) && ValidateLinks();
+}
+
+bool ConditionVariable::Event::ValidateLinks() const {
+  // Make sure both of our neighbors have links that point back to us.
+  // We don't do the O(n) check and traverse the whole loop, and instead only
+  // do a local check to (and returning from) our immediate neighbors.
+  return (next_->prev_ == this) && (prev_->next_ == this);
+}
+
+
+/*
+FAQ On subtle implementation details:
+
+1) What makes this problem subtle?  Please take a look at "Strategies
+for Implementing POSIX Condition Variables on Win32" by Douglas
+C. Schmidt and Irfan Pyarali.
+http://www.cs.wustl.edu/~schmidt/win32-cv-1.html It includes
+discussions of numerous flawed strategies for implementing this
+functionality.  I'm not convinced that even the final proposed
+implementation has semantics that are as nice as this implementation
+(especially with regard to Broadcast() and the impact on threads that
+try to Wait() after a Broadcast() has been called, but before all the
+original waiting threads have been signaled).
+
+2) Why can't you use a single wait_event for all threads that call
+Wait()?  See FAQ-question-1, or consider the following: If a single
+event were used, then numerous threads calling Wait() could release
+their cs locks, and be preempted just before calling
+WaitForSingleObject().  If a call to Broadcast() was then presented on
+a second thread, it would be impossible to actually signal all
+waiting(?) threads.  Some number of SetEvent() calls *could* be made,
+but there could be no guarantee that those led to to more than one
+signaled thread (SetEvent()'s may be discarded after the first!), and
+there could be no guarantee that the SetEvent() calls didn't just
+awaken "other" threads that hadn't even started waiting yet (oops).
+Without any limit on the number of requisite SetEvent() calls, the
+system would be forced to do many such calls, allowing many new waits
+to receive spurious signals.
+
+3) How does this implementation cause spurious signal events?  The
+cause in this implementation involves a race between a signal via
+time-out and a signal via Signal() or Broadcast().  The series of
+actions leading to this are:
+
+a) Timer fires, and a waiting thread exits the line of code:
+
+    WaitForSingleObject(waiting_event, max_time.InMilliseconds());
+
+b) That thread (in (a)) is randomly pre-empted after the above line,
+leaving the waiting_event reset (unsignaled) and still in the
+waiting_list_.
+
+c) A call to Signal() (or Broadcast()) on a second thread proceeds, and
+selects the waiting cv_event (identified in step (b)) as the event to revive
+via a call to SetEvent().
+
+d) The Signal() method (step c) calls SetEvent() on waiting_event (step b).
+
+e) The waiting cv_event (step b) is now signaled, but no thread is
+waiting on it.
+
+f) When that waiting_event (step b) is reused, it will immediately
+be signaled (spuriously).
+
+
+4) Why do you recycle events, and cause spurious signals?  First off,
+the spurious events are very rare.  They can only (I think) appear
+when the race described in FAQ-question-3 takes place.  This should be
+very rare.  Most(?)  uses will involve only timer expiration, or only
+Signal/Broadcast() actions.  When both are used, it will be rare that
+the race will appear, and it would require MANY Wait() and signaling
+activities.  If this implementation did not recycle events, then it
+would have to create and destroy events for every call to Wait().
+That allocation/deallocation and associated construction/destruction
+would be costly (per wait), and would only be a rare benefit (when the
+race was "lost" and a spurious signal took place). That would be bad
+(IMO) optimization trade-off.  Finally, such spurious events are
+allowed by the specification of condition variables (such as
+implemented in Vista), and hence it is better if any user accommodates
+such spurious events (see usage note in condition_variable.h).
+
+5) Why don't you reset events when you are about to recycle them, or
+about to reuse them, so that the spurious signals don't take place?
+The thread described in FAQ-question-3 step c may be pre-empted for an
+arbitrary length of time before proceeding to step d.  As a result,
+the wait_event may actually be re-used *before* step (e) is reached.
+As a result, calling reset would not help significantly.
+
+6) How is it that the callers lock is released atomically with the
+entry into a wait state?  We commit to the wait activity when we
+allocate the wait_event for use in a given call to Wait().  This
+allocation takes place before the caller's lock is released (and
+actually before our internal_lock_ is released).  That allocation is
+the defining moment when "the wait state has been entered," as that
+thread *can* now be signaled by a call to Broadcast() or Signal().
+Hence we actually "commit to wait" before releasing the lock, making
+the pair effectively atomic.
+
+8) Why do you need to lock your data structures during waiting, as the
+caller is already in possession of a lock?  We need to Acquire() and
+Release() our internal lock during Signal() and Broadcast().  If we tried
+to use a callers lock for this purpose, we might conflict with their
+external use of the lock.  For example, the caller may use to consistently
+hold a lock on one thread while calling Signal() on another, and that would
+block Signal().
+
+9) Couldn't a more efficient implementation be provided if you
+preclude using more than one external lock in conjunction with a
+single ConditionVariable instance?  Yes, at least it could be viewed
+as a simpler API (since you don't have to reiterate the lock argument
+in each Wait() call).  One of the constructors now takes a specific
+lock as an argument, and a there are corresponding Wait() calls that
+don't specify a lock now.  It turns that the resulting implmentation
+can't be made more efficient, as the internal lock needs to be used by
+Signal() and Broadcast(), to access internal data structures.  As a
+result, I was not able to utilize the user supplied lock (which is
+being used by the user elsewhere presumably) to protect the private
+member access.
+
+9) Since you have a second lock, how can be be sure that there is no
+possible deadlock scenario?  Our internal_lock_ is always the last
+lock acquired, and the first one released, and hence a deadlock (due
+to critical section problems) is impossible as a consequence of our
+lock.
+
+10) When doing a Broadcast(), why did you copy all the events into
+an STL queue, rather than making a linked-loop, and iterating over it?
+The iterating during Broadcast() is done so outside the protection
+of the internal lock. As a result, other threads, such as the thread
+wherein a related event is waiting, could asynchronously manipulate
+the links around a cv_event.  As a result, the link structure cannot
+be used outside a lock.  Broadcast() could iterate over waiting
+events by cycling in-and-out of the protection of the internal_lock,
+but that appears more expensive than copying the list into an STL
+stack.
+
+11) Why did the lock.h file need to be modified so much for this
+change?  Central to a Condition Variable is the atomic release of a
+lock during a Wait().  This places Wait() functionality exactly
+mid-way between the two classes, Lock and Condition Variable.  Given
+that there can be nested Acquire()'s of locks, and Wait() had to
+Release() completely a held lock, it was necessary to augment the Lock
+class with a recursion counter. Even more subtle is the fact that the
+recursion counter (in a Lock) must be protected, as many threads can
+access it asynchronously.  As a positive fallout of this, there are
+now some DCHECKS to be sure no one Release()s a Lock more than they
+Acquire()ed it, and there is ifdef'ed functionality that can detect
+nested locks (legal under windows, but not under Posix).
+
+12) Why is it that the cv_events removed from list in Broadcast() and Signal()
+are not leaked?  How are they recovered??  The cv_events that appear to leak are
+taken from the waiting_list_.  For each element in that list, there is currently
+a thread in or around the WaitForSingleObject() call of Wait(), and those
+threads have references to these otherwise leaked events. They are passed as
+arguments to be recycled just aftre returning from WaitForSingleObject().
+
+13) Why did you use a custom container class (the linked list), when STL has
+perfectly good containers, such as an STL list?  The STL list, as with any
+container, does not guarantee the utility of an iterator across manipulation
+(such as insertions and deletions) of the underlying container.  The custom
+double-linked-list container provided that assurance.  I don't believe any
+combination of STL containers provided the services that were needed at the same
+O(1) efficiency as the custom linked list.  The unusual requirement
+for the container class is that a reference to an item within a container (an
+iterator) needed to be maintained across an arbitrary manipulation of the
+container.  This requirement exposes itself in the Wait() method, where a
+waiting_event must be selected prior to the WaitForSingleObject(), and then it
+must be used as part of recycling to remove the related instance from the
+waiting_list.  A hash table (STL map) could be used, but I was embarrased to
+use a complex and relatively low efficiency container when a doubly linked list
+provided O(1) performance in all required operations.  Since other operations
+to provide performance-and/or-fairness required queue (FIFO) and list (LIFO)
+containers, I would also have needed to use an STL list/queue as well as an STL
+map.  In the end I decided it would be "fun" to just do it right, and I
+put so many assertions (DCHECKs) into the container class that it is trivial to
+code review and validate its correctness.
+
+*/
diff --git a/base/condition_variable.h b/base/condition_variable.h
new file mode 100644
index 0000000..bdb9a19
--- /dev/null
+++ b/base/condition_variable.h
@@ -0,0 +1,195 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// ConditionVariable is a reasonable attempt at simulating
+// the newer Posix and Vista-only construct for condition variable
+// synchronization.  This functionality is very helpful for having several
+// threads wait for an event, as is common with a thread pool
+// managed by a master.  The meaning of such an event in the
+// (worker) thread pool scenario is that additional tasks are
+// now available for processing.  It is used in Chrome in the
+// DNS prefetching system to notify worker threads that a queue
+// now has items (tasks) which need to be tended to.
+// A related use would have a pool manager waiting on a
+// ConditionVariable, waiting for a thread in the pool to announce
+// (signal) that there is now more room in a (bounded size) communications
+// queue for the manager to deposit tasks, or, as a second example, that
+// the queue of tasks is completely empty and all workers are waiting.
+
+// USAGE NOTE 1: spurious signal events are possible with this and
+// most implementations of condition variables.  As a result, be
+// *sure* to retest your condition before proceeding.  The following
+// is a good example of doing this correctly:
+
+// while (!work_to_be_done()) Wait(...);
+
+// In contrast do NOT do the following:
+
+// if (!work_to_be_done()) Wait(...);  // Don't do this.
+
+// Especially avoid the above if you are relying on some other thread only
+// issuing a signal up *if* there is work-to-do.  There can/will
+// be spurious signals.  Recheck state on waiting thread before
+// assuming the signal was intentional. Caveat caller ;-).
+
+// USAGE NOTE 2: Broadcast() frees up all waiting threads at once,
+// which leads to contention for the locks they all held when they
+// called Wait().  This results in POOR performance.  A much better
+// approach to getting a lot of threads out of Wait() is to have each
+// thread (upon exiting Wait()) call Signal() to free up another
+// Wait'ing thread.  Look at condition_variable_unittest.cc for
+// both examples.
+
+// Broadcast() can be used nicely during teardown, as it gets the job
+// done, and leaves no sleeping threads... and performance is less
+// critical at that point.
+
+// The semantics of Broadcast() are carefully crafted so that *all*
+// threads that were waiting when the request was made will indeed
+// get signaled.  Some implementations mess up, and don't signal them
+// all, while others allow the wait to be effectively turned off (for
+// for a while while waiting threads come around).  This implementation
+// appears correct, as it will not "lose" any signals, and will guarantee
+// that all threads get signaled by Broadcast().
+
+// This implementation offers support for "performance" in its selection of
+// which thread to revive.  Performance, in direct contrast with "fairness,"
+// assures that the thread that most recently began to Wait() is selected by
+// Signal to revive.  Fairness would (if publicly supported) assure that the
+// thread that has Wait()ed the longest is selected. The default policy
+// may improve performance, as the selected thread may have a greater chance of
+// having some of its stack data in various CPU caches.
+
+// For a discussion of the many very subtle implementation details, see the FAQ
+// at the end of condition_variable.cc.
+
+#ifndef BASE_CONDITION_VARIABLE_H__
+#define BASE_CONDITION_VARIABLE_H__
+
+#include "base/lock.h"
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "base/time.h"
+
+class Lock;
+
+class ConditionVariable {
+ public:
+  // Construct a cv for use with ONLY one user lock.
+  explicit ConditionVariable(Lock* user_lock);
+
+  ~ConditionVariable();
+
+  // Wait() releases the caller's critical section atomically as it starts to
+  // sleep, and the reacquires it when it is signaled.
+  void TimedWait(const TimeDelta& max_time);
+  void Wait() {
+    // Default to "wait forever" timing, which means have to get a Signal()
+    // or Broadcast() to come out of this wait state.
+    TimedWait(TimeDelta::FromMilliseconds(INFINITE));
+  }
+
+  // Broadcast() revives all waiting threads.
+  void Broadcast();
+  // Signal() revives one waiting thread.
+  void Signal();
+
+ private:
+  // Define Event class that is used to form circularly linked lists.
+  // The list container is an element with NULL as its handle_ value.
+  // The actual list elements have a non-zero handle_ value.
+  // All calls to methods MUST be done under protection of a lock so that links
+  // can be validated.  Without the lock, some links might asynchronously
+  // change, and the assertions would fail (as would list change operations).
+  class Event {
+   public:
+    // Default constructor with no arguments creates a list container.
+    Event();
+    ~Event();
+
+    // InitListElement transitions an instance from a container, to an element.
+    void InitListElement();
+
+    // Methods for use on lists.
+    bool IsEmpty() const;
+    void PushBack(Event* other);
+    Event* PopFront();
+    Event* PopBack();
+
+    // Methods for use on list elements.
+    // Accessor method.
+    HANDLE handle() const;
+    // Pull an element from a list (if it's in one).
+    Event* Extract();
+
+    // Method for use on a list element or on a list.
+    bool IsSingleton() const;
+
+   private:
+    // Provide pre/post conditions to validate correct manipulations.
+    bool ValidateAsDistinct(Event* other) const;
+    bool ValidateAsItem() const;
+    bool ValidateAsList() const;
+    bool ValidateLinks() const;
+
+    HANDLE handle_;
+    Event* next_;
+    Event* prev_;
+    DISALLOW_EVIL_CONSTRUCTORS(Event);
+  };
+
+  // Note that RUNNING is an unlikely number to have in RAM by accident.
+  // This helps with defensive destructor coding in the face of user error.
+  enum RunState { SHUTDOWN = 0, RUNNING = 64213 };
+
+  // Internal implementation methods supporting Wait().
+  Event* GetEventForWaiting();
+  void RecycleEvent(Event* used_event);
+
+  RunState run_state_;
+
+  // Private critical section for access to member data.
+  Lock internal_lock_;
+  // Lock that is acquired before calling Wait().
+  Lock& user_lock_;
+
+  // Events that threads are blocked on.
+  Event waiting_list_;
+
+  // Free list for old events.
+  Event recycling_list_;
+  int recycling_list_size_;
+
+  // The number of allocated, but not yet deleted events.
+  int allocation_counter_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(ConditionVariable);
+};
+
+#endif  // BASE_CONDITION_VARIABLE_H__
diff --git a/base/condition_variable_test.cc b/base/condition_variable_test.cc
new file mode 100644
index 0000000..35efa5f
--- /dev/null
+++ b/base/condition_variable_test.cc
@@ -0,0 +1,707 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Multi-threaded tests of ConditionVariable class.
+
+#include <time.h>
+#include <algorithm>
+#include <vector>
+
+#include "base/check_handler.h"
+#include "base/condition_variable.h"
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "base/spin_wait.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+//------------------------------------------------------------------------------
+// Define our test class, with several common variables.
+//------------------------------------------------------------------------------
+
+class ConditionVariableTest : public testing::Test {
+ public:
+  const TimeDelta kZeroMs;
+  const TimeDelta kTenMs;
+  const TimeDelta kThirtyMs;
+  const TimeDelta kFortyFiveMs;
+  const TimeDelta kSixtyMs;
+  const TimeDelta kOneHundredMs;
+
+  explicit ConditionVariableTest()
+    : kZeroMs(TimeDelta::FromMilliseconds(0)),
+      kTenMs(TimeDelta::FromMilliseconds(10)),
+      kThirtyMs(TimeDelta::FromMilliseconds(30)),
+      kFortyFiveMs(TimeDelta::FromMilliseconds(45)),
+      kSixtyMs(TimeDelta::FromMilliseconds(60)),
+      kOneHundredMs(TimeDelta::FromMilliseconds(100)) {
+  }
+};
+
+//------------------------------------------------------------------------------
+// Define a class that will control activities an several multi-threaded tests.
+// The general structure of multi-threaded tests is that a test case will
+// construct an instance of a WorkQueue.  The WorkQueue will spin up some
+// threads and control them thoughout their lifetime, as well as maintaining
+// a central respository of the work thread's activity.  Finally, the WorkQueue
+// will command the the worker threads to terminate.  At that point, the test
+// cases will validate that the WorkQueue has records showing that the desired
+// activities were performed.
+//------------------------------------------------------------------------------
+// Forward declare the WorkerProcess task
+static DWORD WINAPI WorkerProcess(void* p);
+
+// Callers are responsible for synchronizing access to the following class.
+// The WorkQueue::lock_, as accessed via WorkQueue::lock(), should be used for
+// all synchronized access.
+class WorkQueue {
+ public:
+  explicit WorkQueue(int thread_count);
+  ~WorkQueue();
+
+  //----------------------------------------------------------------------------
+  // Worker threads only call the following methods.
+  // They should use the lock to get exclusive access.
+  int GetThreadId();  // Get an ID assigned to a thread..
+  bool EveryIdWasAllocated() const;  // Indicates that all IDs were handed out.
+  TimeDelta GetAnAssignment(int thread_id);  // Get a work task duration.
+  void WorkIsCompleted(int thread_id);
+
+  int task_count() const;
+  bool allow_help_requests() const;  // Workers can signal more workers.
+  bool shutdown() const;  // Check if shutdown has been requested.
+  int shutdown_task_count() const;
+
+  void thread_shutting_down();
+  Lock* lock();
+
+  ConditionVariable* work_is_available();
+  ConditionVariable* all_threads_have_ids();
+  ConditionVariable* no_more_tasks();
+
+  //----------------------------------------------------------------------------
+  // The rest of the methods are for use by the controlling master thread (the
+  // test case code).
+  void ResetHistory();
+  int GetMinCompletionsByWorkerThread() const;
+  int GetMaxCompletionsByWorkerThread() const;
+  int GetNumThreadsTakingAssignments() const;
+  int GetNumThreadsCompletingTasks() const;
+  int GetNumberOfCompletedTasks() const;
+
+  void SetWorkTime(TimeDelta delay);
+  void SetTaskCount(int count);
+  void SetAllowHelp(bool allow);
+
+  void SetShutdown();
+
+ private:
+  // Both worker threads and controller use the following to synchronize.
+  Lock lock_;
+  ConditionVariable work_is_available_;  // To tell threads there is work.
+
+  // Conditions to notify the controlling process (if it is interested).
+  ConditionVariable all_threads_have_ids_;  // All threads are running.
+  ConditionVariable no_more_tasks_;  // Task count is zero.
+
+  const int thread_count_;
+  scoped_array<HANDLE> handles_;
+  std::vector<int> assignment_history_;  // Number of assignment per worker.
+  std::vector<int> completion_history_;  // Number of completions per worker.
+  int thread_started_counter_;  // Used to issue unique id to workers.
+  int shutdown_task_count_;  // Number of tasks told to shutdown
+  int task_count_;  // Number of assignment tasks waiting to be processed.
+  TimeDelta worker_delay_;  // Time each task takes to complete.
+  bool allow_help_requests_;  // Workers can signal more workers.
+  bool shutdown_;  // Set when threads need to terminate.
+};
+
+//------------------------------------------------------------------------------
+// Define the standard worker task. Several tests will spin out many of these
+// threads.
+//------------------------------------------------------------------------------
+
+// The multithread tests involve several threads with a task to perform as
+// directed by an instance of the class WorkQueue.
+// The task is to:
+// a) Check to see if there are more tasks (there is a task counter).
+//    a1) Wait on condition variable if there are no tasks currently.
+// b) Call a function to see what should be done.
+// c) Do some computation based on the number of milliseconds returned in (b).
+// d) go back to (a).
+
+// WorkerProcess() implements the above task for all threads.
+// It calls the controlling object to tell the creator about progress, and to
+// ask about tasks.
+static DWORD WINAPI WorkerProcess(void* p) {
+  int thread_id;
+  class WorkQueue* queue = reinterpret_cast<WorkQueue*>(p);
+  {
+    AutoLock auto_lock(*queue->lock());
+    thread_id = queue->GetThreadId();
+    if (queue->EveryIdWasAllocated())
+      queue->all_threads_have_ids()->Signal();  // Tell creator we're ready.
+  }
+
+  Lock private_lock;  // Used to waste time on "our work".
+  while (1) {  // This is the main consumer loop.
+    TimeDelta work_time;
+    bool could_use_help;
+    {
+      AutoLock auto_lock(*queue->lock());
+      while (0 == queue->task_count() && !queue->shutdown()) {
+        queue->work_is_available()->Wait();
+      }
+      if (queue->shutdown()) {
+        // Ack the notification of a shutdown message back to the controller.
+        queue->thread_shutting_down();
+        return 0;  // Terminate.
+      }
+      // Get our task duration from the queue.
+      work_time = queue->GetAnAssignment(thread_id);
+      could_use_help = (queue->task_count() > 0) &&
+                        queue->allow_help_requests();
+    }  // Release lock
+
+    // Do work (outside of locked region.
+    if (could_use_help)
+      queue->work_is_available()->Signal();  // Get help from other threads.
+
+    if (work_time > TimeDelta::FromMilliseconds(0)) {
+      // We could just sleep(), but we'll instead further exercise the
+      // condition variable class, and do a timed wait.
+      AutoLock auto_lock(private_lock);
+      ConditionVariable private_cv(&private_lock);
+      private_cv.TimedWait(work_time);  // Unsynchronized waiting.
+    }
+
+    {
+      AutoLock auto_lock(*queue->lock());
+      // Send notification that we completed our "work."
+      queue->WorkIsCompleted(thread_id);
+    }
+  }
+}
+//------------------------------------------------------------------------------
+// The next section contains the actual tests.
+//------------------------------------------------------------------------------
+
+TEST_F(ConditionVariableTest, StartupShutdownTest) {
+  Lock lock;
+
+  // First try trivial startup/shutdown.
+  {
+    ConditionVariable cv1(&lock);
+  }  // Call for cv1 destruction.
+
+  // Exercise with at least a few waits.
+  ConditionVariable cv(&lock);
+
+  lock.Acquire();
+  cv.TimedWait(kTenMs);  // Wait for 10 ms.
+  cv.TimedWait(kTenMs);  // Wait for 10 ms.
+  lock.Release();
+
+  lock.Acquire();
+  cv.TimedWait(kTenMs);  // Wait for 10 ms.
+  cv.TimedWait(kTenMs);  // Wait for 10 ms.
+  cv.TimedWait(kTenMs);  // Wait for 10 ms.
+  lock.Release();
+}  // Call for cv destruction.
+
+TEST_F(ConditionVariableTest, LockedExpressionTest) {
+  int i = 0;
+  Lock lock;
+
+  // Old LOCKED_EXPRESSION macro caused syntax errors here.
+  // ... yes... compiler will optimize this example.
+  // Syntax error is what I'm after precluding.
+  if (0)
+    LOCKED_EXPRESSION(lock, i = 1);
+  else
+    LOCKED_EXPRESSION(lock, i = 2);
+
+  EXPECT_EQ(2, i);
+}
+
+TEST_F(ConditionVariableTest, TimeoutTest) {
+  Lock lock;
+  ConditionVariable cv(&lock);
+  lock.Acquire();
+
+  TimeTicks start = TimeTicks::Now();
+  const TimeDelta WAIT_TIME = TimeDelta::FromMilliseconds(300);
+  // Allow for clocking rate granularity.
+  const TimeDelta FUDGE_TIME = TimeDelta::FromMilliseconds(50);
+
+  cv.TimedWait(WAIT_TIME + FUDGE_TIME);
+  TimeDelta duration = TimeTicks::Now() - start;
+  // We can't use EXPECT_GE here as the TimeDelta class does not support the
+  // required stream conversion.
+  EXPECT_TRUE(duration >= WAIT_TIME);
+
+  lock.Release();
+}
+
+TEST_F(ConditionVariableTest, MultiThreadConsumerTest) {
+  const int kThreadCount = 10;
+  WorkQueue queue(kThreadCount);  // Start the threads.
+
+  Lock private_lock;  // Used locally for master to wait.
+  AutoLock private_held_lock(private_lock);
+  ConditionVariable private_cv(&private_lock);
+
+  {
+    AutoLock auto_lock(*queue.lock());
+    while (!queue.EveryIdWasAllocated())
+      queue.all_threads_have_ids()->Wait();
+  }
+
+  // Wait a bit more to allow threads to reach their wait state.
+  private_cv.TimedWait(kTenMs);
+
+  {
+    // Since we have no tasks, all threads should be waiting by now.
+    AutoLock auto_lock(*queue.lock());
+    EXPECT_EQ(0, queue.GetNumThreadsTakingAssignments());
+    EXPECT_EQ(0, queue.GetNumThreadsCompletingTasks());
+    EXPECT_EQ(0, queue.task_count());
+    EXPECT_EQ(0, queue.GetMaxCompletionsByWorkerThread());
+    EXPECT_EQ(0, queue.GetMinCompletionsByWorkerThread());
+    EXPECT_EQ(0, queue.GetNumberOfCompletedTasks());
+
+    // Set up to make one worker do 3 30ms tasks.
+    queue.ResetHistory();
+    queue.SetTaskCount(3);
+    queue.SetWorkTime(kThirtyMs);
+    queue.SetAllowHelp(false);
+  }
+  queue.work_is_available()->Signal();  // Start up one thread.
+  // Wait to allow solo worker insufficient time to get done.
+  private_cv.TimedWait(kFortyFiveMs);  // Should take about 90 ms.
+
+  {
+    // Check that all work HASN'T completed yet.
+    AutoLock auto_lock(*queue.lock());
+    EXPECT_EQ(1, queue.GetNumThreadsTakingAssignments());
+    EXPECT_EQ(1, queue.GetNumThreadsCompletingTasks());
+    EXPECT_GT(2, queue.task_count());  // 2 should have started.
+    EXPECT_GT(3, queue.GetMaxCompletionsByWorkerThread());
+    EXPECT_EQ(0, queue.GetMinCompletionsByWorkerThread());
+    EXPECT_EQ(1, queue.GetNumberOfCompletedTasks());
+  }
+  // Wait to allow solo workers to get done.
+  private_cv.TimedWait(kSixtyMs);  // Should take about 45ms more.
+
+  {
+    // Check that all work was done by one thread id.
+    AutoLock auto_lock(*queue.lock());
+    EXPECT_EQ(1, queue.GetNumThreadsTakingAssignments());
+    EXPECT_EQ(1, queue.GetNumThreadsCompletingTasks());
+    EXPECT_EQ(0, queue.task_count());
+    EXPECT_EQ(3, queue.GetMaxCompletionsByWorkerThread());
+    EXPECT_EQ(0, queue.GetMinCompletionsByWorkerThread());
+    EXPECT_EQ(3, queue.GetNumberOfCompletedTasks());
+
+    // Set up to make each task include getting help from another worker.
+    queue.ResetHistory();
+    queue.SetTaskCount(3);
+    queue.SetWorkTime(kThirtyMs);
+    queue.SetAllowHelp(true);
+  }
+  queue.work_is_available()->Signal();  // But each worker can signal another.
+  // Wait to allow the 3 workers to get done.
+  private_cv.TimedWait(kFortyFiveMs);  // Should  take about 30 ms.
+
+  {
+    AutoLock auto_lock(*queue.lock());
+    EXPECT_EQ(3, queue.GetNumThreadsTakingAssignments());
+    EXPECT_EQ(3, queue.GetNumThreadsCompletingTasks());
+    EXPECT_EQ(0, queue.task_count());
+    EXPECT_EQ(1, queue.GetMaxCompletionsByWorkerThread());
+    EXPECT_EQ(0, queue.GetMinCompletionsByWorkerThread());
+    EXPECT_EQ(3, queue.GetNumberOfCompletedTasks());
+
+    // Try to ask all workers to help, and only a few will do the work.
+    queue.ResetHistory();
+    queue.SetTaskCount(3);
+    queue.SetWorkTime(kThirtyMs);
+    queue.SetAllowHelp(false);
+  }
+  queue.work_is_available()->Broadcast();  // Make them all try.
+  // Wait to allow the 3 workers to get done.
+  private_cv.TimedWait(kFortyFiveMs);
+
+  {
+    AutoLock auto_lock(*queue.lock());
+    EXPECT_EQ(3, queue.GetNumThreadsTakingAssignments());
+    EXPECT_EQ(3, queue.GetNumThreadsCompletingTasks());
+    EXPECT_EQ(0, queue.task_count());
+    EXPECT_EQ(1, queue.GetMaxCompletionsByWorkerThread());
+    EXPECT_EQ(0, queue.GetMinCompletionsByWorkerThread());
+    EXPECT_EQ(3, queue.GetNumberOfCompletedTasks());
+
+    // Set up to make each task get help from another worker.
+    queue.ResetHistory();
+    queue.SetTaskCount(3);
+    queue.SetWorkTime(kThirtyMs);
+    queue.SetAllowHelp(true);  // Allow (unnecessary) help requests.
+  }
+  queue.work_is_available()->Broadcast();  // We already signal all threads.
+  // Wait to allow the 3 workers to get done.
+  private_cv.TimedWait(kOneHundredMs);
+
+  {
+    AutoLock auto_lock(*queue.lock());
+    EXPECT_EQ(3, queue.GetNumThreadsTakingAssignments());
+    EXPECT_EQ(3, queue.GetNumThreadsCompletingTasks());
+    EXPECT_EQ(0, queue.task_count());
+    EXPECT_EQ(1, queue.GetMaxCompletionsByWorkerThread());
+    EXPECT_EQ(0, queue.GetMinCompletionsByWorkerThread());
+    EXPECT_EQ(3, queue.GetNumberOfCompletedTasks());
+
+    // Set up to make each task get help from another worker.
+    queue.ResetHistory();
+    queue.SetTaskCount(20);
+    queue.SetWorkTime(kThirtyMs);
+    queue.SetAllowHelp(true);
+  }
+  queue.work_is_available()->Signal();  // But each worker can signal another.
+  // Wait to allow the 10 workers to get done.
+  private_cv.TimedWait(kOneHundredMs);  // Should take about 60 ms.
+
+  {
+    AutoLock auto_lock(*queue.lock());
+    EXPECT_EQ(10, queue.GetNumThreadsTakingAssignments());
+    EXPECT_EQ(10, queue.GetNumThreadsCompletingTasks());
+    EXPECT_EQ(0, queue.task_count());
+    EXPECT_EQ(2, queue.GetMaxCompletionsByWorkerThread());
+    EXPECT_EQ(2, queue.GetMinCompletionsByWorkerThread());
+    EXPECT_EQ(20, queue.GetNumberOfCompletedTasks());
+
+    // Same as last test, but with Broadcast().
+    queue.ResetHistory();
+    queue.SetTaskCount(20);  // 2 tasks per process.
+    queue.SetWorkTime(kThirtyMs);
+    queue.SetAllowHelp(true);
+  }
+  queue.work_is_available()->Broadcast();
+  // Wait to allow the 10 workers to get done.
+  private_cv.TimedWait(kOneHundredMs);  // Should take about 60 ms.
+
+  {
+    AutoLock auto_lock(*queue.lock());
+    EXPECT_EQ(10, queue.GetNumThreadsTakingAssignments());
+    EXPECT_EQ(10, queue.GetNumThreadsCompletingTasks());
+    EXPECT_EQ(0, queue.task_count());
+    EXPECT_EQ(2, queue.GetMaxCompletionsByWorkerThread());
+    EXPECT_EQ(2, queue.GetMinCompletionsByWorkerThread());
+    EXPECT_EQ(20, queue.GetNumberOfCompletedTasks());
+
+    queue.SetShutdown();
+  }
+  queue.work_is_available()->Broadcast();  // Force check for shutdown.
+
+  SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(1),
+                                   queue.shutdown_task_count() == kThreadCount);
+  Sleep(10);  // Be sure they're all shutdown.
+}
+
+TEST_F(ConditionVariableTest, LargeFastTaskTest) {
+  const int kThreadCount = 200;
+  WorkQueue queue(kThreadCount);  // Start the threads.
+
+  Lock private_lock;  // Used locally for master to wait.
+  AutoLock private_held_lock(private_lock);
+  ConditionVariable private_cv(&private_lock);
+
+  {
+    AutoLock auto_lock(*queue.lock());
+    while (!queue.EveryIdWasAllocated())
+      queue.all_threads_have_ids()->Wait();
+  }
+
+  // Wait a bit more to allow threads to reach their wait state.
+  private_cv.TimedWait(kThirtyMs);
+
+  {
+    // Since we have no tasks, all threads should be waiting by now.
+    AutoLock auto_lock(*queue.lock());
+    EXPECT_EQ(0, queue.GetNumThreadsTakingAssignments());
+    EXPECT_EQ(0, queue.GetNumThreadsCompletingTasks());
+    EXPECT_EQ(0, queue.task_count());
+    EXPECT_EQ(0, queue.GetMaxCompletionsByWorkerThread());
+    EXPECT_EQ(0, queue.GetMinCompletionsByWorkerThread());
+    EXPECT_EQ(0, queue.GetNumberOfCompletedTasks());
+
+    // Set up to make all workers do (an average of) 20 tasks.
+    queue.ResetHistory();
+    queue.SetTaskCount(20 * kThreadCount);
+    queue.SetWorkTime(kFortyFiveMs);
+    queue.SetAllowHelp(false);
+  }
+  queue.work_is_available()->Broadcast();  // Start up all threads.
+  // Wait until we've handed out all tasks.
+  {
+    AutoLock auto_lock(*queue.lock());
+    while (queue.task_count() != 0)
+      queue.no_more_tasks()->Wait();
+  }
+
+  // Wait till the last of the tasks complete.
+  // Don't bother to use locks: We may not get info in time... but we'll see it
+  // eventually.
+  SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(1),
+                                    20 * kThreadCount ==
+                                      queue.GetNumberOfCompletedTasks());
+
+  {
+    // With Broadcast(), every thread should have participated.
+    // but with racing.. they may not all have done equal numbers of tasks.
+    AutoLock auto_lock(*queue.lock());
+    EXPECT_EQ(kThreadCount, queue.GetNumThreadsTakingAssignments());
+    EXPECT_EQ(kThreadCount, queue.GetNumThreadsCompletingTasks());
+    EXPECT_EQ(0, queue.task_count());
+    EXPECT_LE(20, queue.GetMaxCompletionsByWorkerThread());
+    EXPECT_EQ(20 * kThreadCount, queue.GetNumberOfCompletedTasks());
+
+    // Set up to make all workers do (an average of) 4 tasks.
+    queue.ResetHistory();
+    queue.SetTaskCount(kThreadCount * 4);
+    queue.SetWorkTime(kFortyFiveMs);
+    queue.SetAllowHelp(true);  // Might outperform Broadcast().
+  }
+  queue.work_is_available()->Signal();  // Start up one thread.
+
+  // Wait until we've handed out all tasks
+  {
+    AutoLock auto_lock(*queue.lock());
+    while (queue.task_count() != 0)
+      queue.no_more_tasks()->Wait();
+  }
+
+  // Wait till the last of the tasks complete.
+  // Don't bother to use locks: We may not get info in time... but we'll see it
+  // eventually.
+  SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(1),
+                                    4 * kThreadCount ==
+                                      queue.GetNumberOfCompletedTasks());
+
+  {
+    // With Signal(), every thread should have participated.
+    // but with racing.. they may not all have done four tasks.
+    AutoLock auto_lock(*queue.lock());
+    EXPECT_EQ(kThreadCount, queue.GetNumThreadsTakingAssignments());
+    EXPECT_EQ(kThreadCount, queue.GetNumThreadsCompletingTasks());
+    EXPECT_EQ(0, queue.task_count());
+    EXPECT_LE(4, queue.GetMaxCompletionsByWorkerThread());
+    EXPECT_EQ(4 * kThreadCount, queue.GetNumberOfCompletedTasks());
+
+    queue.SetShutdown();
+  }
+  queue.work_is_available()->Broadcast();  // Force check for shutdown.
+
+  // Wait for shutdows to complete.
+  SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(1),
+                                   queue.shutdown_task_count() == kThreadCount);
+  Sleep(10);  // Be sure they're all shutdown.
+}
+
+//------------------------------------------------------------------------------
+// Finally we provide the implementation for the methods in the WorkQueue class.
+//------------------------------------------------------------------------------
+
+WorkQueue::WorkQueue(int thread_count)
+  : lock_(),
+    work_is_available_(&lock_),
+    all_threads_have_ids_(&lock_),
+    no_more_tasks_(&lock_),
+    thread_count_(thread_count),
+    handles_(new HANDLE[thread_count]),
+    assignment_history_(thread_count),
+    completion_history_(thread_count),
+    thread_started_counter_(0),
+    shutdown_task_count_(0),
+    task_count_(0),
+    allow_help_requests_(false),
+    shutdown_(false) {
+  EXPECT_GE(thread_count_, 1);
+  ResetHistory();
+  SetTaskCount(0);
+  SetWorkTime(TimeDelta::FromMilliseconds(30));
+
+  for (int i = 0; i < thread_count_; ++i) {
+    handles_[i] = CreateThread(NULL,  // security.
+                               0,     // <64K stack size.
+                               WorkerProcess,  // Static function.
+                               reinterpret_cast<void*>(this),
+                               0,      // Create running process.
+                               NULL);  // OS version of thread id.
+    EXPECT_NE(reinterpret_cast<void*>(NULL), handles_[i]);
+  }
+}
+
+WorkQueue::~WorkQueue() {
+  {
+    AutoLock auto_lock(lock_);
+    SetShutdown();
+  }
+  work_is_available_.Broadcast();  // Tell them all to terminate.
+  DWORD result = WaitForMultipleObjects(
+                      thread_count_,
+                      &handles_[0],
+                      true,  // Wait for all
+                      10000);  // Ten seconds max.
+
+  for (int i = 0; i < thread_count_; ++i) {
+    int ret_value = CloseHandle(handles_[i]);
+    CHECK(ret_value);
+    handles_[i] = NULL;
+  }
+}
+
+int WorkQueue::GetThreadId() {
+  DCHECK(!EveryIdWasAllocated());
+  return thread_started_counter_++;  // Give out Unique IDs.
+}
+
+bool WorkQueue::EveryIdWasAllocated() const {
+  return thread_count_ == thread_started_counter_;
+}
+
+TimeDelta WorkQueue::GetAnAssignment(int thread_id) {
+  DCHECK_LT(0, task_count_);
+  assignment_history_[thread_id]++;
+  if (0 == --task_count_) {
+    no_more_tasks_.Signal();
+  }
+  return worker_delay_;
+}
+
+void WorkQueue::WorkIsCompleted(int thread_id) {
+  completion_history_[thread_id]++;
+}
+
+int WorkQueue::task_count() const {
+  return task_count_;
+}
+
+bool WorkQueue::allow_help_requests() const {
+  return allow_help_requests_;
+}
+
+bool WorkQueue::shutdown() const {
+  return shutdown_;
+}
+
+int WorkQueue::shutdown_task_count() const {
+  return shutdown_task_count_;
+}
+
+void WorkQueue::thread_shutting_down() {
+  shutdown_task_count_++;
+}
+
+Lock* WorkQueue::lock() {
+  return &lock_;
+}
+
+ConditionVariable* WorkQueue::work_is_available() {
+  return &work_is_available_;
+}
+
+ConditionVariable* WorkQueue::all_threads_have_ids() {
+  return &all_threads_have_ids_;
+}
+
+ConditionVariable* WorkQueue::no_more_tasks() {
+  return &no_more_tasks_;
+}
+
+void WorkQueue::ResetHistory() {
+  for (int i = 0; i < thread_count_; ++i) {
+    assignment_history_[i] = 0;
+    completion_history_[i] = 0;
+  }
+}
+
+int WorkQueue::GetMinCompletionsByWorkerThread() const {
+  int minumum = completion_history_[0];
+  for (int i = 0; i < thread_count_; ++i)
+    minumum = std::min(minumum, completion_history_[i]);
+  return minumum;
+}
+
+int WorkQueue::GetMaxCompletionsByWorkerThread() const {
+  int maximum = completion_history_[0];
+  for (int i = 0; i < thread_count_; ++i)
+    maximum = std::max(maximum, completion_history_[i]);
+  return maximum;
+}
+
+int WorkQueue::GetNumThreadsTakingAssignments() const {
+  int count = 0;
+  for (int i = 0; i < thread_count_; ++i)
+    if (assignment_history_[i])
+      count++;
+  return count;
+}
+
+int WorkQueue::GetNumThreadsCompletingTasks() const {
+  int count = 0;
+  for (int i = 0; i < thread_count_; ++i)
+    if (completion_history_[i])
+      count++;
+  return count;
+}
+
+int WorkQueue::GetNumberOfCompletedTasks() const {
+  int total = 0;
+  for (int i = 0; i < thread_count_; ++i)
+    total += completion_history_[i];
+  return total;
+}
+
+void WorkQueue::SetWorkTime(TimeDelta delay) {
+  worker_delay_ = delay;
+}
+
+void WorkQueue::SetTaskCount(int count) {
+  task_count_ = count;
+}
+
+void WorkQueue::SetAllowHelp(bool allow) {
+  allow_help_requests_ = allow;
+}
+
+void WorkQueue::SetShutdown() {
+  shutdown_ = true;
+}
+
+}  // namespace
diff --git a/base/data/file_util_unittest/binary_file.bin b/base/data/file_util_unittest/binary_file.bin
new file mode 100644
index 0000000..f53cc82
--- /dev/null
+++ b/base/data/file_util_unittest/binary_file.bin
Binary files differ
diff --git a/base/data/file_util_unittest/binary_file_diff.bin b/base/data/file_util_unittest/binary_file_diff.bin
new file mode 100644
index 0000000..103b26d
--- /dev/null
+++ b/base/data/file_util_unittest/binary_file_diff.bin
Binary files differ
diff --git a/base/data/file_util_unittest/binary_file_same.bin b/base/data/file_util_unittest/binary_file_same.bin
new file mode 100644
index 0000000..f53cc82
--- /dev/null
+++ b/base/data/file_util_unittest/binary_file_same.bin
Binary files differ
diff --git a/base/data/file_util_unittest/different.txt b/base/data/file_util_unittest/different.txt
new file mode 100644
index 0000000..5b9f9c4
--- /dev/null
+++ b/base/data/file_util_unittest/different.txt
@@ -0,0 +1 @@
+This file is different.
diff --git a/base/data/file_util_unittest/different_first.txt b/base/data/file_util_unittest/different_first.txt
new file mode 100644
index 0000000..8661d66
--- /dev/null
+++ b/base/data/file_util_unittest/different_first.txt
@@ -0,0 +1 @@
+this file is the same.
diff --git a/base/data/file_util_unittest/different_last.txt b/base/data/file_util_unittest/different_last.txt
new file mode 100644
index 0000000..e8b3e5a
--- /dev/null
+++ b/base/data/file_util_unittest/different_last.txt
@@ -0,0 +1 @@
+This file is the same. 
\ No newline at end of file
diff --git a/base/data/file_util_unittest/empty1.txt b/base/data/file_util_unittest/empty1.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/base/data/file_util_unittest/empty1.txt
diff --git a/base/data/file_util_unittest/empty2.txt b/base/data/file_util_unittest/empty2.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/base/data/file_util_unittest/empty2.txt
diff --git a/base/data/file_util_unittest/original.txt b/base/data/file_util_unittest/original.txt
new file mode 100644
index 0000000..4422f57
--- /dev/null
+++ b/base/data/file_util_unittest/original.txt
@@ -0,0 +1 @@
+This file is the same.
diff --git a/base/data/file_util_unittest/same.txt b/base/data/file_util_unittest/same.txt
new file mode 100644
index 0000000..4422f57
--- /dev/null
+++ b/base/data/file_util_unittest/same.txt
@@ -0,0 +1 @@
+This file is the same.
diff --git a/base/data/file_util_unittest/same_length.txt b/base/data/file_util_unittest/same_length.txt
new file mode 100644
index 0000000..157405c
--- /dev/null
+++ b/base/data/file_util_unittest/same_length.txt
@@ -0,0 +1 @@
+This file is not same.
diff --git a/base/data/file_util_unittest/shortened.txt b/base/data/file_util_unittest/shortened.txt
new file mode 100644
index 0000000..2bee82c
--- /dev/null
+++ b/base/data/file_util_unittest/shortened.txt
@@ -0,0 +1 @@
+This file is the
\ No newline at end of file
diff --git a/base/data/file_version_info_unittest/FileVersionInfoTest1.dll b/base/data/file_version_info_unittest/FileVersionInfoTest1.dll
new file mode 100644
index 0000000..bdf8dc0
--- /dev/null
+++ b/base/data/file_version_info_unittest/FileVersionInfoTest1.dll
Binary files differ
diff --git a/base/data/file_version_info_unittest/FileVersionInfoTest2.dll b/base/data/file_version_info_unittest/FileVersionInfoTest2.dll
new file mode 100644
index 0000000..51e7966
--- /dev/null
+++ b/base/data/file_version_info_unittest/FileVersionInfoTest2.dll
Binary files differ
diff --git a/base/data/purify/base_unittests.exe.gtest.txt b/base/data/purify/base_unittests.exe.gtest.txt
new file mode 100644
index 0000000..0687e7e
--- /dev/null
+++ b/base/data/purify/base_unittests.exe.gtest.txt
@@ -0,0 +1,19 @@
+# this test causes Purify to get completely confused, aborting the test and 
+# popping up 10 or more error dialogs
+StatsTableTest.MultipleProcesses
+
+# see bug 1151158
+# causes purify to occasionally crash, possibly the same reason as 1110206 below
+StatsTableTest.MultipleThreads
+
+# this test takes a really long time to run in Purify
+TimeTicks.Rollover
+
+# see bug 1110206
+ConditionVariableTest.LargeFastTaskTest
+
+# see bug 1150075
+MessageLoopTest.Crasher*
+
+# see bug 1195707
+WMIUtilTest.*
diff --git a/base/data/purify/base_unittests.exe_MLK.txt b/base/data/purify/base_unittests.exe_MLK.txt
new file mode 100644
index 0000000..e622502
--- /dev/null
+++ b/base/data/purify/base_unittests.exe_MLK.txt
@@ -0,0 +1,97 @@
+std::D::_Allocate(unsigned int,char *) [base_unittests.exe]
+Alloc Location
+   ...  
+   base/check_handler_unittest.cc  ThisFunctionAsserts(void)
+   base/check_handler_unittest.cc  CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void)
+   testing/gtest/src/gtest.cc  testing::Test::Run(void)
+   ^^^  
+
+std::_Mutex::_Mutex(void) [base_unittests.exe]
+Alloc Location
+   ...  
+   base/logging.cc  logging::LogMessage::LogMessage(char const*,int,int)
+   base/check_handler_unittest.cc  ThisFunctionAsserts(void)
+   base/check_handler_unittest.cc  CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void)
+   testing/gtest/src/gtest.cc  testing::Test::Run(void)
+   ^^^  
+
+std::basic_streambuf<char,char_traits<char>::std>::basic_streambuf<char,char_traits<char>::std>(void) [base_unittests.exe]
+Alloc Location
+   ...  
+   base/logging.cc  logging::LogMessage::LogMessage(char const*,int,int)
+   base/check_handler_unittest.cc  ThisFunctionAsserts(void)
+   base/check_handler_unittest.cc  CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void)
+   testing/gtest/src/gtest.cc  testing::Test::Run(void)
+   ^^^  
+
+std::ios_base::_Init(void) [base_unittests.exe]
+Alloc Location
+   ...  
+   base/logging.cc  logging::LogMessage::LogMessage(char const*,int,int)
+   base/check_handler_unittest.cc  ThisFunctionAsserts(void)
+   base/check_handler_unittest.cc  CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void)
+   testing/gtest/src/gtest.cc  testing::Test::Run(void)
+   ^^^  
+
+std::D::_Allocate(unsigned int,char *) [base_unittests.exe]
+Alloc Location
+   ...  
+   base/logging.cc  logging::LogMessage::~LogMessage(void)
+   base/check_handler_unittest.cc  ThisFunctionAsserts(void)
+   base/check_handler_unittest.cc  CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void)
+   testing/gtest/src/gtest.cc  testing::Test::Run(void)
+   ^^^  
+
+std::D::_Allocate(unsigned int,char *) [base_unittests.exe]
+Alloc Location
+   ...  
+   base/check_handler_unittest.cc  SimpleTestClass::ThisMethodAsserts(void)
+   base/check_handler_unittest.cc  CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void)
+   testing/gtest/src/gtest.cc  testing::Test::Run(void)
+   ^^^  
+
+std::_Mutex::_Mutex(void) [base_unittests.exe]
+Alloc Location
+   ...  
+   base/logging.cc  logging::LogMessage::LogMessage(char const*,int,int)
+   base/check_handler_unittest.cc  SimpleTestClass::ThisMethodAsserts(void)
+   base/check_handler_unittest.cc  CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void)
+   testing/gtest/src/gtest.cc  testing::Test::Run(void)
+   ^^^  
+
+std::basic_streambuf<char,char_traits<char>::std>::basic_streambuf<char,char_traits<char>::std>(void) [base_unittests.exe]
+Alloc Location
+   ...  
+   base/logging.cc  logging::LogMessage::LogMessage(char const*,int,int)
+   base/check_handler_unittest.cc  SimpleTestClass::ThisMethodAsserts(void)
+   base/check_handler_unittest.cc  CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void)
+   testing/gtest/src/gtest.cc  testing::Test::Run(void)
+   ^^^  
+
+std::ios_base::_Init(void) [base_unittests.exe]
+Alloc Location
+   ...  
+   base/logging.cc  logging::LogMessage::LogMessage(char const*,int,int)
+   base/check_handler_unittest.cc  SimpleTestClass::ThisMethodAsserts(void)
+   base/check_handler_unittest.cc  CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void)
+   testing/gtest/src/gtest.cc  testing::Test::Run(void)
+   ^^^  
+
+std::D::_Allocate(unsigned int,char *) [base_unittests.exe]
+Alloc Location
+   ...  
+   base/logging.cc  logging::LogMessage::~LogMessage(void)
+   base/check_handler_unittest.cc  SimpleTestClass::ThisMethodAsserts(void)
+   base/check_handler_unittest.cc  CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void)
+   testing/gtest/src/gtest.cc  testing::Test::Run(void)
+   ^^^  
+
+CoTaskMemAlloc [OLE32.DLL]
+Alloc Location
+   ...  
+   base/wmi_util.cc  WMIUtil::CreateLocalConnection(bool)
+   base/wmi_util.cc  WMIProcessUtil::Launch(class std::basic_string const &,int *)
+   base/wmi_util_unittest.cc  WMIUtilTest_TestLaunchProcess_Test::TestBody(void)
+   testing/gtest/src/gtest.cc  testing::Test::Run(void)
+   ^^^  
+
diff --git a/base/data/purify/base_unittests.exe_MLK_flakey.txt b/base/data/purify/base_unittests.exe_MLK_flakey.txt
new file mode 100644
index 0000000..0626ba0
--- /dev/null
+++ b/base/data/purify/base_unittests.exe_MLK_flakey.txt
@@ -0,0 +1,8 @@
+CoTaskMemAlloc [OLE32.DLL]
+Alloc Location
+   ...  
+   base/wmi_util.cc  WMIUtil::CreateLocalConnection(bool)
+   base/wmi_util.cc  WMIProcessUtil::Launch(class std::basic_string const &,int *)
+   base/wmi_util_unittest.cc  WMIUtilTest_TestLaunchProcess_Test::TestBody(void)
+   testing/gtest/src/gtest.cc  testing::Test::Run(void)
+   ^^^  
diff --git a/base/data/purify/base_unittests.exe_MLK_ignore.txt b/base/data/purify/base_unittests.exe_MLK_ignore.txt
new file mode 100644
index 0000000..020fadb
--- /dev/null
+++ b/base/data/purify/base_unittests.exe_MLK_ignore.txt
@@ -0,0 +1,93 @@
+# -----
+# Leaks in ::RaiseException, called when we log a fatal error. See bug 1078612.
+
+std::strstreambuf::overflow(int) [base_unittests.exe]
+Alloc Location
+   ...  
+   base/check_handler_unittest.cc  ThisFunctionAsserts(void)
+   base/check_handler_unittest.cc  CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void)
+   testing/gtest/src/gtest.cc  testing::Test::Run(void)
+   ^^^  
+
+std::_Mutex::_Mutex(void) [base_unittests.exe]
+Alloc Location
+   ...  
+   base/logging.cc  logging::LogMessage::LogMessage(char const*,int,int)
+   base/check_handler_unittest.cc  ThisFunctionAsserts(void)
+   base/check_handler_unittest.cc  CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void)
+   testing/gtest/src/gtest.cc  testing::Test::Run(void)
+   ^^^  
+
+std::basic_streambuf<char,char_traits<char>::std>::basic_streambuf<char,char_traits<char>::std>(void) [base_unittests.exe]
+Alloc Location
+   ...  
+   base/logging.cc  logging::LogMessage::LogMessage(char const*,int,int)
+   base/check_handler_unittest.cc  ThisFunctionAsserts(void)
+   base/check_handler_unittest.cc  CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void)
+   testing/gtest/src/gtest.cc  testing::Test::Run(void)
+   ^^^  
+
+std::ios_base::_Init(void) [base_unittests.exe]
+Alloc Location
+   ...  
+   base/logging.cc  logging::LogMessage::LogMessage(char const*,int,int)
+   base/check_handler_unittest.cc  ThisFunctionAsserts(void)
+   base/check_handler_unittest.cc  CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void)
+   testing/gtest/src/gtest.cc  testing::Test::Run(void)
+   ^^^  
+
+std::D::_Allocate(unsigned int,char *) [base_unittests.exe]
+Alloc Location
+   ...  
+   base/logging.cc  logging::LogMessage::~LogMessage(void)
+   base/check_handler_unittest.cc  ThisFunctionAsserts(void)
+   base/check_handler_unittest.cc  CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void)
+   testing/gtest/src/gtest.cc  testing::Test::Run(void)
+   ^^^  
+
+std::strstreambuf::overflow(int) [base_unittests.exe]
+Alloc Location
+   ...  
+   base/check_handler_unittest.cc  SimpleTestClass::ThisMethodAsserts(void)
+   base/check_handler_unittest.cc  CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void)
+   testing/gtest/src/gtest.cc  testing::Test::Run(void)
+   ^^^  
+
+std::_Mutex::_Mutex(void) [base_unittests.exe]
+Alloc Location
+   ...  
+   base/logging.cc  logging::LogMessage::LogMessage(char const*,int,int)
+   base/check_handler_unittest.cc  SimpleTestClass::ThisMethodAsserts(void)
+   base/check_handler_unittest.cc  CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void)
+   testing/gtest/src/gtest.cc  testing::Test::Run(void)
+   ^^^  
+
+std::basic_streambuf<char,char_traits<char>::std>::basic_streambuf<char,char_traits<char>::std>(void) [base_unittests.exe]
+Alloc Location
+   ...  
+   base/logging.cc  logging::LogMessage::LogMessage(char const*,int,int)
+   base/check_handler_unittest.cc  SimpleTestClass::ThisMethodAsserts(void)
+   base/check_handler_unittest.cc  CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void)
+   testing/gtest/src/gtest.cc  testing::Test::Run(void)
+   ^^^  
+
+std::ios_base::_Init(void) [base_unittests.exe]
+Alloc Location
+   ...  
+   base/logging.cc  logging::LogMessage::LogMessage(char const*,int,int)
+   base/check_handler_unittest.cc  SimpleTestClass::ThisMethodAsserts(void)
+   base/check_handler_unittest.cc  CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void)
+   testing/gtest/src/gtest.cc  testing::Test::Run(void)
+   ^^^  
+
+std::D::_Allocate(unsigned int,char *) [base_unittests.exe]
+Alloc Location
+   ...  
+   base/logging.cc  logging::LogMessage::~LogMessage(void)
+   base/check_handler_unittest.cc  SimpleTestClass::ThisMethodAsserts(void)
+   base/check_handler_unittest.cc  CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void)
+   testing/gtest/src/gtest.cc  testing::Test::Run(void)
+   ^^^  
+
+# End of leaks in ::RaiseException
+# -----
diff --git a/base/data/purify/base_unittests.exe_PAR_ignore.txt b/base/data/purify/base_unittests.exe_PAR_ignore.txt
new file mode 100644
index 0000000..69018f2
--- /dev/null
+++ b/base/data/purify/base_unittests.exe_PAR_ignore.txt
@@ -0,0 +1,8 @@
+# Probably a Purify error.  See bug 1076843.
+WideCharToMultiByte: Invalid size (0x27) for destination buffer.
+Error Location
+   ...  
+   base/file_util_unittest.cc  FileUtilTest_ResolveShortcutTest_Test::TestBody(void)
+   testing/gtest/src/gtest.cc  testing::Test::Run(void)
+   ^^^  
+
diff --git a/base/data/purify/base_unittests.exe_UMR.txt b/base/data/purify/base_unittests.exe_UMR.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/base/data/purify/base_unittests.exe_UMR.txt
diff --git a/base/data/vectorcanvastest/basicdrawing/00_pc_clean.png b/base/data/vectorcanvastest/basicdrawing/00_pc_clean.png
new file mode 100644
index 0000000..a5435f2
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/00_pc_clean.png
Binary files differ
diff --git a/base/data/vectorcanvastest/basicdrawing/00_vc_clean.png b/base/data/vectorcanvastest/basicdrawing/00_vc_clean.png
new file mode 100644
index 0000000..a5435f2
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/00_vc_clean.png
Binary files differ
diff --git a/base/data/vectorcanvastest/basicdrawing/01_pc_drawargb.png b/base/data/vectorcanvastest/basicdrawing/01_pc_drawargb.png
new file mode 100644
index 0000000..a5435f2
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/01_pc_drawargb.png
Binary files differ
diff --git a/base/data/vectorcanvastest/basicdrawing/01_vc_drawargb.png b/base/data/vectorcanvastest/basicdrawing/01_vc_drawargb.png
new file mode 100644
index 0000000..a5435f2
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/01_vc_drawargb.png
Binary files differ
diff --git a/base/data/vectorcanvastest/basicdrawing/02_pc_drawline_black.png b/base/data/vectorcanvastest/basicdrawing/02_pc_drawline_black.png
new file mode 100644
index 0000000..c21fdf1
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/02_pc_drawline_black.png
Binary files differ
diff --git a/base/data/vectorcanvastest/basicdrawing/02_vc_drawline_black.png b/base/data/vectorcanvastest/basicdrawing/02_vc_drawline_black.png
new file mode 100644
index 0000000..c21fdf1
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/02_vc_drawline_black.png
Binary files differ
diff --git a/base/data/vectorcanvastest/basicdrawing/03_pc_drawrect_green.png b/base/data/vectorcanvastest/basicdrawing/03_pc_drawrect_green.png
new file mode 100644
index 0000000..dfc46a8
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/03_pc_drawrect_green.png
Binary files differ
diff --git a/base/data/vectorcanvastest/basicdrawing/03_vc_drawrect_green.png b/base/data/vectorcanvastest/basicdrawing/03_vc_drawrect_green.png
new file mode 100644
index 0000000..dfc46a8
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/03_vc_drawrect_green.png
Binary files differ
diff --git a/base/data/vectorcanvastest/basicdrawing/04_pc_drawrect_noop.png b/base/data/vectorcanvastest/basicdrawing/04_pc_drawrect_noop.png
new file mode 100644
index 0000000..dfc46a8
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/04_pc_drawrect_noop.png
Binary files differ
diff --git a/base/data/vectorcanvastest/basicdrawing/04_vc_drawrect_noop.png b/base/data/vectorcanvastest/basicdrawing/04_vc_drawrect_noop.png
new file mode 100644
index 0000000..dfc46a8
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/04_vc_drawrect_noop.png
Binary files differ
diff --git a/base/data/vectorcanvastest/basicdrawing/05_pc_drawrect_noop.png b/base/data/vectorcanvastest/basicdrawing/05_pc_drawrect_noop.png
new file mode 100644
index 0000000..69cc6dc
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/05_pc_drawrect_noop.png
Binary files differ
diff --git a/base/data/vectorcanvastest/basicdrawing/05_vc_drawrect_noop.png b/base/data/vectorcanvastest/basicdrawing/05_vc_drawrect_noop.png
new file mode 100644
index 0000000..69cc6dc
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/05_vc_drawrect_noop.png
Binary files differ
diff --git a/base/data/vectorcanvastest/basicdrawing/06_pc_drawpaint_black.png b/base/data/vectorcanvastest/basicdrawing/06_pc_drawpaint_black.png
new file mode 100644
index 0000000..9cbff6e
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/06_pc_drawpaint_black.png
Binary files differ
diff --git a/base/data/vectorcanvastest/basicdrawing/06_vc_drawpaint_black.png b/base/data/vectorcanvastest/basicdrawing/06_vc_drawpaint_black.png
new file mode 100644
index 0000000..9cbff6e
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/06_vc_drawpaint_black.png
Binary files differ
diff --git a/base/data/vectorcanvastest/basicdrawing/07_pc_drawline_left_to_right.png b/base/data/vectorcanvastest/basicdrawing/07_pc_drawline_left_to_right.png
new file mode 100644
index 0000000..bbdfc36
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/07_pc_drawline_left_to_right.png
Binary files differ
diff --git a/base/data/vectorcanvastest/basicdrawing/07_vc_drawline_left_to_right.png b/base/data/vectorcanvastest/basicdrawing/07_vc_drawline_left_to_right.png
new file mode 100644
index 0000000..bbdfc36
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/07_vc_drawline_left_to_right.png
Binary files differ
diff --git a/base/data/vectorcanvastest/basicdrawing/08_pc_drawline_red.png b/base/data/vectorcanvastest/basicdrawing/08_pc_drawline_red.png
new file mode 100644
index 0000000..9dc35f0
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/08_pc_drawline_red.png
Binary files differ
diff --git a/base/data/vectorcanvastest/basicdrawing/08_vc_drawline_red.png b/base/data/vectorcanvastest/basicdrawing/08_vc_drawline_red.png
new file mode 100644
index 0000000..9dc35f0
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/08_vc_drawline_red.png
Binary files differ
diff --git a/base/data/vectorcanvastest/bitmaps/00_pc_opaque.png b/base/data/vectorcanvastest/bitmaps/00_pc_opaque.png
new file mode 100644
index 0000000..812b1ca
--- /dev/null
+++ b/base/data/vectorcanvastest/bitmaps/00_pc_opaque.png
Binary files differ
diff --git a/base/data/vectorcanvastest/bitmaps/00_vc_opaque.png b/base/data/vectorcanvastest/bitmaps/00_vc_opaque.png
new file mode 100644
index 0000000..812b1ca
--- /dev/null
+++ b/base/data/vectorcanvastest/bitmaps/00_vc_opaque.png
Binary files differ
diff --git a/base/data/vectorcanvastest/bitmaps/01_pc_alpha.png b/base/data/vectorcanvastest/bitmaps/01_pc_alpha.png
new file mode 100644
index 0000000..1d1342b
--- /dev/null
+++ b/base/data/vectorcanvastest/bitmaps/01_pc_alpha.png
Binary files differ
diff --git a/base/data/vectorcanvastest/bitmaps/01_vc_alpha.png b/base/data/vectorcanvastest/bitmaps/01_vc_alpha.png
new file mode 100644
index 0000000..1d1342b
--- /dev/null
+++ b/base/data/vectorcanvastest/bitmaps/01_vc_alpha.png
Binary files differ
diff --git a/base/data/vectorcanvastest/bitmaps/bitmap_alpha.png b/base/data/vectorcanvastest/bitmaps/bitmap_alpha.png
new file mode 100644
index 0000000..a19d09d
--- /dev/null
+++ b/base/data/vectorcanvastest/bitmaps/bitmap_alpha.png
Binary files differ
diff --git a/base/data/vectorcanvastest/bitmaps/bitmap_opaque.png b/base/data/vectorcanvastest/bitmaps/bitmap_opaque.png
new file mode 100644
index 0000000..3560d27
--- /dev/null
+++ b/base/data/vectorcanvastest/bitmaps/bitmap_opaque.png
Binary files differ
diff --git a/base/data/vectorcanvastest/circles/00_pc_circle_stroke.png b/base/data/vectorcanvastest/circles/00_pc_circle_stroke.png
new file mode 100644
index 0000000..896631b
--- /dev/null
+++ b/base/data/vectorcanvastest/circles/00_pc_circle_stroke.png
Binary files differ
diff --git a/base/data/vectorcanvastest/circles/00_vc_circle_stroke.png b/base/data/vectorcanvastest/circles/00_vc_circle_stroke.png
new file mode 100644
index 0000000..d1d4cb2
--- /dev/null
+++ b/base/data/vectorcanvastest/circles/00_vc_circle_stroke.png
Binary files differ
diff --git a/base/data/vectorcanvastest/circles/01_pc_circle_fill.png b/base/data/vectorcanvastest/circles/01_pc_circle_fill.png
new file mode 100644
index 0000000..a317292
--- /dev/null
+++ b/base/data/vectorcanvastest/circles/01_pc_circle_fill.png
Binary files differ
diff --git a/base/data/vectorcanvastest/circles/01_vc_circle_fill.png b/base/data/vectorcanvastest/circles/01_vc_circle_fill.png
new file mode 100644
index 0000000..0628c02
--- /dev/null
+++ b/base/data/vectorcanvastest/circles/01_vc_circle_fill.png
Binary files differ
diff --git a/base/data/vectorcanvastest/circles/02_pc_circle_over_strike.png b/base/data/vectorcanvastest/circles/02_pc_circle_over_strike.png
new file mode 100644
index 0000000..64ae06a
--- /dev/null
+++ b/base/data/vectorcanvastest/circles/02_pc_circle_over_strike.png
Binary files differ
diff --git a/base/data/vectorcanvastest/circles/02_vc_circle_over_strike.png b/base/data/vectorcanvastest/circles/02_vc_circle_over_strike.png
new file mode 100644
index 0000000..f333f9d
--- /dev/null
+++ b/base/data/vectorcanvastest/circles/02_vc_circle_over_strike.png
Binary files differ
diff --git a/base/data/vectorcanvastest/circles/03_pc_circle_stroke_and_fill.png b/base/data/vectorcanvastest/circles/03_pc_circle_stroke_and_fill.png
new file mode 100644
index 0000000..66d6a332
--- /dev/null
+++ b/base/data/vectorcanvastest/circles/03_pc_circle_stroke_and_fill.png
Binary files differ
diff --git a/base/data/vectorcanvastest/circles/03_vc_circle_stroke_and_fill.png b/base/data/vectorcanvastest/circles/03_vc_circle_stroke_and_fill.png
new file mode 100644
index 0000000..a756cf3
--- /dev/null
+++ b/base/data/vectorcanvastest/circles/03_vc_circle_stroke_and_fill.png
Binary files differ
diff --git a/base/data/vectorcanvastest/circles/04_pc_mixed_stroke.png b/base/data/vectorcanvastest/circles/04_pc_mixed_stroke.png
new file mode 100644
index 0000000..de9c492
--- /dev/null
+++ b/base/data/vectorcanvastest/circles/04_pc_mixed_stroke.png
Binary files differ
diff --git a/base/data/vectorcanvastest/circles/04_vc_mixed_stroke.png b/base/data/vectorcanvastest/circles/04_vc_mixed_stroke.png
new file mode 100644
index 0000000..ae3cd03
--- /dev/null
+++ b/base/data/vectorcanvastest/circles/04_vc_mixed_stroke.png
Binary files differ
diff --git a/base/data/vectorcanvastest/clippingclean/00_pc_clipped.png b/base/data/vectorcanvastest/clippingclean/00_pc_clipped.png
new file mode 100644
index 0000000..14ff949
--- /dev/null
+++ b/base/data/vectorcanvastest/clippingclean/00_pc_clipped.png
Binary files differ
diff --git a/base/data/vectorcanvastest/clippingclean/00_vc_clipped.png b/base/data/vectorcanvastest/clippingclean/00_vc_clipped.png
new file mode 100644
index 0000000..14ff949
--- /dev/null
+++ b/base/data/vectorcanvastest/clippingclean/00_vc_clipped.png
Binary files differ
diff --git a/base/data/vectorcanvastest/clippingclean/01_pc_unclipped.png b/base/data/vectorcanvastest/clippingclean/01_pc_unclipped.png
new file mode 100644
index 0000000..436f9a5
--- /dev/null
+++ b/base/data/vectorcanvastest/clippingclean/01_pc_unclipped.png
Binary files differ
diff --git a/base/data/vectorcanvastest/clippingclean/01_vc_unclipped.png b/base/data/vectorcanvastest/clippingclean/01_vc_unclipped.png
new file mode 100644
index 0000000..436f9a5
--- /dev/null
+++ b/base/data/vectorcanvastest/clippingclean/01_vc_unclipped.png
Binary files differ
diff --git a/base/data/vectorcanvastest/clippingcombined/00_pc_combined.png b/base/data/vectorcanvastest/clippingcombined/00_pc_combined.png
new file mode 100644
index 0000000..14ff949
--- /dev/null
+++ b/base/data/vectorcanvastest/clippingcombined/00_pc_combined.png
Binary files differ
diff --git a/base/data/vectorcanvastest/clippingcombined/00_vc_combined.png b/base/data/vectorcanvastest/clippingcombined/00_vc_combined.png
new file mode 100644
index 0000000..14ff949
--- /dev/null
+++ b/base/data/vectorcanvastest/clippingcombined/00_vc_combined.png
Binary files differ
diff --git a/base/data/vectorcanvastest/clippingintersect/00_pc_intersect.png b/base/data/vectorcanvastest/clippingintersect/00_pc_intersect.png
new file mode 100644
index 0000000..704f03a
--- /dev/null
+++ b/base/data/vectorcanvastest/clippingintersect/00_pc_intersect.png
Binary files differ
diff --git a/base/data/vectorcanvastest/clippingintersect/00_vc_intersect.png b/base/data/vectorcanvastest/clippingintersect/00_vc_intersect.png
new file mode 100644
index 0000000..704f03a
--- /dev/null
+++ b/base/data/vectorcanvastest/clippingintersect/00_vc_intersect.png
Binary files differ
diff --git a/base/data/vectorcanvastest/clippingpath/00_pc_path.png b/base/data/vectorcanvastest/clippingpath/00_pc_path.png
new file mode 100644
index 0000000..78443af
--- /dev/null
+++ b/base/data/vectorcanvastest/clippingpath/00_pc_path.png
Binary files differ
diff --git a/base/data/vectorcanvastest/clippingpath/00_vc_path.png b/base/data/vectorcanvastest/clippingpath/00_vc_path.png
new file mode 100644
index 0000000..78443af
--- /dev/null
+++ b/base/data/vectorcanvastest/clippingpath/00_vc_path.png
Binary files differ
diff --git a/base/data/vectorcanvastest/clippingrect/00_pc_rect.png b/base/data/vectorcanvastest/clippingrect/00_pc_rect.png
new file mode 100644
index 0000000..9c365e1
--- /dev/null
+++ b/base/data/vectorcanvastest/clippingrect/00_pc_rect.png
Binary files differ
diff --git a/base/data/vectorcanvastest/clippingrect/00_vc_rect.png b/base/data/vectorcanvastest/clippingrect/00_vc_rect.png
new file mode 100644
index 0000000..9c365e1
--- /dev/null
+++ b/base/data/vectorcanvastest/clippingrect/00_vc_rect.png
Binary files differ
diff --git a/base/data/vectorcanvastest/diagonallines/00_pc_nw-se.png b/base/data/vectorcanvastest/diagonallines/00_pc_nw-se.png
new file mode 100644
index 0000000..5736c35
--- /dev/null
+++ b/base/data/vectorcanvastest/diagonallines/00_pc_nw-se.png
Binary files differ
diff --git a/base/data/vectorcanvastest/diagonallines/00_vc_nw-se.png b/base/data/vectorcanvastest/diagonallines/00_vc_nw-se.png
new file mode 100644
index 0000000..5736c35
--- /dev/null
+++ b/base/data/vectorcanvastest/diagonallines/00_vc_nw-se.png
Binary files differ
diff --git a/base/data/vectorcanvastest/diagonallines/01_pc_sw-ne.png b/base/data/vectorcanvastest/diagonallines/01_pc_sw-ne.png
new file mode 100644
index 0000000..bfffd8a
--- /dev/null
+++ b/base/data/vectorcanvastest/diagonallines/01_pc_sw-ne.png
Binary files differ
diff --git a/base/data/vectorcanvastest/diagonallines/01_vc_sw-ne.png b/base/data/vectorcanvastest/diagonallines/01_vc_sw-ne.png
new file mode 100644
index 0000000..ae6b753
--- /dev/null
+++ b/base/data/vectorcanvastest/diagonallines/01_vc_sw-ne.png
Binary files differ
diff --git a/base/data/vectorcanvastest/diagonallines/02_pc_ne-sw.png b/base/data/vectorcanvastest/diagonallines/02_pc_ne-sw.png
new file mode 100644
index 0000000..75acdad
--- /dev/null
+++ b/base/data/vectorcanvastest/diagonallines/02_pc_ne-sw.png
Binary files differ
diff --git a/base/data/vectorcanvastest/diagonallines/02_vc_ne-sw.png b/base/data/vectorcanvastest/diagonallines/02_vc_ne-sw.png
new file mode 100644
index 0000000..86a6799
--- /dev/null
+++ b/base/data/vectorcanvastest/diagonallines/02_vc_ne-sw.png
Binary files differ
diff --git a/base/data/vectorcanvastest/diagonallines/03_pc_se-nw.png b/base/data/vectorcanvastest/diagonallines/03_pc_se-nw.png
new file mode 100644
index 0000000..50502cc
--- /dev/null
+++ b/base/data/vectorcanvastest/diagonallines/03_pc_se-nw.png
Binary files differ
diff --git a/base/data/vectorcanvastest/diagonallines/03_vc_se-nw.png b/base/data/vectorcanvastest/diagonallines/03_vc_se-nw.png
new file mode 100644
index 0000000..362f6e7
--- /dev/null
+++ b/base/data/vectorcanvastest/diagonallines/03_vc_se-nw.png
Binary files differ
diff --git a/base/data/vectorcanvastest/lineorientation/00_pc_horizontal.png b/base/data/vectorcanvastest/lineorientation/00_pc_horizontal.png
new file mode 100644
index 0000000..7bcd998
--- /dev/null
+++ b/base/data/vectorcanvastest/lineorientation/00_pc_horizontal.png
Binary files differ
diff --git a/base/data/vectorcanvastest/lineorientation/00_vc_horizontal.png b/base/data/vectorcanvastest/lineorientation/00_vc_horizontal.png
new file mode 100644
index 0000000..46c9b0a
--- /dev/null
+++ b/base/data/vectorcanvastest/lineorientation/00_vc_horizontal.png
Binary files differ
diff --git a/base/data/vectorcanvastest/lineorientation/01_pc_vertical.png b/base/data/vectorcanvastest/lineorientation/01_pc_vertical.png
new file mode 100644
index 0000000..09f41db
--- /dev/null
+++ b/base/data/vectorcanvastest/lineorientation/01_pc_vertical.png
Binary files differ
diff --git a/base/data/vectorcanvastest/lineorientation/01_vc_vertical.png b/base/data/vectorcanvastest/lineorientation/01_vc_vertical.png
new file mode 100644
index 0000000..7f5f1f7
--- /dev/null
+++ b/base/data/vectorcanvastest/lineorientation/01_vc_vertical.png
Binary files differ
diff --git a/base/data/vectorcanvastest/lineorientation/02_pc_horizontal_180.png b/base/data/vectorcanvastest/lineorientation/02_pc_horizontal_180.png
new file mode 100644
index 0000000..5966df6
--- /dev/null
+++ b/base/data/vectorcanvastest/lineorientation/02_pc_horizontal_180.png
Binary files differ
diff --git a/base/data/vectorcanvastest/lineorientation/02_vc_horizontal_180.png b/base/data/vectorcanvastest/lineorientation/02_vc_horizontal_180.png
new file mode 100644
index 0000000..e43a844
--- /dev/null
+++ b/base/data/vectorcanvastest/lineorientation/02_vc_horizontal_180.png
Binary files differ
diff --git a/base/data/vectorcanvastest/lineorientation/03_pc_vertical_180.png b/base/data/vectorcanvastest/lineorientation/03_pc_vertical_180.png
new file mode 100644
index 0000000..9ac4825
--- /dev/null
+++ b/base/data/vectorcanvastest/lineorientation/03_pc_vertical_180.png
Binary files differ
diff --git a/base/data/vectorcanvastest/lineorientation/03_vc_vertical_180.png b/base/data/vectorcanvastest/lineorientation/03_vc_vertical_180.png
new file mode 100644
index 0000000..d9e033a
--- /dev/null
+++ b/base/data/vectorcanvastest/lineorientation/03_vc_vertical_180.png
Binary files differ
diff --git a/base/data/vectorcanvastest/matrix/00_pc_translate1.png b/base/data/vectorcanvastest/matrix/00_pc_translate1.png
new file mode 100644
index 0000000..fe27cb3
--- /dev/null
+++ b/base/data/vectorcanvastest/matrix/00_pc_translate1.png
Binary files differ
diff --git a/base/data/vectorcanvastest/matrix/00_vc_translate1.png b/base/data/vectorcanvastest/matrix/00_vc_translate1.png
new file mode 100644
index 0000000..fe27cb3
--- /dev/null
+++ b/base/data/vectorcanvastest/matrix/00_vc_translate1.png
Binary files differ
diff --git a/base/data/vectorcanvastest/matrix/01_pc_translate2.png b/base/data/vectorcanvastest/matrix/01_pc_translate2.png
new file mode 100644
index 0000000..406bf57
--- /dev/null
+++ b/base/data/vectorcanvastest/matrix/01_pc_translate2.png
Binary files differ
diff --git a/base/data/vectorcanvastest/matrix/01_vc_translate2.png b/base/data/vectorcanvastest/matrix/01_vc_translate2.png
new file mode 100644
index 0000000..406bf57
--- /dev/null
+++ b/base/data/vectorcanvastest/matrix/01_vc_translate2.png
Binary files differ
diff --git a/base/data/vectorcanvastest/matrix/02_pc_scale.png b/base/data/vectorcanvastest/matrix/02_pc_scale.png
new file mode 100644
index 0000000..9e94fb0
--- /dev/null
+++ b/base/data/vectorcanvastest/matrix/02_pc_scale.png
Binary files differ
diff --git a/base/data/vectorcanvastest/matrix/02_vc_scale.png b/base/data/vectorcanvastest/matrix/02_vc_scale.png
new file mode 100644
index 0000000..fde62aa
--- /dev/null
+++ b/base/data/vectorcanvastest/matrix/02_vc_scale.png
Binary files differ
diff --git a/base/data/vectorcanvastest/matrix/03_pc_rotate.png b/base/data/vectorcanvastest/matrix/03_pc_rotate.png
new file mode 100644
index 0000000..7a43a2a
--- /dev/null
+++ b/base/data/vectorcanvastest/matrix/03_pc_rotate.png
Binary files differ
diff --git a/base/data/vectorcanvastest/matrix/03_vc_rotate.png b/base/data/vectorcanvastest/matrix/03_vc_rotate.png
new file mode 100644
index 0000000..7a22b7f
--- /dev/null
+++ b/base/data/vectorcanvastest/matrix/03_vc_rotate.png
Binary files differ
diff --git a/base/data/vectorcanvastest/patheffects/00_pc_dash_line.png b/base/data/vectorcanvastest/patheffects/00_pc_dash_line.png
new file mode 100644
index 0000000..e08d3e2
--- /dev/null
+++ b/base/data/vectorcanvastest/patheffects/00_pc_dash_line.png
Binary files differ
diff --git a/base/data/vectorcanvastest/patheffects/00_vc_dash_line.png b/base/data/vectorcanvastest/patheffects/00_vc_dash_line.png
new file mode 100644
index 0000000..e08d3e2
--- /dev/null
+++ b/base/data/vectorcanvastest/patheffects/00_vc_dash_line.png
Binary files differ
diff --git a/base/data/vectorcanvastest/patheffects/01_pc_dash_path.png b/base/data/vectorcanvastest/patheffects/01_pc_dash_path.png
new file mode 100644
index 0000000..3a30135
--- /dev/null
+++ b/base/data/vectorcanvastest/patheffects/01_pc_dash_path.png
Binary files differ
diff --git a/base/data/vectorcanvastest/patheffects/01_vc_dash_path.png b/base/data/vectorcanvastest/patheffects/01_vc_dash_path.png
new file mode 100644
index 0000000..7868b9a
--- /dev/null
+++ b/base/data/vectorcanvastest/patheffects/01_vc_dash_path.png
Binary files differ
diff --git a/base/data/vectorcanvastest/patheffects/02_pc_dash_rect.png b/base/data/vectorcanvastest/patheffects/02_pc_dash_rect.png
new file mode 100644
index 0000000..04f2ceb
--- /dev/null
+++ b/base/data/vectorcanvastest/patheffects/02_pc_dash_rect.png
Binary files differ
diff --git a/base/data/vectorcanvastest/patheffects/02_vc_dash_rect.png b/base/data/vectorcanvastest/patheffects/02_vc_dash_rect.png
new file mode 100644
index 0000000..5344eee
--- /dev/null
+++ b/base/data/vectorcanvastest/patheffects/02_vc_dash_rect.png
Binary files differ
diff --git a/base/data/vectorcanvastest/patheffects/03_pc_circle.png b/base/data/vectorcanvastest/patheffects/03_pc_circle.png
new file mode 100644
index 0000000..90fa28b
--- /dev/null
+++ b/base/data/vectorcanvastest/patheffects/03_pc_circle.png
Binary files differ
diff --git a/base/data/vectorcanvastest/patheffects/03_vc_circle.png b/base/data/vectorcanvastest/patheffects/03_vc_circle.png
new file mode 100644
index 0000000..8027297
--- /dev/null
+++ b/base/data/vectorcanvastest/patheffects/03_vc_circle.png
Binary files differ
diff --git a/base/data/vectorcanvastest/pathorientation/00_pc_drawpath_ltr.png b/base/data/vectorcanvastest/pathorientation/00_pc_drawpath_ltr.png
new file mode 100644
index 0000000..cefbf87
--- /dev/null
+++ b/base/data/vectorcanvastest/pathorientation/00_pc_drawpath_ltr.png
Binary files differ
diff --git a/base/data/vectorcanvastest/pathorientation/00_vc_drawpath_ltr.png b/base/data/vectorcanvastest/pathorientation/00_vc_drawpath_ltr.png
new file mode 100644
index 0000000..cefbf87
--- /dev/null
+++ b/base/data/vectorcanvastest/pathorientation/00_vc_drawpath_ltr.png
Binary files differ
diff --git a/base/data/vectorcanvastest/pathorientation/01_pc_drawpath_rtl.png b/base/data/vectorcanvastest/pathorientation/01_pc_drawpath_rtl.png
new file mode 100644
index 0000000..7bcd998
--- /dev/null
+++ b/base/data/vectorcanvastest/pathorientation/01_pc_drawpath_rtl.png
Binary files differ
diff --git a/base/data/vectorcanvastest/pathorientation/01_vc_drawpath_rtl.png b/base/data/vectorcanvastest/pathorientation/01_vc_drawpath_rtl.png
new file mode 100644
index 0000000..46c9b0a
--- /dev/null
+++ b/base/data/vectorcanvastest/pathorientation/01_vc_drawpath_rtl.png
Binary files differ
diff --git a/base/data/vectorcanvastest/uninitialized/00_pc_empty.png b/base/data/vectorcanvastest/uninitialized/00_pc_empty.png
new file mode 100644
index 0000000..dec6694
--- /dev/null
+++ b/base/data/vectorcanvastest/uninitialized/00_pc_empty.png
Binary files differ
diff --git a/base/data/vectorcanvastest/uninitialized/00_vc_empty.png b/base/data/vectorcanvastest/uninitialized/00_vc_empty.png
new file mode 100644
index 0000000..9cbff6e
--- /dev/null
+++ b/base/data/vectorcanvastest/uninitialized/00_vc_empty.png
Binary files differ
diff --git a/base/debug_message.cc b/base/debug_message.cc
new file mode 100644
index 0000000..e725dcf
--- /dev/null
+++ b/base/debug_message.cc
@@ -0,0 +1,42 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+
+// Display the command line. This program is designed to be called from
+// another process to display assertions. Since the other process has
+// complete control of our command line, we assume that it did *not*
+// add the program name as the first parameter. This allows us to just
+// show the command line directly as the message.
+int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
+                     LPSTR lpCmdLine, int nCmdShow) {
+  LPWSTR cmdline = GetCommandLineW();
+  MessageBox(NULL, cmdline, L"Kr\x00d8m", MB_TOPMOST);
+  return 0;
+}
diff --git a/base/debug_on_start.cc b/base/debug_on_start.cc
new file mode 100644
index 0000000..7ae6cd1
--- /dev/null
+++ b/base/debug_on_start.cc
@@ -0,0 +1,89 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#include <windows.h>
+
+#include "base/debug_on_start.h"
+
+#include "base/base_switches.h"
+#include "base/basictypes.h"
+#include "base/debug_util.h"
+
+// Minimalist implementation to try to find a command line argument. We can use
+// kernel32 exported functions but not the CRT functions because we're too early
+// in the process startup.
+// The code is not that bright and will find things like ---argument or
+// /-/argument.
+// Note: command_line is non-destructively modified.
+bool DebugOnStart::FindArgument(wchar_t* command_line, const wchar_t* argument)
+{
+  int argument_len = lstrlen(argument);
+  int command_line_len = lstrlen(command_line);
+  while (command_line_len > argument_len) {
+    wchar_t first_char = command_line[0];
+    wchar_t last_char = command_line[argument_len+1];
+    // Try to find an argument.
+    if ((first_char == L'-' || first_char == L'/') &&
+        (last_char == L' ' || last_char == 0 || last_char == L'=')) {
+      command_line[argument_len+1] = 0;
+      // Skip the - or /
+      if (lstrcmpi(command_line+1, argument) == 0) {
+        // Found it.
+        command_line[argument_len+1] = last_char;
+        return true;
+      }
+      // Fix back.
+      command_line[argument_len+1] = last_char;
+    }
+    // Continue searching.
+    ++command_line;
+    --command_line_len;
+  }
+  return false;
+}
+
+// static
+int __cdecl DebugOnStart::Init() {
+  // Try to find the argument.
+  if (FindArgument(GetCommandLine(), switches::kDebugOnStart)) {
+    // We can do 2 things here:
+    // - Ask for a debugger to attach to us. This involve reading the registry
+    //   key and creating the process.
+    // - Do a int3.
+
+    // It will fails if we run in a sandbox. That is expected.
+    DebugUtil::SpawnDebuggerOnProcess(GetCurrentProcessId());
+
+    // Wait for a debugger to come take us.
+    DebugUtil::WaitForDebugger(60, false);
+  } else if (FindArgument(GetCommandLine(), switches::kWaitForDebugger)) {
+    // Wait for a debugger to come take us.
+    DebugUtil::WaitForDebugger(60, true);
+  }
+  return 0;
+}
diff --git a/base/debug_on_start.h b/base/debug_on_start.h
new file mode 100644
index 0000000..87afe9b
--- /dev/null
+++ b/base/debug_on_start.h
@@ -0,0 +1,75 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Define the necessary code and global data to look for kDebugOnStart command
+// line argument. When the command line argument is detected, it invokes the
+// debugger, if no system-wide debugger is registered, a debug break is done.
+
+#ifndef BASE_DEBUG_ON_START_H__
+#define BASE_DEBUG_ON_START_H__
+
+// This only works on Windows.
+#ifdef _WIN32
+
+#ifndef DECLSPEC_SELECTANY
+#define DECLSPEC_SELECTANY  __declspec(selectany)
+#endif
+
+// Debug on start functions and data.
+class DebugOnStart {
+ public:
+  // Expected function type in the .CRT$XI* section.
+  // Note: See VC\crt\src\internal.h for reference.
+  typedef int  (__cdecl *PIFV)(void);
+
+  // Looks at the command line for kDebugOnStart argument. If found, it invokes
+  // the debugger, if this fails, it crashes.
+  static int __cdecl Init();
+
+  // Returns true if the 'argument' is present in the 'command_line'. It does
+  // not use the CRT, only Kernel32 functions.
+  static bool FindArgument(wchar_t* command_line, const wchar_t* argument);
+};
+
+// Set the function pointer to our function to look for a crash on start. The
+// XIB section is started pretty early in the program initialization so in
+// theory it should be called before any user created global variable
+// initialization code and CRT initialization code.
+// Note: See VC\crt\src\defsects.inc and VC\crt\src\crt0.c for reference.
+
+// "Fix" the data section.
+#pragma data_seg(push, ".CRT$XIB")
+// Declare the pointer so the CRT will find it.
+DECLSPEC_SELECTANY DebugOnStart::PIFV debug_on_start = &DebugOnStart::Init;
+// Fix back the data segment.
+#pragma data_seg(pop)
+
+#endif  // _WIN32
+
+#endif  // BASE_DEBUG_ON_START_H__
diff --git a/base/debug_util.cc b/base/debug_util.cc
new file mode 100644
index 0000000..fb60f5a
--- /dev/null
+++ b/base/debug_util.cc
@@ -0,0 +1,132 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+
+#include "base/basictypes.h"
+#include "base/debug_util.h"
+
+namespace {
+
+// Minimalist key reader.
+// Note: Does not use the CRT.
+bool RegReadString(HKEY root, const wchar_t* subkey,
+                   const wchar_t* value_name, wchar_t* buffer, int* len) {
+  HKEY key = NULL;
+  DWORD res = RegOpenKeyEx(root, subkey, 0, KEY_READ, &key);
+  if (ERROR_SUCCESS != res || key == NULL)
+    return false;
+
+  DWORD type = 0;
+  DWORD buffer_size = *len * sizeof(wchar_t);
+  // We don't support REG_EXPAND_SZ.
+  res = RegQueryValueEx(key, value_name, NULL, &type,
+                        reinterpret_cast<BYTE*>(buffer), &buffer_size);
+  if (ERROR_SUCCESS == res && buffer_size != 0 && type == REG_SZ) {
+    // Make sure the buffer is NULL terminated.
+    buffer[*len - 1] = 0;
+    *len = lstrlen(buffer);
+    RegCloseKey(key);
+    return true;
+  }
+  RegCloseKey(key);
+  return false;
+}
+
+// Replaces each "%ld" in input per a value. Not efficient but it works.
+// Note: Does not use the CRT.
+bool StringReplace(const wchar_t* input, int value, wchar_t* output,
+                   int output_len) {
+  memset(output, 0, output_len*sizeof(wchar_t));
+  int input_len = lstrlen(input);
+
+  for (int i = 0; i < input_len; ++i) {
+    int current_output_len = lstrlen(output);
+
+    if (input[i] == L'%' && input[i + 1] == L'l' && input[i + 2] == L'd') {
+      // Make sure we have enough place left.
+      if ((current_output_len + 12) >= output_len)
+        return false;
+
+      // Cheap _itow().
+      wsprintf(output+current_output_len, L"%d", value);
+      i += 2;
+    } else {
+      if (current_output_len >= output_len)
+        return false;
+      output[current_output_len] = input[i];
+    }
+  }
+  return true;
+}
+
+}  // namespace
+
+// Note: Does not use the CRT.
+bool DebugUtil::SpawnDebuggerOnProcess(unsigned process_id) {
+  wchar_t reg_value[1026];
+  int len = arraysize(reg_value);
+  if (RegReadString(HKEY_LOCAL_MACHINE,
+      L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug",
+      L"Debugger", reg_value, &len)) {
+    wchar_t command_line[1026];
+    if (StringReplace(reg_value, process_id, command_line,
+                      arraysize(command_line))) {
+      // We don't mind if the debugger is present because it will simply fail
+      // to attach to this process.
+      STARTUPINFO startup_info = {0};
+      startup_info.cb = sizeof(startup_info);
+      PROCESS_INFORMATION process_info = {0};
+
+      if (CreateProcess(NULL, command_line, NULL, NULL, FALSE, 0, NULL, NULL,
+                        &startup_info, &process_info)) {
+        CloseHandle(process_info.hThread);
+        WaitForInputIdle(process_info.hProcess, 10000);
+        CloseHandle(process_info.hProcess);
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+// Note: Does not use the CRT.
+bool DebugUtil::WaitForDebugger(int wait_seconds, bool silent) {
+  for (int i = 0; i < wait_seconds * 10; ++i) {
+    if (IsDebuggerPresent()) {
+      if (!silent) {
+        // If you hit here, you just use -debug-on-start or -debug-children.
+        __debugbreak();
+      }
+      return true;
+    }
+    Sleep(100);
+  }
+  return false;
+}
diff --git a/base/debug_util.h b/base/debug_util.h
new file mode 100644
index 0000000..54381e5
--- /dev/null
+++ b/base/debug_util.h
@@ -0,0 +1,44 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_DEBUG_UTIL_H__
+#define BASE_DEBUG_UTIL_H__
+
+class DebugUtil {
+ public:
+  // Starts the registered system-wide JIT debugger to attach it to specified
+  // process.
+  static bool SpawnDebuggerOnProcess(unsigned process_id);
+
+  // Waits wait_seconds seconds for a debugger to attach to the current process.
+  // When silent is false, an exception is thrown when a debugger is detected.
+  static bool WaitForDebugger(int wait_seconds, bool silent);
+};
+
+#endif  // BASE_DEBUG_UTIL_H__
diff --git a/base/event_recorder.cc b/base/event_recorder.cc
new file mode 100644
index 0000000..b4c89a3
--- /dev/null
+++ b/base/event_recorder.cc
@@ -0,0 +1,281 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/event_recorder.h"
+
+#include <mmsystem.h>
+
+#include "base/logging.h"
+#include "base/time.h"
+
+// A note about time.
+// For perfect playback of events, you'd like a very accurate timer
+// so that events are played back at exactly the same time that
+// they were recorded.  However, windows has a clock which is only
+// granular to ~15ms.  We see more consistent event playback when
+// using a higher resolution timer.  To do this, we use the
+// timeGetTime API instead of the default GetTickCount() API.
+
+namespace base {
+
+EventRecorder* EventRecorder::current_ = NULL;
+
+LRESULT CALLBACK StaticRecordWndProc(int nCode, WPARAM wParam,
+                                     LPARAM lParam) {
+  CHECK(EventRecorder::current());
+  return EventRecorder::current()->RecordWndProc(nCode, wParam, lParam);
+}
+
+LRESULT CALLBACK StaticPlaybackWndProc(int nCode, WPARAM wParam,
+                                       LPARAM lParam) {
+  CHECK(EventRecorder::current());
+  return EventRecorder::current()->PlaybackWndProc(nCode, wParam, lParam);
+}
+
+EventRecorder::~EventRecorder() {
+  // Try to assert early if the caller deletes the recorder
+  // while it is still in use.
+  DCHECK(!journal_hook_);
+  DCHECK(!is_recording_ && !is_playing_);
+}
+
+bool EventRecorder::StartRecording(std::wstring& filename) {
+  if (journal_hook_ != NULL)
+    return false;
+  if (is_recording_ || is_playing_)
+    return false;
+
+  // Open the recording file.
+  DCHECK(file_ == NULL);
+  if (_wfopen_s(&file_, filename.c_str(), L"wb+") != 0) {
+    DLOG(ERROR) << "EventRecorder could not open log file";
+    return false;
+  }
+
+  // Set the faster clock, if possible.
+  ::timeBeginPeriod(1);
+
+  // Set the recording hook.  JOURNALRECORD can only be used as a global hook.
+  journal_hook_ = ::SetWindowsHookEx(WH_JOURNALRECORD, StaticRecordWndProc,
+                                     GetModuleHandle(NULL), 0);
+  if (!journal_hook_) {
+    DLOG(ERROR) << "EventRecorder Record Hook failed";
+    fclose(file_);
+    return false;
+  }
+
+  is_recording_ = true;
+  return true;
+}
+
+void EventRecorder::StopRecording() {
+  if (is_recording_) {
+    DCHECK(journal_hook_ != NULL);
+
+    if (!::UnhookWindowsHookEx(journal_hook_)) {
+      DLOG(ERROR) << "EventRecorder Unhook failed";
+      // Nothing else we can really do here.
+      return;
+    }
+
+    ::timeEndPeriod(1);
+
+    DCHECK(file_ != NULL);
+    fclose(file_);
+    file_ = NULL;
+
+    journal_hook_ = NULL;
+    is_recording_ = false;
+  }
+}
+
+bool EventRecorder::StartPlayback(std::wstring& filename) {
+  if (journal_hook_ != NULL)
+    return false;
+  if (is_recording_ || is_playing_)
+    return false;
+
+  // Open the recording file.
+  DCHECK(file_ == NULL);
+  if (_wfopen_s(&file_, filename.c_str(), L"rb") != 0) {
+    DLOG(ERROR) << "EventRecorder Playback could not open log file";
+    return false;
+  }
+  // Read the first event from the record.
+  if (fread(&playback_msg_, sizeof(EVENTMSG), 1, file_) != 1) {
+    DLOG(ERROR) << "EventRecorder Playback has no records!";
+    fclose(file_);
+    return false;
+  }
+
+  // Set the faster clock, if possible.
+  ::timeBeginPeriod(1);
+
+  // Playback time is tricky.  When playing back, we read a series of events,
+  // each with timeouts.  Simply subtracting the delta between two timers will
+  // lead to fast playback (about 2x speed).  The API has two events, one
+  // which advances to the next event (HC_SKIP), and another that requests the
+  // event (HC_GETNEXT).  The same event will be requested multiple times.
+  // Each time the event is requested, we must calculate the new delay.
+  // To do this, we track the start time of the playback, and constantly
+  // re-compute the delay.   I mention this only because I saw two examples
+  // of how to use this code on the net, and both were broken :-)
+  playback_start_time_ = timeGetTime();
+  playback_first_msg_time_ = playback_msg_.time;
+
+  // Set the hook.  JOURNALPLAYBACK can only be used as a global hook.
+  journal_hook_ = ::SetWindowsHookEx(WH_JOURNALPLAYBACK, StaticPlaybackWndProc,
+                                     GetModuleHandle(NULL), 0);
+  if (!journal_hook_) {
+    DLOG(ERROR) << "EventRecorder Playback Hook failed";
+    return false;
+  }
+
+  is_playing_ = true;
+
+  return true;
+}
+
+void EventRecorder::StopPlayback() {
+  if (is_playing_) {
+    DCHECK(journal_hook_ != NULL);
+
+    if (!::UnhookWindowsHookEx(journal_hook_)) {
+      DLOG(ERROR) << "EventRecorder Unhook failed";
+      // Nothing else we can really do here.
+    }
+
+    DCHECK(file_ != NULL);
+    fclose(file_);
+    file_ = NULL;
+
+    ::timeEndPeriod(1);
+
+    journal_hook_ = NULL;
+    is_playing_ = false;
+  }
+}
+
+// Windows callback hook for the recorder.
+LRESULT EventRecorder::RecordWndProc(int nCode, WPARAM wParam, LPARAM lParam) {
+  static bool recording_enabled = true;
+  EVENTMSG *msg_ptr = NULL;
+
+  // The API says we have to do this.
+  // See http://msdn2.microsoft.com/en-us/library/ms644983(VS.85).aspx
+  if (nCode < 0)
+    return ::CallNextHookEx(journal_hook_, nCode, wParam, lParam);
+
+  // Check for the break key being pressed and stop recording.
+  if (::GetKeyState(VK_CANCEL) & 0x8000) {
+    StopRecording();
+    return ::CallNextHookEx(journal_hook_, nCode, wParam, lParam);
+  }
+
+  // The Journal Recorder must stop recording events when system modal
+  // dialogs are present. (see msdn link above)
+  switch(nCode)
+  {
+    case HC_SYSMODALON:
+	    recording_enabled = false;
+      break;
+    case HC_SYSMODALOFF:
+	    recording_enabled = true;
+      break;
+  }
+
+  if (nCode == HC_ACTION && recording_enabled) {
+    // Aha - we have an event to record.
+    msg_ptr = reinterpret_cast<EVENTMSG*>(lParam);
+    msg_ptr->time = timeGetTime();
+    fwrite(msg_ptr, sizeof(EVENTMSG), 1, file_);
+    fflush(file_);
+  }
+
+  return CallNextHookEx(journal_hook_, nCode, wParam, lParam);
+}
+
+// Windows callback for the playback mode.
+LRESULT EventRecorder::PlaybackWndProc(int nCode, WPARAM wParam, LPARAM lParam)
+{
+  static bool playback_enabled = true;
+  int delay = 0;
+
+  switch(nCode)
+  {
+    // A system modal dialog box is being displayed.  Stop playing back
+    // messages.
+    case HC_SYSMODALON:
+	    playback_enabled = false;
+	    break;
+
+    // A system modal dialog box is destroyed.  We can start playing back
+    // messages again.
+    case HC_SYSMODALOFF:
+	    playback_enabled = true;
+	    break;
+
+    // Prepare to copy the next mouse or keyboard event to playback.
+    case HC_SKIP:
+	    if (!playback_enabled)
+	      break;
+
+      // Read the next event from the record.
+      if (fread(&playback_msg_, sizeof(EVENTMSG), 1, file_) != 1)
+        this->StopPlayback();
+	    break;
+
+    // Copy the mouse or keyboard event to the EVENTMSG structure in lParam.
+    case HC_GETNEXT:
+	    if (!playback_enabled)
+        break;
+
+      memcpy(reinterpret_cast<void*>(lParam), &playback_msg_, sizeof(playback_msg_));
+
+      // The return value is the amount of time (in milliseconds) to wait
+      // before playing back the next message in the playback queue.  Each
+      // time this is called, we recalculate the delay relative to our current
+      // wall clock.
+      delay = (playback_msg_.time - playback_first_msg_time_) -
+              (timeGetTime() - playback_start_time_);
+      if (delay < 0)
+        delay = 0;
+      return delay;
+
+    // An application has called PeekMessage with wRemoveMsg set to PM_NOREMOVE
+    // indicating that the message is not removed from the message queue after
+    // PeekMessage processing.
+    case HC_NOREMOVE:
+      break;
+  }
+
+  return CallNextHookEx(journal_hook_, nCode, wParam, lParam);
+}
+
+}  // namespace base
\ No newline at end of file
diff --git a/base/event_recorder.h b/base/event_recorder.h
new file mode 100644
index 0000000..71b815f
--- /dev/null
+++ b/base/event_recorder.h
@@ -0,0 +1,115 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_EVENT_RECORDER_H_
+#define BASE_EVENT_RECORDER_H_
+
+#include <string>
+#include <windows.h>
+#include "base/basictypes.h"
+
+namespace base {
+
+// A class for recording and playing back keyboard and mouse input events.
+//
+// Note - if you record events, and the playback with the windows in
+//        different sizes or positions, the playback will fail.  When
+//        recording and playing, you should move the relevant windows
+//        to constant sizes and locations.
+// TODO(mbelshe) For now this is a singleton.  I believe that this class
+//        could be easily modified to:
+//             support two simultaneous recorders
+//             be playing back events while already recording events.
+//        Why?  Imagine if the product had a "record a macro" feature.
+//        You might be recording globally, while recording or playing back
+//        a macro.  I don't think two playbacks make sense.
+class EventRecorder {
+ public:
+  // Get the singleton EventRecorder.
+  // We can only handle one recorder/player at a time.
+  static EventRecorder* current() {
+    if (!current_)
+      current_ = new EventRecorder();
+    return current_;
+  }
+
+  // Starts recording events.
+  // Will clobber the file if it already exists.
+  // Returns true on success, or false if an error occurred.
+  bool StartRecording(std::wstring &filename);
+
+  // Stops recording.
+  void StopRecording();
+
+  // Is the EventRecorder currently recording.
+  bool is_recording() const { return is_recording_; }
+
+  // Plays events previously recorded.
+  // Returns true on success, or false if an error occurred.
+  bool StartPlayback(std::wstring &filename);
+
+  // Stops playback.
+  void StopPlayback();
+
+  // Is the EventRecorder currently playing.
+  bool is_playing() const { return is_playing_; }
+
+  // C-style callbacks for the EventRecorder.
+  // Used for internal purposes only.
+  LRESULT RecordWndProc(int nCode, WPARAM wParam, LPARAM lParam);
+  LRESULT PlaybackWndProc(int nCode, WPARAM wParam, LPARAM lParam);
+
+ private:
+  // Create a new EventRecorder.  Events are saved to the file filename.
+  // If the file already exists, it will be deleted before recording
+  // starts.
+  explicit EventRecorder()
+      : is_recording_(false),
+        is_playing_(false),
+        journal_hook_(NULL),
+        file_(NULL) {
+  }
+  ~EventRecorder();
+
+  static EventRecorder* current_;  // Our singleton.
+
+  bool is_recording_;
+  bool is_playing_;
+  HHOOK journal_hook_;
+  FILE* file_;
+  EVENTMSG playback_msg_;
+  int playback_first_msg_time_;
+  int playback_start_time_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(EventRecorder);
+};
+
+}  // namespace base
+
+#endif // BASE_EVENT_RECORDER_H_
diff --git a/base/file_util.cc b/base/file_util.cc
new file mode 100644
index 0000000..83cf534
--- /dev/null
+++ b/base/file_util.cc
@@ -0,0 +1,852 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/file_util.h"
+
+#include <windows.h>
+#include <shellapi.h>
+#include <shlobj.h>
+#include <time.h>
+#include <fstream>
+#include <string>
+
+#include "base/logging.h"
+#include "base/scoped_handle.h"
+#include "base/string_util.h"
+#include "base/win_util.h"
+#include "unicode/uniset.h"
+
+using std::wstring;
+
+namespace file_util {
+
+const wchar_t kPathSeparator = L'\\';
+const wchar_t kExtensionSeparator = L'.';
+
+bool EndsWithSeparator(std::wstring* path) {
+  return (!path->empty() && (*path)[path->length() - 1] == kPathSeparator);
+}
+
+void TrimTrailingSeparator(std::wstring* dir) {
+  while (EndsWithSeparator(dir))
+    dir->resize(dir->length() - 1);
+}
+
+void UpOneDirectory(std::wstring* dir) {
+  TrimTrailingSeparator(dir);
+
+  wstring::size_type last_sep = dir->find_last_of(kPathSeparator);
+  if (last_sep != wstring::npos)
+    dir->resize(last_sep);
+}
+
+void UpOneDirectoryOrEmpty(std::wstring* dir) {
+  TrimTrailingSeparator(dir);
+
+  wstring::size_type last_sep = dir->find_last_of(kPathSeparator);
+  if (last_sep != wstring::npos)
+    dir->resize(last_sep);
+  else
+    dir->clear();
+}
+
+void TrimFilename(std::wstring* path) {
+  if (EndsWithSeparator(path)) {
+    TrimTrailingSeparator(path);
+  } else {
+    wstring::size_type last_sep = path->find_last_of(kPathSeparator);
+    if (last_sep != wstring::npos)
+      path->resize(last_sep);
+  }
+}
+
+// TODO(mpcomplete): Make this platform-independent, etc.
+wstring GetFilenameFromPath(const wstring& path) {
+  wstring::size_type pos = path.find_last_of(L"\\/");
+  return wstring(path, pos == wstring::npos ? 0 : pos+1);
+}
+
+wstring GetFileExtensionFromPath(const wstring& path) {
+  wstring file_name = GetFilenameFromPath(path);
+  wstring::size_type last_dot = file_name.rfind(L'.');
+  return wstring(last_dot == wstring::npos? L"" : file_name, last_dot+1);
+}
+
+void AppendToPath(std::wstring* path, const std::wstring& new_ending) {
+  if (!path) {
+    NOTREACHED();
+    return;  // Don't crash in this function in release builds.
+  }
+
+  if (!EndsWithSeparator(path))
+    path->push_back(kPathSeparator);
+  path->append(new_ending);
+}
+
+void InsertBeforeExtension(std::wstring* path, const std::wstring& suffix) {
+  DCHECK(path);
+
+  const wstring::size_type last_dot = path->rfind(kExtensionSeparator);
+  const wstring::size_type last_sep = path->rfind(kPathSeparator);
+
+  if (last_dot == wstring::npos ||
+      (last_sep != wstring::npos && last_dot < last_sep)) {
+    // The path looks something like "C:\pics.old\jojo" or "C:\pics\jojo".
+    // We should just append the suffix to the entire path.
+    path->append(suffix);
+    return;
+  }
+
+  path->insert(last_dot, suffix);
+}
+
+void ReplaceIllegalCharacters(std::wstring* file_name, int replace_char) {
+  DCHECK(file_name);
+
+  // Control characters, formating characters, non-characters, and
+  // some printable ASCII characters regarded as dangerous ('"*/:<>?\\').
+  // See  http://blogs.msdn.com/michkap/archive/2006/11/03/941420.aspx
+  // and http://msdn2.microsoft.com/en-us/library/Aa365247.aspx
+  // TODO(jungshik): Revisit the set. ZWJ and ZWNJ are excluded because they
+  // are legitimate in Arabic and some S/SE Asian scripts. However, when used
+  // elsewhere, they can be confusing/problematic.
+  // Also, consider wrapping the set with our Singleton class to create and
+  // freeze it only once. Note that there's a trade-off between memory and
+  // speed.
+
+  UErrorCode status = U_ZERO_ERROR;
+#ifdef U_WCHAR_IS_UTF16
+  UnicodeSet illegal_characters(UnicodeString(
+      L"[[\"*/:<>?\\\\|][:Cc:][:Cf:] - [\u200c\u200d]]"), status);
+#else
+  UnicodeSet illegal_characters(UNICODE_STRING_SIMPLE(
+      "[[\"*/:<>?\\\\|][:Cc:][:Cf:] - [\\u200c\\u200d]]").unescape(), status);
+#endif
+  DCHECK(U_SUCCESS(status));
+  // Add non-characters. If this becomes a performance bottleneck by
+  // any chance, check |ucs4 & 0xFFFEu == 0xFFFEu|, instead.
+  illegal_characters.add(0xFDD0, 0xFDEF);
+  for (int i = 0; i <= 0x10; ++i) {
+    int plane_base = 0x10000 * i;
+    illegal_characters.add(plane_base + 0xFFFE, plane_base + 0xFFFF);
+  }
+  illegal_characters.freeze();
+  DCHECK(!illegal_characters.contains(replace_char) && replace_char < 0x10000);
+
+  // Remove leading and trailing whitespace.
+  TrimWhitespace(*file_name, TRIM_ALL, file_name);
+
+  std::wstring::size_type i = 0;
+  std::wstring::size_type length = file_name->size();
+#ifdef U_WCHAR_IS_UTF16
+  // Using |span| method of UnicodeSet might speed things up a bit, but
+  // it's not likely to matter here.
+  const wchar_t* wstr = file_name->data();
+  std::wstring temp;
+  temp.reserve(length);
+  while (i < length) {
+    UChar32 ucs4;
+    std::wstring::size_type prev = i;
+    U16_NEXT(wstr, i, length, ucs4);
+    if (illegal_characters.contains(ucs4)) {
+      temp.push_back(replace_char);
+    } else if (ucs4 < 0x10000) {
+      temp.push_back(ucs4);
+    } else {
+      temp.push_back(wstr[prev]);
+      temp.push_back(wstr[prev + 1]);
+    }
+  }
+  file_name->swap(temp);
+#elif defined(U_WCHAR_IS_UTF32)
+  while (i < length) {
+    if (illegal_characters.contains(wstr[i])) {
+      *file_name[i] = replace_char;
+    }
+  }
+#else
+#error wchar_t* should be either UTF-16 or UTF-32
+#endif
+}
+
+void ReplaceExtension(std::wstring* file_name, const std::wstring& extension) {
+  const wstring::size_type last_dot = file_name->rfind(L'.');
+  wstring result = file_name->substr(0, last_dot);
+  if (!extension.empty() && extension != L".") {
+    if (extension.at(0) != L'.')
+      result.append(L".");
+    result.append(extension);
+  }
+  file_name->swap(result);
+}
+
+wstring GetDirectoryFromPath(const std::wstring& path) {
+  wchar_t path_buffer[MAX_PATH];
+  wchar_t* file_ptr = NULL;
+  if (GetFullPathName(path.c_str(), MAX_PATH, path_buffer, &file_ptr) == 0)
+    return L"";
+
+  wstring::size_type nc = file_ptr ? file_ptr - path_buffer : path.length();
+  wstring directory(path, 0, nc);
+  TrimTrailingSeparator(&directory);
+  return directory;
+}
+
+int CountFilesCreatedAfter(const std::wstring& path,
+                           const FILETIME& comparison_time) {
+  int file_count = 0;
+
+  WIN32_FIND_DATA find_file_data;
+  std::wstring filename_spec = path + L"\\*";  // All files in given dir
+  HANDLE find_handle = FindFirstFile(filename_spec.c_str(), &find_file_data);
+  if (find_handle != INVALID_HANDLE_VALUE) {
+    do {
+      // Don't count current or parent directories.
+      if ((wcscmp(find_file_data.cFileName, L"..") == 0) ||
+          (wcscmp(find_file_data.cFileName, L".") == 0))
+        continue;
+
+      long result = CompareFileTime(&find_file_data.ftCreationTime,
+                                    &comparison_time);
+      // File was created after or on comparison time
+      if ((result == 1) || (result == 0))
+        ++file_count;
+    } while (FindNextFile(find_handle,  &find_file_data));
+    FindClose(find_handle);
+  }
+
+  return file_count;
+}
+
+bool Delete(const std::wstring& path, bool recursive) {
+  if (path.length() >= MAX_PATH)
+    return false;
+
+  // If we're not recursing use DeleteFile; it should be faster. DeleteFile
+  // fails if passed a directory though, which is why we fall through on
+  // failure to the SHFileOperation.
+  if (!recursive && DeleteFile(path.c_str()) != 0)
+    return true;
+
+  // SHFILEOPSTRUCT wants the path to be terminated with two NULLs,
+  // so we have to use wcscpy because wcscpy_s writes non-NULLs
+  // into the rest of the buffer.
+  wchar_t double_terminated_path[MAX_PATH + 1] = {0};
+#pragma warning(suppress:4996)  // don't complain about wcscpy deprecation
+  wcscpy(double_terminated_path, path.c_str());
+
+  SHFILEOPSTRUCT file_operation = {0};
+  file_operation.wFunc = FO_DELETE;
+  file_operation.pFrom = double_terminated_path;
+  file_operation.fFlags = FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION;
+  if (!recursive)
+    file_operation.fFlags |= FOF_NORECURSION | FOF_FILESONLY;
+  return (SHFileOperation(&file_operation) == 0);
+}
+
+bool Move(const std::wstring& from_path, const std::wstring& to_path) {
+  // NOTE: I suspect we could support longer paths, but that would involve
+  // analyzing all our usage of files.
+  if (from_path.length() >= MAX_PATH || to_path.length() >= MAX_PATH)
+    return false;
+  return (MoveFileEx(from_path.c_str(), to_path.c_str(),
+                     MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING) != 0);
+}
+
+bool CopyFile(const std::wstring& from_path, const std::wstring& to_path) {
+  // NOTE: I suspect we could support longer paths, but that would involve
+  // analyzing all our usage of files.
+  if (from_path.length() >= MAX_PATH || to_path.length() >= MAX_PATH)
+    return false;
+  return (::CopyFile(from_path.c_str(), to_path.c_str(), false) != 0);
+}
+
+bool ShellCopy(const std::wstring& from_path, const std::wstring& to_path,
+               bool recursive) {
+  // NOTE: I suspect we could support longer paths, but that would involve
+  // analyzing all our usage of files.
+  if (from_path.length() >= MAX_PATH || to_path.length() >= MAX_PATH)
+    return false;
+
+  // SHFILEOPSTRUCT wants the path to be terminated with two NULLs,
+  // so we have to use wcscpy because wcscpy_s writes non-NULLs
+  // into the rest of the buffer.
+  wchar_t double_terminated_path_from[MAX_PATH + 1] = {0};
+  wchar_t double_terminated_path_to[MAX_PATH + 1] = {0};
+#pragma warning(suppress:4996)  // don't complain about wcscpy deprecation
+  wcscpy(double_terminated_path_from, from_path.c_str());
+#pragma warning(suppress:4996)  // don't complain about wcscpy deprecation
+  wcscpy(double_terminated_path_to, to_path.c_str());
+
+  SHFILEOPSTRUCT file_operation = {0};
+  file_operation.wFunc = FO_COPY;
+  file_operation.pFrom = double_terminated_path_from;
+  file_operation.pTo = double_terminated_path_to;
+  file_operation.fFlags = FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION |
+                          FOF_NOCONFIRMMKDIR;
+  if (!recursive)
+    file_operation.fFlags |= FOF_NORECURSION | FOF_FILESONLY;
+
+  return (SHFileOperation(&file_operation) == 0);
+}
+
+bool CopyDirectory(const std::wstring& from_path, const std::wstring& to_path,
+                   bool recursive) {
+  if (recursive)
+    return ShellCopy(from_path, to_path, true);
+
+  // Instead of creating a new directory, we copy the old one to include the
+  // security information of the folder as part of the copy.
+  if (!PathExists(to_path)) {
+    // Except that Vista fails to do that, and instead do a recursive copy if
+    // the target directory doesn't exist.
+    if (win_util::GetWinVersion() >= win_util::WINVERSION_VISTA)
+      CreateDirectory(to_path);
+    else
+      ShellCopy(from_path, to_path, false);
+  }
+
+  std::wstring directory(from_path);
+  AppendToPath(&directory, L"*.*");
+  return ShellCopy(directory, to_path, false);
+}
+
+bool PathExists(const std::wstring& path) {
+  return (GetFileAttributes(path.c_str()) != INVALID_FILE_ATTRIBUTES);
+}
+
+bool PathIsWritable(const std::wstring& path) {
+  HANDLE dir =
+      CreateFile(path.c_str(), FILE_ADD_FILE,
+                 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                 NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+
+  if (dir == INVALID_HANDLE_VALUE)
+    return false;
+
+  CloseHandle(dir);
+  return true;
+}
+
+bool GetFileCreationLocalTimeFromHandle(HANDLE file_handle,
+                                        LPSYSTEMTIME creation_time) {
+  if (!file_handle)
+    return false;
+
+  FILETIME utc_filetime;
+  if (!GetFileTime(file_handle, &utc_filetime, NULL, NULL))
+    return false;
+
+  FILETIME local_filetime;
+  if (!FileTimeToLocalFileTime(&utc_filetime, &local_filetime))
+    return false;
+
+  return !!FileTimeToSystemTime(&local_filetime, creation_time);
+}
+
+bool GetFileCreationLocalTime(const std::wstring& filename,
+                              LPSYSTEMTIME creation_time) {
+  ScopedHandle file_handle(
+      CreateFile(filename.c_str(), GENERIC_READ,
+                 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+                 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
+  return GetFileCreationLocalTimeFromHandle(file_handle.Get(), creation_time);
+}
+
+bool ContentsEqual(const std::wstring& filename1,
+                   const std::wstring& filename2) {
+  // We open the file in binary format even if they are text files because
+  // we are just comparing that bytes are exactly same in both files and not
+  // doing anything smart with text formatting.
+  std::ifstream file1(filename1.c_str(), std::ios::in | std::ios::binary);
+  std::ifstream file2(filename2.c_str(), std::ios::in | std::ios::binary);
+
+  // Even if both files aren't openable (and thus, in some sense, "equal"),
+  // any unusable file yields a result of "false".
+  if (!file1.is_open() || !file2.is_open())
+    return false;
+
+  const int BUFFER_SIZE = 2056;
+  char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE];
+  do {
+    file1.read(buffer1, BUFFER_SIZE);
+    file2.read(buffer2, BUFFER_SIZE);
+
+    if ((file1.eof() && !file2.eof()) ||
+        (!file1.eof() && file2.eof()) ||
+        (file1.gcount() != file2.gcount()) ||
+        (memcmp(buffer1, buffer2, file1.gcount()))) {
+      file1.close();
+      file2.close();
+      return false;
+    }
+  } while (!file1.eof() && !file2.eof());
+
+  file1.close();
+  file2.close();
+  return true;
+}
+
+bool ReadFileToString(const std::wstring& path, std::string* contents) {
+  FILE* file;
+  errno_t err = _wfopen_s(&file, path.c_str(), L"rbS");
+  if (err != 0)
+    return false;
+
+  char buf[1 << 16];
+  size_t len;
+  while ((len = fread(buf, 1, sizeof(buf), file)) > 0) {
+    contents->append(buf, len);
+  }
+  fclose(file);
+
+  return true;
+}
+
+bool ResolveShortcut(std::wstring* path) {
+  HRESULT result;
+  IShellLink *shell = NULL;
+  bool is_resolved = false;
+
+  // Get pointer to the IShellLink interface
+  result = CoCreateInstance(CLSID_ShellLink, NULL,
+                            CLSCTX_INPROC_SERVER, IID_IShellLink,
+                            reinterpret_cast<LPVOID*>(&shell));
+  if (SUCCEEDED(result)) {
+    IPersistFile *persist = NULL;
+    // Query IShellLink for the IPersistFile interface
+    result = shell->QueryInterface(IID_IPersistFile,
+                                   reinterpret_cast<LPVOID*>(&persist));
+    if (SUCCEEDED(result)) {
+      WCHAR temp_path[MAX_PATH];
+      // Load the shell link
+      result = persist->Load(path->c_str(), STGM_READ);
+      if (SUCCEEDED(result)) {
+        // Try to find the target of a shortcut
+        result = shell->Resolve(0, SLR_NO_UI);
+        if (SUCCEEDED(result)) {
+          result = shell->GetPath(temp_path, MAX_PATH,
+                                  NULL, SLGP_UNCPRIORITY);
+          *path = temp_path;
+          is_resolved = true;
+        }
+      }
+    }
+    if (persist)
+      persist->Release();
+  }
+  if (shell)
+    shell->Release();
+
+  return is_resolved;
+}
+
+bool CreateShortcutLink(const wchar_t *source, const wchar_t *destination,
+                        const wchar_t *working_dir, const wchar_t *arguments,
+                        const wchar_t *description, const wchar_t *icon,
+                        int icon_index) {
+  IShellLink *i_shell_link = NULL;
+  IPersistFile *i_persist_file = NULL;
+
+  // Get pointer to the IShellLink interface
+  HRESULT result = CoCreateInstance(CLSID_ShellLink, NULL,
+                                    CLSCTX_INPROC_SERVER, IID_IShellLink,
+                                    reinterpret_cast<LPVOID*>(&i_shell_link));
+  if (FAILED(result))
+    return false;
+
+  // Query IShellLink for the IPersistFile interface
+  result = i_shell_link->QueryInterface(IID_IPersistFile,
+      reinterpret_cast<LPVOID*>(&i_persist_file));
+  if (FAILED(result)) {
+    i_shell_link->Release();
+    return false;
+  }
+
+  if (FAILED(i_shell_link->SetPath(source))) {
+    i_persist_file->Release();
+    i_shell_link->Release();
+    return false;
+  }
+
+  if (working_dir && FAILED(i_shell_link->SetWorkingDirectory(working_dir))) {
+    i_persist_file->Release();
+    i_shell_link->Release();
+    return false;
+  }
+
+  if (arguments && FAILED(i_shell_link->SetArguments(arguments))) {
+    i_persist_file->Release();
+    i_shell_link->Release();
+    return false;
+  }
+
+  if (description && FAILED(i_shell_link->SetDescription(description))) {
+    i_persist_file->Release();
+    i_shell_link->Release();
+    return false;
+  }
+
+  if (icon && FAILED(i_shell_link->SetIconLocation(icon, icon_index))) {
+    i_persist_file->Release();
+    i_shell_link->Release();
+    return false;
+  }
+
+  result = i_persist_file->Save(destination, TRUE);
+  i_persist_file->Release();
+  i_shell_link->Release();
+  return SUCCEEDED(result);
+}
+
+
+bool UpdateShortcutLink(const wchar_t *source, const wchar_t *destination,
+                        const wchar_t *working_dir, const wchar_t *arguments,
+                        const wchar_t *description, const wchar_t *icon,
+                        int icon_index) {
+  // Get pointer to the IPersistFile interface and load existing link
+  IShellLink *i_shell_link = NULL;
+  if (FAILED(CoCreateInstance(CLSID_ShellLink, NULL,
+                              CLSCTX_INPROC_SERVER, IID_IShellLink,
+                              reinterpret_cast<LPVOID*>(&i_shell_link))))
+    return false;
+
+  IPersistFile *i_persist_file = NULL;
+  if (FAILED(i_shell_link->QueryInterface(
+      IID_IPersistFile, reinterpret_cast<LPVOID*>(&i_persist_file)))) {
+    i_shell_link->Release();
+    return false;
+  }
+
+  if (FAILED(i_persist_file->Load(destination, 0))) {
+    i_persist_file->Release();
+    i_shell_link->Release();
+    return false;
+  }
+
+  if (source && FAILED(i_shell_link->SetPath(source))) {
+    i_persist_file->Release();
+    i_shell_link->Release();
+    return false;
+  }
+
+  if (working_dir && FAILED(i_shell_link->SetWorkingDirectory(working_dir))) {
+    i_persist_file->Release();
+    i_shell_link->Release();
+    return false;
+  }
+
+  if (arguments && FAILED(i_shell_link->SetArguments(arguments))) {
+    i_persist_file->Release();
+    i_shell_link->Release();
+    return false;
+  }
+
+  if (description && FAILED(i_shell_link->SetDescription(description))) {
+    i_persist_file->Release();
+    i_shell_link->Release();
+    return false;
+  }
+
+  if (icon && FAILED(i_shell_link->SetIconLocation(icon, icon_index))) {
+    i_persist_file->Release();
+    i_shell_link->Release();
+    return false;
+  }
+
+  HRESULT result = i_persist_file->Save(destination, TRUE);
+  i_persist_file->Release();
+  i_shell_link->Release();
+  return SUCCEEDED(result);
+}
+
+bool GetTempDir(std::wstring* path) {
+  wchar_t temp_path[MAX_PATH + 1];
+  DWORD path_len = ::GetTempPath(MAX_PATH, temp_path);
+  if (path_len >= MAX_PATH || path_len <= 0)
+    return false;
+  path->assign(temp_path);
+  TrimTrailingSeparator(path);
+  return true;
+}
+
+bool CreateTemporaryFileName(std::wstring* temp_file) {
+  wchar_t temp_name[MAX_PATH + 1];
+  std::wstring temp_path;
+
+  if (!GetTempDir(&temp_path))
+    return false;
+
+  if (!GetTempFileName(temp_path.c_str(), L"", 0, temp_name))
+    return false;  // fail!
+
+  DWORD path_len = GetLongPathName(temp_name, temp_name, MAX_PATH);
+  if (path_len > MAX_PATH + 1 || path_len == 0)
+    return false;  // fail!
+
+  temp_file->assign(temp_name, path_len);
+  return true;
+}
+
+bool CreateNewTempDirectory(const std::wstring& prefix,
+                            std::wstring* new_temp_path) {
+  std::wstring system_temp_dir;
+  if (!GetTempDir(&system_temp_dir))
+    return false;
+
+  std::wstring path_to_create;
+  srand(static_cast<uint32>(time(NULL)));
+
+  int count = 0;
+  while (count < 50) {
+    // Try create a new temporary directory with random generated name. If
+    // the one exists, keep trying another path name until we reach some limit.
+    path_to_create.assign(system_temp_dir);
+    std::wstring new_dir_name;
+    new_dir_name.assign(prefix);
+    new_dir_name.append(IntToWString(rand() % kint16max));
+    file_util::AppendToPath(&path_to_create, new_dir_name);
+
+    if (::CreateDirectory(path_to_create.c_str(), NULL))
+      break;
+    count++;
+  }
+
+  if (count == 50) {
+    return false;
+  }
+
+  new_temp_path->assign(path_to_create);
+  return true;
+}
+
+bool CreateDirectory(const std::wstring& full_path) {
+  int err = SHCreateDirectoryEx(NULL, full_path.c_str(), NULL);
+  return err == ERROR_SUCCESS;
+}
+
+bool GetFileSize(const std::wstring& file_path, int64* file_size) {
+  ScopedHandle file_handle(
+      CreateFile(file_path.c_str(), GENERIC_READ,
+                 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+                 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
+
+  LARGE_INTEGER win32_file_size = {0};
+  if (!GetFileSizeEx(file_handle, &win32_file_size)) {
+    return false;
+  }
+
+  *file_size = win32_file_size.QuadPart;
+  return true;
+}
+
+int ReadFile(const std::wstring& filename, char* data, int size) {
+  ScopedHandle file(CreateFile(filename.c_str(),
+                               GENERIC_READ,
+                               FILE_SHARE_READ | FILE_SHARE_WRITE,
+                               NULL,
+                               OPEN_EXISTING,
+                               FILE_FLAG_SEQUENTIAL_SCAN,
+                               NULL));
+  if (file == INVALID_HANDLE_VALUE)
+    return -1;
+
+  int ret_value;
+  DWORD read;
+  if (::ReadFile(file, data, size, &read, NULL) && read == size) {
+    ret_value = static_cast<int>(read);
+  } else {
+    ret_value = -1;
+  }
+
+  return ret_value;
+}
+
+int WriteFile(const std::wstring& filename, const char* data, int size) {
+  ScopedHandle file(CreateFile(filename.c_str(),
+                               GENERIC_WRITE,
+                               0,
+                               NULL,
+                               CREATE_ALWAYS,
+                               0,
+                               NULL));
+  if (file == INVALID_HANDLE_VALUE)
+    return -1;
+
+  int ret_value;
+  DWORD written;
+  if (::WriteFile(file, data, size, &written, NULL)  && written == size) {
+    ret_value = static_cast<int>(written);
+  } else {
+    ret_value = -1;
+  }
+
+  return ret_value;
+}
+
+FileEnumerator::FileEnumerator(const std::wstring& root_path,
+                               bool recursive,
+                               FileEnumerator::FILE_TYPE file_type)
+    : recursive_(recursive),
+      file_type_(file_type),
+      is_in_find_op_(false),
+      find_handle_(INVALID_HANDLE_VALUE) {
+  pending_paths_.push(root_path);
+}
+
+FileEnumerator::FileEnumerator(const std::wstring& root_path,
+                               bool recursive,
+                               FileEnumerator::FILE_TYPE file_type,
+                               const std::wstring& pattern)
+    : recursive_(recursive),
+      file_type_(file_type),
+      is_in_find_op_(false),
+      pattern_(pattern),
+      find_handle_(INVALID_HANDLE_VALUE) {
+  pending_paths_.push(root_path);
+}
+
+FileEnumerator::~FileEnumerator() {
+  if (find_handle_ != INVALID_HANDLE_VALUE)
+    FindClose(find_handle_);
+}
+
+std::wstring FileEnumerator::Next() {
+  if (!is_in_find_op_) {
+    if (pending_paths_.empty())
+      return std::wstring();
+
+    // The last find FindFirstFile operation is done, prepare a new one.
+    // root_path_ must have the trailing directory character.
+    root_path_ = pending_paths_.top();
+    file_util::AppendToPath(&root_path_, std::wstring());
+    pending_paths_.pop();
+
+    // Start a new find operation.
+    std::wstring src(root_path_);
+
+    if (pattern_.empty())
+      file_util::AppendToPath(&src, L"*");  // No pattern = match everything.
+    else
+      file_util::AppendToPath(&src, pattern_);
+
+    find_handle_ = FindFirstFile(src.c_str(), &find_data_);
+    is_in_find_op_ = true;
+
+  } else {
+    // Search for the next file/directory.
+    if (!FindNextFile(find_handle_, &find_data_)) {
+      FindClose(find_handle_);
+      find_handle_ = INVALID_HANDLE_VALUE;
+    }
+  }
+
+  if (INVALID_HANDLE_VALUE == find_handle_) {
+    is_in_find_op_ = false;
+
+    // This is reached when we have finished a directory and are advancing to
+    // the next one in the queue. We applied the pattern (if any) to the files
+    // in the root search directory, but for those directories which were
+    // matched, we want to enumerate all files inside them. This will happen
+    // when the handle is empty.
+    pattern_.clear();
+
+    return Next();
+  }
+
+  std::wstring cur_file(find_data_.cFileName);
+  // Skip over . and ..
+  if (L"." == cur_file || L".." == cur_file)
+    return Next();
+
+  // Construct the absolute filename.
+  cur_file.insert(0, root_path_);
+
+  if (find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+    if (recursive_) {
+      // If |cur_file| is a directory, and we are doing recursive searching, add
+      // it to pending_paths_ so we scan it after we finish scanning this
+      // directory.
+      pending_paths_.push(cur_file);
+      return (file_type_ & FileEnumerator::DIRECTORIES) ? cur_file : Next();
+    }
+
+    if ((file_type_ & FileEnumerator::DIRECTORIES) == 0)
+      return Next();
+  }
+  return (file_type_ & FileEnumerator::FILES) ? cur_file : Next();
+}
+
+bool RenameFileAndResetSecurityDescriptor(
+    const std::wstring& source_file_path,
+    const std::wstring& target_file_path) {
+  // The MoveFile API does not reset the security descriptor on the target
+  // file. To ensure that the target file gets the correct security descriptor
+  // we create the target file initially in the target path, read its security
+  // descriptor and stamp this descriptor on the target file after the MoveFile
+  // API completes.
+  ScopedHandle temp_file_handle_for_security_desc(
+      CreateFileW(target_file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
+                  FILE_SHARE_READ, NULL, OPEN_ALWAYS,
+                  FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE,
+                  NULL));
+  if (!temp_file_handle_for_security_desc.IsValid())
+    return false;
+
+  // Check how much we should allocate for the security descriptor.
+  unsigned long security_descriptor_size_in_bytes = 0;
+  GetFileSecurity(target_file_path.c_str(), DACL_SECURITY_INFORMATION, NULL, 0,
+                  &security_descriptor_size_in_bytes);
+  if (ERROR_INSUFFICIENT_BUFFER != GetLastError() ||
+      security_descriptor_size_in_bytes == 0)
+    return false;
+
+  scoped_array<char> security_descriptor(
+      new char[security_descriptor_size_in_bytes]);
+
+  if (!GetFileSecurity(target_file_path.c_str(), DACL_SECURITY_INFORMATION,
+                       security_descriptor.get(),
+                       security_descriptor_size_in_bytes,
+                       &security_descriptor_size_in_bytes)) {
+    return false;
+  }
+
+  temp_file_handle_for_security_desc.Set(INVALID_HANDLE_VALUE);
+
+  if (!MoveFileEx(source_file_path.c_str(), target_file_path.c_str(),
+                  MOVEFILE_COPY_ALLOWED)) {
+    return false;
+  }
+
+  return !!SetFileSecurity(target_file_path.c_str(),
+                           DACL_SECURITY_INFORMATION,
+                           security_descriptor.get());
+}
+
+}  // namespace
diff --git a/base/file_util.h b/base/file_util.h
new file mode 100644
index 0000000..9783e93
--- /dev/null
+++ b/base/file_util.h
@@ -0,0 +1,302 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file contains utility functions for dealing with the local
+// filesystem.
+
+#ifndef BASE_FILE_UTIL_H__
+#define BASE_FILE_UTIL_H__
+
+#include <windows.h>
+#include <stack>
+#include <string>
+
+#include "base/basictypes.h"
+
+namespace file_util {
+
+//-----------------------------------------------------------------------------
+// Constants
+
+extern const wchar_t kPathSeparator;
+
+
+//-----------------------------------------------------------------------------
+// Functions that operate purely on a path string w/o touching the filesystem:
+
+// Returns true if the given path ends with a path separator character.
+bool EndsWithSeparator(std::wstring* path);
+
+// Modifies a string by trimming all trailing separators from the end.
+void TrimTrailingSeparator(std::wstring* dir);
+
+// Strips the topmost directory from the end of 'dir'.  Assumes 'dir' does not
+// refer to a file.
+// If 'dir' is a root directory, return without change.
+void UpOneDirectory(std::wstring* dir);
+
+// Strips the topmost directory from the end of 'dir'.  Assumes 'dir' does not
+// refer to a file.
+// If 'dir' is a root directory, the result becomes empty string.
+void UpOneDirectoryOrEmpty(std::wstring* dir);
+
+// Strips the filename component from the end of 'path'.
+void TrimFilename(std::wstring* path);
+
+// Returns the filename portion of 'path', without any leading \'s or /'s.
+std::wstring GetFilenameFromPath(const std::wstring& path);
+
+// Returns "jpg" for path "C:\pics\jojo.jpg", or an empty string if
+// the file has no extension.
+std::wstring GetFileExtensionFromPath(const std::wstring& path);
+
+// Returns the directory component of a path, without the trailing
+// path separator, or an empty string on error. The function does not
+// check for the existence of the path, so if it is passed a directory
+// without the trailing \, it will interpret the last component of the
+// path as a file and chomp it. This does not support relative paths.
+// Examples:
+// path == "C:\pics\jojo.jpg",     returns "C:\pics"
+// path == "C:\Windows\system32\", returns "C:\Windows\system32"
+// path == "C:\Windows\system32",  returns "C:\Windows"
+std::wstring GetDirectoryFromPath(const std::wstring& path);
+
+// Appends new_ending to path, adding a separator between the two if necessary.
+void AppendToPath(std::wstring* path, const std::wstring& new_ending);
+
+// Inserts |suffix| after the file name portion of |path| but before the
+// extension.
+// Examples:
+// path == "C:\pics\jojo.jpg" suffix == " (1)", returns "C:\pics\jojo (1).jpg"
+// path == "jojo.jpg"         suffix == " (1)", returns "jojo (1).jpg"
+// path == "C:\pics\jojo"     suffix == " (1)", returns "C:\pics\jojo (1)"
+// path == "C:\pics.old\jojo" suffix == " (1)", returns "C:\pics.old\jojo (1)"
+void InsertBeforeExtension(std::wstring* path, const std::wstring& suffix);
+
+// Replaces characters in 'file_name' that are illegal for file names with
+// 'replace_char'. 'file_name' must not be a full or relative path, but just the
+// file name component. Any leading or trailing whitespace in 'file_name' is
+// removed.
+// Example:
+//   file_name == "bad:file*name?.txt", changed to: "bad-file-name-.txt" when
+//   'replace_char' is '-'.
+void ReplaceIllegalCharacters(std::wstring* file_name, int replace_char);
+
+// Replaces the extension of |file_name| with |extension|.  If |file_name|
+// does not have an extension, them |extension| is added.  If |extention| is
+// empty, then the extension is removed from |file_name|.
+void ReplaceExtension(std::wstring* file_name, const std::wstring& extension);
+
+//-----------------------------------------------------------------------------
+// Functions that involve filesystem access or modification:
+
+// Returns the number of files matching the current path that were
+// created on or after the given FILETIME.  Doesn't count ".." or ".".
+// Filetime is UTC filetime, not LocalFiletime.
+int CountFilesCreatedAfter(const std::wstring& path,
+                           const FILETIME& file_time);
+
+// Deletes the given path, whether it's a file or a directory.
+// If it's a directory, it's perfectly happy to delete all of the
+// directory's contents.  Passing true to recursive deletes
+// subdirectories and their contents as well.
+// Returns true if successful, false otherwise.
+//
+// WARNING: USING THIS WITH recursive==true IS EQUIVALENT
+//          TO "rm -rf", SO USE WITH CAUTION.
+bool Delete(const std::wstring& path, bool recursive);
+
+// Moves the given path, whether it's a file or a directory.
+// Returns true if successful, false otherwise.
+bool Move(const std::wstring& from_path, const std::wstring& to_path);
+
+// Copies a single file. Use CopyDirectory to copy directories.
+bool CopyFile(const std::wstring& from_path, const std::wstring& to_path);
+
+// Copies the given path, and optionally all subdirectories and their contents
+// as well.
+// If there are files existing under to_path, always overwrite.
+// Returns true if successful, false otherwise.
+// Dont't use wildcards on the names, it may stop working without notice.
+//
+// If you only need to copy a file use CopyFile, it's faster.
+bool CopyDirectory(const std::wstring& from_path, const std::wstring& to_path,
+                   bool recursive);
+
+// Returns true if the given path exists on the local filesystem,
+// false otherwise.
+bool PathExists(const std::wstring& path);
+
+// Returns true if the given path is writable by the user, false otherwise.
+bool PathIsWritable(const std::wstring& path);
+
+// Gets the creation time of the given file (expressed in the local timezone),
+// and returns it via the creation_time parameter.  Returns true if successful,
+// false otherwise.
+bool GetFileCreationLocalTime(const std::wstring& filename,
+                              LPSYSTEMTIME creation_time);
+
+// Same as above, but takes a previously-opened file handle instead of a name.
+bool GetFileCreationLocalTimeFromHandle(HANDLE file_handle,
+                                        LPSYSTEMTIME creation_time);
+
+// Returns true if the contents of the two files given are equal, false
+// otherwise.  If either file can't be read, returns false.
+bool ContentsEqual(const std::wstring& filename1,
+                   const std::wstring& filename2);
+
+// Read the file at |path| into |contents|, returning true on success.
+// Useful for unit tests.
+bool ReadFileToString(const std::wstring& path, std::string* contents);
+
+// Resolve Windows shortcut (.LNK file)
+// Argument path specifies a valid LNK file. On success, return true and put
+// the URL into path. If path is a invalid .LNK file, return false.
+bool ResolveShortcut(std::wstring* path);
+
+// Create a Windows shortcut (.LNK file)
+// This method creates a shortcut link using the information given. Ensure
+// you have initialized COM before calling into this function. 'source'
+// and 'destination' parameters are required, everything else can be NULL.
+// 'source' is the existing file, 'destination' is the new link file to be
+// created; for best resoults pass the filename with the .lnk extension.
+// The 'icon' can specify a dll or exe in which case the icon index is the
+// resource id.
+// Note that if the shortcut exists it will overwrite it.
+bool CreateShortcutLink(const wchar_t *source, const wchar_t *destination,
+                        const wchar_t *working_dir, const wchar_t *arguments,
+                        const wchar_t *description, const wchar_t *icon,
+                        int icon_index);
+
+// Update a Windows shortcut (.LNK file). This method assumes the shortcut
+// link already exists (otherwise false is returned). Ensure you have
+// initialized COM before calling into this function. Only 'destination'
+// parameter is required, everything else can be NULL (but if everthing else
+// is NULL no changes are made to the shortcut). 'destination' is the link
+// file to be updated. For best results pass the filename with the .lnk
+// extension.
+bool UpdateShortcutLink(const wchar_t *source, const wchar_t *destination,
+                        const wchar_t *working_dir, const wchar_t *arguments,
+                        const wchar_t *description, const wchar_t *icon,
+                        int icon_index);
+
+// Get the temporary directory provided by the system.
+bool GetTempDir(std::wstring* path);
+
+// Creates a temporary file name, but does it not create the file. It accesses
+// the disk to do this, however. The full path is placed in 'temp_file', and the
+// function returns true if was successful in creating the file name.
+bool CreateTemporaryFileName(std::wstring* temp_file);
+
+// Create a new directory under TempPath. If prefix is provided, the new
+// directory name is in the format of prefixyyyy.
+// If success, return true and output the full path of the directory created.
+bool CreateNewTempDirectory(const std::wstring& prefix,
+                            std::wstring* new_temp_path);
+
+// Creates a directory, as well as creating any parent directories, if they
+// don't exist. Returns 'true' on successful creation.
+bool CreateDirectory(const std::wstring& full_path);
+
+// Returns the file size. Returns true on success.
+bool GetFileSize(const std::wstring& file_path, int64* file_size);
+
+// Reads the given number of bytes from the file into the buffer.  Returns
+// the number of read bytes, or -1 on error.
+int ReadFile(const std::wstring& filename, char* data, int size);
+
+// Writes the given buffer into the file, overwriting any data that was
+// previously there.  Returns the number of bytes written, or -1 on error.
+int WriteFile(const std::wstring& filename, const char* data, int size);
+
+// A class for enumerating the files in a provided path. The order of the
+// results is not guaranteed.
+//
+// DO NOT USE FROM THE MAIN THREAD of your application unless it is a test
+// program where latency does not matter. This class is blocking.
+class FileEnumerator {
+ public:
+  enum FILE_TYPE {
+    FILES                 = 0x1,
+    DIRECTORIES           = 0x2,
+    FILES_AND_DIRECTORIES = 0x3
+  };
+
+  // |root_path| is the starting directory to search for. It may or may not end
+  // in a slash.
+  //
+  // If |recursive| is true, this will enumerate all matches in any
+  // subdirectories matched as well. It does a breadth-first search, so all
+  // files in one directory will be returned before any files in a
+  // subdirectory.
+  //
+  // The last parameter is an optional pattern for which files to match. This
+  // works like a Windows file pattern. For example, "*.txt" or "Foo???.doc".
+  // If unspecified, this will match all files.
+  FileEnumerator(const std::wstring& root_path,
+                 bool recursive,
+                 FileEnumerator::FILE_TYPE file_type);
+  FileEnumerator(const std::wstring& root_path,
+                 bool recursive,
+                 FileEnumerator::FILE_TYPE file_type,
+                 const std::wstring& pattern);
+  ~FileEnumerator();
+
+  // Returns an empty string if there are no more results.
+  std::wstring Next();
+
+ private:
+  std::wstring root_path_;
+  bool recursive_;
+  FILE_TYPE file_type_;
+  std::wstring pattern_;  // Empty when we want to find everything.
+
+  // Set to true when there is a find operation open. This way, we can lazily
+  // start the operations when the caller calls Next().
+  bool is_in_find_op_;
+
+  // A stack that keeps track of which subdirectories we still need to
+  // enumerate in the breadth-first search.
+  std::stack<std::wstring> pending_paths_;
+
+  WIN32_FIND_DATA find_data_;
+  HANDLE find_handle_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(FileEnumerator);
+};
+
+// Renames a file using the MoveFileEx API and ensures that the target file gets
+// the correct security descriptor in the new path.
+bool RenameFileAndResetSecurityDescriptor(
+    const std::wstring& source_file_path,
+    const std::wstring& target_file_path);
+
+}  // namespace file_util
+
+#endif  // BASE_FILE_UTIL_H__
diff --git a/base/file_util_unittest.cc b/base/file_util_unittest.cc
new file mode 100644
index 0000000..f82a894
--- /dev/null
+++ b/base/file_util_unittest.cc
@@ -0,0 +1,777 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+#include <set>
+#include <shellapi.h>
+#include <shlobj.h>
+
+#include <fstream>
+#include <iostream>
+
+#include "base/base_paths.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/string_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class FileUtilTest : public testing::Test {
+ protected:
+  virtual void SetUp() {
+    // Name a subdirectory of the temp directory.
+    ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &test_dir_));
+    file_util::AppendToPath(&test_dir_, L"FileUtilTest");
+
+    // Create a fresh, empty copy of this directory.
+    file_util::Delete(test_dir_, true);
+    CreateDirectory(test_dir_.c_str(), NULL);
+  }
+  virtual void TearDown() {
+    // Clean up test directory
+    ASSERT_TRUE(file_util::Delete(test_dir_, false));
+    ASSERT_FALSE(file_util::PathExists(test_dir_));
+  }
+
+  // the path to temporary directory used to contain the test operations
+  std::wstring test_dir_;
+};
+
+// Collects all the results from the given file enumerator, and provides an
+// interface to query whether a given file is present.
+class FindResultCollector {
+ public:
+  FindResultCollector(file_util::FileEnumerator& enumerator) {
+    std::wstring cur_file;
+    while (!(cur_file = enumerator.Next()).empty()) {
+      // The file should not be returned twice.
+      EXPECT_TRUE(files_.end() == files_.find(cur_file))
+          << "Same file returned twice";
+
+      // Save for later.
+      files_.insert(cur_file);
+    }
+  }
+
+  // Returns true if the enumerator found the file.
+  bool HasFile(const std::wstring& file) const {
+    return files_.find(file) != files_.end();
+  }
+
+ private:
+  std::set<std::wstring> files_;
+};
+
+// Simple function to dump some text into a new file.
+void CreateTextFile(const std::wstring& filename,
+                    const std::wstring& contents) {
+  std::ofstream file;
+  file.open(filename.c_str());
+  ASSERT_TRUE(file.is_open());
+  file << contents;
+  file.close();
+}
+
+// Simple function to take out some text from a file.
+std::wstring ReadTextFile(const std::wstring& filename) {
+  WCHAR contents[64];
+  std::wifstream file;
+  file.open(filename.c_str());
+  EXPECT_TRUE(file.is_open());
+  file.getline(contents, 64);
+  file.close();
+  return std::wstring(contents);
+}
+
+uint64 FileTimeAsUint64(const FILETIME& ft) {
+  ULARGE_INTEGER u;
+  u.LowPart = ft.dwLowDateTime;
+  u.HighPart = ft.dwHighDateTime;
+  return u.QuadPart;
+}
+
+const struct append_case {
+  const wchar_t* path;
+  const wchar_t* ending;
+  const wchar_t* result;
+} append_cases[] = {
+  {L"c:\\colon\\backslash", L"path", L"c:\\colon\\backslash\\path"},
+  {L"c:\\colon\\backslash\\", L"path", L"c:\\colon\\backslash\\path"},
+  {L"c:\\colon\\backslash\\\\", L"path", L"c:\\colon\\backslash\\\\path"},
+  {L"c:\\colon\\backslash\\", L"", L"c:\\colon\\backslash\\"},
+  {L"c:\\colon\\backslash", L"", L"c:\\colon\\backslash\\"},
+  {L"", L"path", L"\\path"},
+  {L"", L"", L"\\"},
+};
+
+}  // namespace
+
+TEST_F(FileUtilTest, AppendToPath) {
+  for (int i = 0; i < arraysize(append_cases); ++i) {
+    const append_case& value = append_cases[i];
+    std::wstring result = value.path;
+    file_util::AppendToPath(&result, value.ending);
+    EXPECT_EQ(value.result, result);
+  }
+
+#ifdef NDEBUG
+  file_util::AppendToPath(NULL, L"path");  // asserts in debug mode
+#endif
+}
+
+static const struct InsertBeforeExtensionCase {
+  std::wstring path;
+  std::wstring suffix;
+  std::wstring result;
+} kInsertBeforeExtension[] = {
+  {L"", L"", L""},
+  {L"", L"txt", L"txt"},
+  {L".", L"txt", L"txt."},
+  {L".", L"", L"."},
+  {L"foo.dll", L"txt", L"footxt.dll"},
+  {L"foo.dll", L".txt", L"foo.txt.dll"},
+  {L"foo", L"txt", L"footxt"},
+  {L"foo", L".txt", L"foo.txt"},
+  {L"foo.baz.dll", L"txt", L"foo.baztxt.dll"},
+  {L"foo.baz.dll", L".txt", L"foo.baz.txt.dll"},
+  {L"foo.dll", L"", L"foo.dll"},
+  {L"foo.dll", L".", L"foo..dll"},
+  {L"foo", L"", L"foo"},
+  {L"foo", L".", L"foo."},
+  {L"foo.baz.dll", L"", L"foo.baz.dll"},
+  {L"foo.baz.dll", L".", L"foo.baz..dll"},
+  {L"\\", L"", L"\\"},
+  {L"\\", L"txt", L"\\txt"},
+  {L"\\.", L"txt", L"\\txt."},
+  {L"\\.", L"", L"\\."},
+  {L"C:\\bar\\foo.dll", L"txt", L"C:\\bar\\footxt.dll"},
+  {L"C:\\bar.baz\\foodll", L"txt", L"C:\\bar.baz\\foodlltxt"},
+  {L"C:\\bar.baz\\foo.dll", L"txt", L"C:\\bar.baz\\footxt.dll"},
+  {L"C:\\bar.baz\\foo.dll.exe", L"txt", L"C:\\bar.baz\\foo.dlltxt.exe"},
+  {L"C:\\bar.baz\\foo", L"", L"C:\\bar.baz\\foo"},
+  {L"C:\\bar.baz\\foo.exe", L"", L"C:\\bar.baz\\foo.exe"},
+  {L"C:\\bar.baz\\foo.dll.exe", L"", L"C:\\bar.baz\\foo.dll.exe"},
+  {L"C:\\bar\\baz\\foo.exe", L" (1)", L"C:\\bar\\baz\\foo (1).exe"},
+};
+
+TEST_F(FileUtilTest, InsertBeforeExtensionTest) {
+  for (int i = 0; i < arraysize(kInsertBeforeExtension); ++i) {
+    std::wstring path(kInsertBeforeExtension[i].path);
+    file_util::InsertBeforeExtension(&path, kInsertBeforeExtension[i].suffix);
+    EXPECT_EQ(path, kInsertBeforeExtension[i].result);
+  }
+}
+
+static const struct filename_case {
+  const wchar_t* path;
+  const wchar_t* filename;
+} filename_cases[] = {
+  {L"c:\\colon\\backslash", L"backslash"},
+  {L"c:\\colon\\backslash\\", L""},
+  {L"\\\\filename.exe", L"filename.exe"},
+  {L"filename.exe", L"filename.exe"},
+  {L"", L""},
+  {L"\\\\\\", L""},
+  {L"c:/colon/backslash", L"backslash"},
+  {L"c:/colon/backslash/", L""},
+  {L"//////", L""},
+  {L"///filename.exe", L"filename.exe"},
+};
+
+TEST_F(FileUtilTest, GetFilenameFromPath) {
+  for (int i = 0; i < arraysize(filename_cases); ++i) {
+    const filename_case& value = filename_cases[i];
+    std::wstring result = file_util::GetFilenameFromPath(value.path);
+    EXPECT_EQ(value.filename, result);
+  }
+}
+
+// Test finding the file type from a path name
+static const struct extension_case {
+  const wchar_t* path;
+  const wchar_t* extension;
+} extension_cases[] = {
+  {L"C:\\colon\\backslash\\filename.extension", L"extension"},
+  {L"C:\\colon\\backslash\\filename.", L""},
+  {L"C:\\colon\\backslash\\filename", L""},
+  {L"C:\\colon\\backslash\\", L""},
+  {L"C:\\colon\\backslash.\\", L""},
+  {L"C:\\colon\\backslash\filename.extension.extension2", L"extension2"},
+};
+
+TEST_F(FileUtilTest, GetFileExtensionFromPath) {
+  for (int i = 0; i < arraysize(extension_cases); ++i) {
+    const extension_case& ext = extension_cases[i];
+    const std::wstring fext = file_util::GetFileExtensionFromPath(ext.path);
+    EXPECT_EQ(ext.extension, fext);
+  }
+}
+
+// Test finding the directory component of a path
+static const struct dir_case {
+  const wchar_t* full_path;
+  const wchar_t* directory;
+} dir_cases[] = {
+  {L"C:\\WINDOWS\\system32\\gdi32.dll", L"C:\\WINDOWS\\system32"},
+  {L"C:\\WINDOWS\\system32\\not_exist_thx_1138", L"C:\\WINDOWS\\system32"},
+  {L"C:\\WINDOWS\\system32\\", L"C:\\WINDOWS\\system32"},
+  {L"C:\\WINDOWS\\system32\\\\", L"C:\\WINDOWS\\system32"},
+  {L"C:\\WINDOWS\\system32", L"C:\\WINDOWS"},
+  {L"C:\\WINDOWS\\system32.\\", L"C:\\WINDOWS\\system32."},
+  {L"C:\\", L"C:"},
+};
+
+TEST_F(FileUtilTest, GetDirectoryFromPath) {
+  for (int i = 0; i < arraysize(dir_cases); ++i) {
+    const dir_case& dir = dir_cases[i];
+    const std::wstring parent =
+        file_util::GetDirectoryFromPath(dir.full_path);
+    EXPECT_EQ(dir.directory, parent);
+  }
+}
+
+TEST_F(FileUtilTest, CountFilesCreatedAfter) {
+  // Create old file (that we don't want to count)
+  std::wstring old_file_name = test_dir_;
+  file_util::AppendToPath(&old_file_name, L"Old File.txt");
+  CreateTextFile(old_file_name, L"Just call me Mr. Creakybits");
+
+  // Age to perfection
+  Sleep(100);
+
+  // Establish our cutoff time
+  FILETIME test_start_time;
+  GetSystemTimeAsFileTime(&test_start_time);
+  EXPECT_EQ(0, file_util::CountFilesCreatedAfter(test_dir_, test_start_time));
+
+  // Create a new file (that we do want to count)
+  std::wstring new_file_name = test_dir_;
+  file_util::AppendToPath(&new_file_name, L"New File.txt");
+  CreateTextFile(new_file_name, L"Waaaaaaaaaaaaaah.");
+
+  // We should see only the new file.
+  EXPECT_EQ(1, file_util::CountFilesCreatedAfter(test_dir_, test_start_time));
+
+  // Delete new file, we should see no files after cutoff now
+  EXPECT_TRUE(file_util::Delete(new_file_name, false));
+  EXPECT_EQ(0, file_util::CountFilesCreatedAfter(test_dir_, test_start_time));
+}
+
+// Tests that the Delete function works as expected, especially
+// the recursion flag.  Also coincidentally tests PathExists.
+TEST_F(FileUtilTest, Delete) {
+  // Create a file
+  std::wstring file_name = test_dir_;
+  file_util::AppendToPath(&file_name, L"Test File.txt");
+  CreateTextFile(file_name, L"I'm cannon fodder.");
+
+  ASSERT_TRUE(file_util::PathExists(file_name));
+
+  std::wstring subdir_path = test_dir_;
+  file_util::AppendToPath(&subdir_path, L"Subdirectory");
+  CreateDirectory(subdir_path.c_str(), NULL);
+
+  ASSERT_TRUE(file_util::PathExists(subdir_path));
+
+  std::wstring directory_contents = test_dir_;
+  file_util::AppendToPath(&directory_contents, L"*");
+
+  // Delete non-recursively and check that only the file is deleted
+  ASSERT_TRUE(file_util::Delete(directory_contents, false));
+  ASSERT_FALSE(file_util::PathExists(file_name));
+  ASSERT_TRUE(file_util::PathExists(subdir_path));
+
+  // Delete recursively and make sure all contents are deleted
+  ASSERT_TRUE(file_util::Delete(directory_contents, true));
+  ASSERT_FALSE(file_util::PathExists(file_name));
+  ASSERT_FALSE(file_util::PathExists(subdir_path));
+}
+
+TEST_F(FileUtilTest, Move) {
+  // Create a directory
+  std::wstring dir_name_from(test_dir_);
+  file_util::AppendToPath(&dir_name_from, L"Move_From_Subdir");
+  CreateDirectory(dir_name_from.c_str(), NULL);
+  ASSERT_TRUE(file_util::PathExists(dir_name_from));
+
+  // Create a file under the directory
+  std::wstring file_name_from(dir_name_from);
+  file_util::AppendToPath(&file_name_from, L"Move_Test_File.txt");
+  CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+  ASSERT_TRUE(file_util::PathExists(file_name_from));
+
+  // Move the directory
+  std::wstring dir_name_to(test_dir_);
+  file_util::AppendToPath(&dir_name_to, L"Move_To_Subdir");
+  std::wstring file_name_to(dir_name_to);
+  file_util::AppendToPath(&file_name_to, L"Move_Test_File.txt");
+
+  ASSERT_FALSE(file_util::PathExists(dir_name_to));
+
+  EXPECT_TRUE(file_util::Move(dir_name_from, dir_name_to));
+
+  // Check everything has been moved.
+  EXPECT_FALSE(file_util::PathExists(dir_name_from));
+  EXPECT_FALSE(file_util::PathExists(file_name_from));
+  EXPECT_TRUE(file_util::PathExists(dir_name_to));
+  EXPECT_TRUE(file_util::PathExists(file_name_to));
+}
+
+TEST_F(FileUtilTest, CopyDirectoryRecursively) {
+  // Create a directory.
+  std::wstring dir_name_from(test_dir_);
+  file_util::AppendToPath(&dir_name_from, L"Copy_From_Subdir");
+  CreateDirectory(dir_name_from.c_str(), NULL);
+  ASSERT_TRUE(file_util::PathExists(dir_name_from));
+
+  // Create a file under the directory.
+  std::wstring file_name_from(dir_name_from);
+  file_util::AppendToPath(&file_name_from, L"Copy_Test_File.txt");
+  CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+  ASSERT_TRUE(file_util::PathExists(file_name_from));
+
+  // Create a subdirectory.
+  std::wstring subdir_name_from(dir_name_from);
+  file_util::AppendToPath(&subdir_name_from, L"Subdir");
+  CreateDirectory(subdir_name_from.c_str(), NULL);
+  ASSERT_TRUE(file_util::PathExists(subdir_name_from));
+
+  // Create a file under the subdirectory.
+  std::wstring file_name2_from(subdir_name_from);
+  file_util::AppendToPath(&file_name2_from, L"Copy_Test_File.txt");
+  CreateTextFile(file_name2_from, L"Gooooooooooooooooooooogle");
+  ASSERT_TRUE(file_util::PathExists(file_name2_from));
+
+  // Copy the directory recursively.
+  std::wstring dir_name_to(test_dir_);
+  file_util::AppendToPath(&dir_name_to, L"Copy_To_Subdir");
+  std::wstring file_name_to(dir_name_to);
+  file_util::AppendToPath(&file_name_to, L"Copy_Test_File.txt");
+  std::wstring subdir_name_to(dir_name_to);
+  file_util::AppendToPath(&subdir_name_to, L"Subdir");
+  std::wstring file_name2_to(subdir_name_to);
+  file_util::AppendToPath(&file_name2_to, L"Copy_Test_File.txt");
+
+  ASSERT_FALSE(file_util::PathExists(dir_name_to));
+
+  EXPECT_TRUE(file_util::CopyDirectory(dir_name_from, dir_name_to, true));
+
+  // Check everything has been copied.
+  EXPECT_TRUE(file_util::PathExists(dir_name_from));
+  EXPECT_TRUE(file_util::PathExists(file_name_from));
+  EXPECT_TRUE(file_util::PathExists(subdir_name_from));
+  EXPECT_TRUE(file_util::PathExists(file_name2_from));
+  EXPECT_TRUE(file_util::PathExists(dir_name_to));
+  EXPECT_TRUE(file_util::PathExists(file_name_to));
+  EXPECT_TRUE(file_util::PathExists(subdir_name_to));
+  EXPECT_TRUE(file_util::PathExists(file_name2_to));
+}
+
+TEST_F(FileUtilTest, CopyDirectory) {
+  // Create a directory.
+  std::wstring dir_name_from(test_dir_);
+  file_util::AppendToPath(&dir_name_from, L"Copy_From_Subdir");
+  CreateDirectory(dir_name_from.c_str(), NULL);
+  ASSERT_TRUE(file_util::PathExists(dir_name_from));
+
+  // Create a file under the directory.
+  std::wstring file_name_from(dir_name_from);
+  file_util::AppendToPath(&file_name_from, L"Copy_Test_File.txt");
+  CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+  ASSERT_TRUE(file_util::PathExists(file_name_from));
+
+  // Create a subdirectory.
+  std::wstring subdir_name_from(dir_name_from);
+  file_util::AppendToPath(&subdir_name_from, L"Subdir");
+  CreateDirectory(subdir_name_from.c_str(), NULL);
+  ASSERT_TRUE(file_util::PathExists(subdir_name_from));
+
+  // Create a file under the subdirectory.
+  std::wstring file_name2_from(subdir_name_from);
+  file_util::AppendToPath(&file_name2_from, L"Copy_Test_File.txt");
+  CreateTextFile(file_name2_from, L"Gooooooooooooooooooooogle");
+  ASSERT_TRUE(file_util::PathExists(file_name2_from));
+
+  // Copy the directory not recursively.
+  std::wstring dir_name_to(test_dir_);
+  file_util::AppendToPath(&dir_name_to, L"Copy_To_Subdir");
+  std::wstring file_name_to(dir_name_to);
+  file_util::AppendToPath(&file_name_to, L"Copy_Test_File.txt");
+  std::wstring subdir_name_to(dir_name_to);
+  file_util::AppendToPath(&subdir_name_to, L"Subdir");
+
+  ASSERT_FALSE(file_util::PathExists(dir_name_to));
+
+  EXPECT_TRUE(file_util::CopyDirectory(dir_name_from, dir_name_to, false));
+
+  // Check everything has been copied.
+  EXPECT_TRUE(file_util::PathExists(dir_name_from));
+  EXPECT_TRUE(file_util::PathExists(file_name_from));
+  EXPECT_TRUE(file_util::PathExists(subdir_name_from));
+  EXPECT_TRUE(file_util::PathExists(file_name2_from));
+  EXPECT_TRUE(file_util::PathExists(dir_name_to));
+  EXPECT_TRUE(file_util::PathExists(file_name_to));
+  EXPECT_FALSE(file_util::PathExists(subdir_name_to));
+}
+
+TEST_F(FileUtilTest, CopyFile) {
+  // Create a directory
+  std::wstring dir_name_from(test_dir_);
+  file_util::AppendToPath(&dir_name_from, L"Copy_From_Subdir");
+  CreateDirectory(dir_name_from.c_str(), NULL);
+  ASSERT_TRUE(file_util::PathExists(dir_name_from));
+
+  // Create a file under the directory
+  std::wstring file_name_from(dir_name_from);
+  file_util::AppendToPath(&file_name_from, L"Copy_Test_File.txt");
+  const std::wstring file_contents(L"Gooooooooooooooooooooogle");
+  CreateTextFile(file_name_from, file_contents);
+  ASSERT_TRUE(file_util::PathExists(file_name_from));
+
+  // Copy the file.
+  std::wstring dest_file(dir_name_from);
+  file_util::AppendToPath(&dest_file, L"DestFile.txt");
+  ASSERT_TRUE(file_util::CopyFile(file_name_from, dest_file));
+
+  // Check everything has been copied.
+  EXPECT_TRUE(file_util::PathExists(file_name_from));
+  EXPECT_TRUE(file_util::PathExists(dest_file));
+  const std::wstring read_contents = ReadTextFile(dest_file);
+  EXPECT_EQ(file_contents, read_contents);
+}
+
+TEST_F(FileUtilTest, GetFileCreationLocalTime) {
+  std::wstring file_name = test_dir_;
+  file_util::AppendToPath(&file_name, L"Test File.txt");
+
+  SYSTEMTIME start_time;
+  GetLocalTime(&start_time);
+  Sleep(100);
+  CreateTextFile(file_name, L"New file!");
+  Sleep(100);
+  SYSTEMTIME end_time;
+  GetLocalTime(&end_time);
+
+  SYSTEMTIME file_creation_time;
+  file_util::GetFileCreationLocalTime(file_name, &file_creation_time);
+
+  FILETIME start_filetime;
+  SystemTimeToFileTime(&start_time, &start_filetime);
+  FILETIME end_filetime;
+  SystemTimeToFileTime(&end_time, &end_filetime);
+  FILETIME file_creation_filetime;
+  SystemTimeToFileTime(&file_creation_time, &file_creation_filetime);
+
+  EXPECT_EQ(-1, CompareFileTime(&start_filetime, &file_creation_filetime)) <<
+    "start time: " << FileTimeAsUint64(start_filetime) << ", " <<
+    "creation time: " << FileTimeAsUint64(file_creation_filetime);
+
+  EXPECT_EQ(-1, CompareFileTime(&file_creation_filetime, &end_filetime)) <<
+    "creation time: " << FileTimeAsUint64(file_creation_filetime) << ", " <<
+    "end time: " << FileTimeAsUint64(end_filetime);
+
+  ASSERT_TRUE(DeleteFile(file_name.c_str()));
+}
+
+typedef testing::Test ReadOnlyFileUtilTest;
+
+TEST(ReadOnlyFileUtilTest, ContentsEqual) {
+  std::wstring data_dir;
+  ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &data_dir));
+  file_util::AppendToPath(&data_dir, L"base");
+  file_util::AppendToPath(&data_dir, L"data");
+  file_util::AppendToPath(&data_dir, L"file_util_unittest");
+  ASSERT_TRUE(file_util::PathExists(data_dir));
+
+  std::wstring original_file = data_dir;
+  file_util::AppendToPath(&original_file, L"original.txt");
+  std::wstring same_file = data_dir;
+  file_util::AppendToPath(&same_file, L"same.txt");
+  std::wstring same_length_file = data_dir;
+  file_util::AppendToPath(&same_length_file, L"same_length.txt");
+  std::wstring different_file = data_dir;
+  file_util::AppendToPath(&different_file, L"different.txt");
+  std::wstring different_first_file = data_dir;
+  file_util::AppendToPath(&different_first_file, L"different_first.txt");
+  std::wstring different_last_file = data_dir;
+  file_util::AppendToPath(&different_last_file, L"different_last.txt");
+  std::wstring empty1_file = data_dir;
+  file_util::AppendToPath(&empty1_file, L"empty1.txt");
+  std::wstring empty2_file = data_dir;
+  file_util::AppendToPath(&empty2_file, L"empty2.txt");
+  std::wstring shortened_file = data_dir;
+  file_util::AppendToPath(&shortened_file, L"shortened.txt");
+  std::wstring binary_file = data_dir;
+  file_util::AppendToPath(&binary_file, L"binary_file.bin");
+  std::wstring binary_file_same = data_dir;
+  file_util::AppendToPath(&binary_file_same, L"binary_file_same.bin");
+  std::wstring binary_file_diff = data_dir;
+  file_util::AppendToPath(&binary_file_diff, L"binary_file_diff.bin");
+
+  EXPECT_TRUE(file_util::ContentsEqual(original_file, original_file));
+  EXPECT_TRUE(file_util::ContentsEqual(original_file, same_file));
+  EXPECT_FALSE(file_util::ContentsEqual(original_file, same_length_file));
+  EXPECT_FALSE(file_util::ContentsEqual(original_file, different_file));
+  EXPECT_FALSE(file_util::ContentsEqual(L"bogusname", L"bogusname"));
+  EXPECT_FALSE(file_util::ContentsEqual(original_file, different_first_file));
+  EXPECT_FALSE(file_util::ContentsEqual(original_file, different_last_file));
+  EXPECT_TRUE(file_util::ContentsEqual(empty1_file, empty2_file));
+  EXPECT_FALSE(file_util::ContentsEqual(original_file, shortened_file));
+  EXPECT_FALSE(file_util::ContentsEqual(shortened_file, original_file));
+  EXPECT_TRUE(file_util::ContentsEqual(binary_file, binary_file_same));
+  EXPECT_FALSE(file_util::ContentsEqual(binary_file, binary_file_diff));
+}
+
+TEST_F(FileUtilTest, ResolveShortcutTest) {
+  std::wstring target_file = test_dir_;
+  file_util::AppendToPath(&target_file, L"Target.txt");
+  CreateTextFile(target_file, L"This is the target.");
+
+  std::wstring link_file = test_dir_;
+  file_util::AppendToPath(&link_file, L"Link.lnk");
+
+  HRESULT result;
+  IShellLink *shell = NULL;
+  IPersistFile *persist = NULL;
+
+  CoInitialize(NULL);
+  // Temporarily create a shortcut for test
+  result = CoCreateInstance(CLSID_ShellLink, NULL,
+                          CLSCTX_INPROC_SERVER, IID_IShellLink,
+                          reinterpret_cast<LPVOID*>(&shell));
+  EXPECT_TRUE(SUCCEEDED(result));
+  result = shell->QueryInterface(IID_IPersistFile,
+                             reinterpret_cast<LPVOID*>(&persist));
+  EXPECT_TRUE(SUCCEEDED(result));
+  result = shell->SetPath(target_file.c_str());
+  EXPECT_TRUE(SUCCEEDED(result));
+  result = shell->SetDescription(L"ResolveShortcutTest");
+  EXPECT_TRUE(SUCCEEDED(result));
+  result = persist->Save(link_file.c_str(), TRUE);
+  EXPECT_TRUE(SUCCEEDED(result));
+  if (persist)
+    persist->Release();
+  if (shell)
+    shell->Release();
+
+  bool is_solved;
+  is_solved = file_util::ResolveShortcut(&link_file);
+  EXPECT_TRUE(is_solved);
+  std::wstring contents;
+  contents = ReadTextFile(link_file);
+  EXPECT_EQ(L"This is the target.", contents);
+
+  // Cleanning
+  DeleteFile(target_file.c_str());
+  DeleteFile(link_file.c_str());
+  CoUninitialize();
+}
+
+TEST_F(FileUtilTest, CreateShortcutTest) {
+  const wchar_t file_contents[] = L"This is another target.";
+  std::wstring target_file = test_dir_;
+  file_util::AppendToPath(&target_file, L"Target1.txt");
+  CreateTextFile(target_file, file_contents);
+
+  std::wstring link_file = test_dir_;
+  file_util::AppendToPath(&link_file, L"Link1.lnk");
+
+  CoInitialize(NULL);
+  EXPECT_TRUE(file_util::CreateShortcutLink(target_file.c_str(),
+                                            link_file.c_str(),
+                                            NULL, NULL, NULL, NULL, 0));
+  std::wstring resolved_name = link_file;
+  EXPECT_TRUE(file_util::ResolveShortcut(&resolved_name));
+  std::wstring read_contents = ReadTextFile(resolved_name);
+  EXPECT_EQ(file_contents, read_contents);
+
+  DeleteFile(target_file.c_str());
+  DeleteFile(link_file.c_str());
+  CoUninitialize();
+}
+
+TEST_F(FileUtilTest, CreateTemporaryFileNameTest) {
+  std::wstring temp_file;
+  file_util::CreateTemporaryFileName(&temp_file);
+  EXPECT_EQ(file_util::PathExists(temp_file), true);
+}
+
+TEST_F(FileUtilTest, CreateNewTempDirectoryTest) {
+  std::wstring temp_dir;
+  file_util::CreateNewTempDirectory(std::wstring(), &temp_dir);
+  EXPECT_EQ(file_util::PathExists(temp_dir), true);
+}
+
+TEST_F(FileUtilTest, CreateDirectoryTest) {
+  std::wstring test_root = test_dir_;
+  file_util::AppendToPath(&test_root, L"create_directory_test");
+  std::wstring test_path(test_root);
+  file_util::AppendToPath(&test_path, L"dir\\tree\\likely\\doesnt\\exist\\");
+
+  EXPECT_EQ(file_util::PathExists(test_path), false);
+  EXPECT_EQ(file_util::CreateDirectory(test_path), true);
+  EXPECT_EQ(file_util::PathExists(test_path), true);
+  EXPECT_EQ(file_util::Delete(test_root, true), true);
+  EXPECT_EQ(file_util::PathExists(test_root), false);
+  EXPECT_EQ(file_util::PathExists(test_path), false);
+}
+
+static const struct {
+  std::wstring bad_name;
+  std::wstring good_name;
+} kIllegalCharacterCases[] = {
+  {L"bad*file:name?.jpg", L"bad-file-name-.jpg"},
+  {L"**********::::.txt", L"--------------.txt"},
+  {L"bad*file\\name.jpg", L"bad-file-name.jpg"},
+  // We can't use UCNs (universal character names) for C0/C1 characters and
+  // U+007F, but \x escape is interpreted by MSVC and gcc as we intend.
+  {L"bad\x0003\x0091 file\u200E\u200Fname.png", L"bad-- file--name.png"},
+  {L"\t  bad*file\\name/.jpg ", L"bad-file-name-.jpg"},
+  {L"bad\uFFFFfile\U0010FFFEname.jpg ", L"bad-file-name.jpg"},
+  {L"this_file_name is okay!.mp3", L"this_file_name is okay!.mp3"},
+  {L"\u4E00\uAC00.mp3", L"\u4E00\uAC00.mp3"},
+  {L"\u0635\u200C\u0644.mp3", L"\u0635\u200C\u0644.mp3"},
+  {L"\U00010330\U00010331.mp3", L"\U00010330\U00010331.mp3"},
+  // Unassigned codepoints are ok.
+  {L"\u0378\U00040001.mp3", L"\u0378\U00040001.mp3"},
+};
+
+TEST_F(FileUtilTest, ReplaceIllegalCharactersTest) {
+  for (int i = 0; i < arraysize(kIllegalCharacterCases); ++i) {
+    std::wstring bad_name(kIllegalCharacterCases[i].bad_name);
+    file_util::ReplaceIllegalCharacters(&bad_name, L'-');
+    EXPECT_EQ(kIllegalCharacterCases[i].good_name, bad_name);
+  }
+}
+
+static const struct ReplaceExtensionCase {
+  std::wstring file_name;
+  std::wstring extension;
+  std::wstring result;
+} kReplaceExtension[] = {
+  {L"", L"", L""},
+  {L"", L"txt", L".txt"},
+  {L".", L"txt", L".txt"},
+  {L".", L"", L""},
+  {L"foo.dll", L"txt", L"foo.txt"},
+  {L"foo.dll", L".txt", L"foo.txt"},
+  {L"foo", L"txt", L"foo.txt"},
+  {L"foo", L".txt", L"foo.txt"},
+  {L"foo.baz.dll", L"txt", L"foo.baz.txt"},
+  {L"foo.baz.dll", L".txt", L"foo.baz.txt"},
+  {L"foo.dll", L"", L"foo"},
+  {L"foo.dll", L".", L"foo"},
+  {L"foo", L"", L"foo"},
+  {L"foo", L".", L"foo"},
+  {L"foo.baz.dll", L"", L"foo.baz"},
+  {L"foo.baz.dll", L".", L"foo.baz"},
+};
+
+TEST_F(FileUtilTest, ReplaceExtensionTest) {
+  for (int i = 0; i < arraysize(kReplaceExtension); ++i) {
+    std::wstring file_name(kReplaceExtension[i].file_name);
+    file_util::ReplaceExtension(&file_name, kReplaceExtension[i].extension);
+    EXPECT_EQ(file_name, kReplaceExtension[i].result);
+  }
+}
+
+TEST_F(FileUtilTest, FileEnumeratorTest) {
+  // Test an empty directory.
+  file_util::FileEnumerator f0(test_dir_, true,
+      file_util::FileEnumerator::FILES_AND_DIRECTORIES);
+  EXPECT_EQ(f0.Next(), L"");
+  EXPECT_EQ(f0.Next(), L"");
+
+  // Populate the test dir.
+  file_util::CreateDirectory(test_dir_ + L"\\dir1");
+  file_util::CreateDirectory(test_dir_ + L"\\dir2");
+  CreateTextFile(test_dir_ + L"\\dir2\\dir2file.txt", L"");
+  file_util::CreateDirectory(test_dir_ + L"\\dir2\\inner");
+  CreateTextFile(test_dir_ + L"\\dir2\\inner\\innerfile.txt", L"");
+  CreateTextFile(test_dir_ + L"\\file1.txt", L"");
+  CreateTextFile(test_dir_ + L"\\file2.txt", L"");
+
+  // Only enumerate files.
+  file_util::FileEnumerator f1(test_dir_, true,
+                               file_util::FileEnumerator::FILES);
+  FindResultCollector c1(f1);
+  EXPECT_TRUE(c1.HasFile(test_dir_ + L"\\file1.txt"));
+  EXPECT_TRUE(c1.HasFile(test_dir_ + L"\\file2.txt"));
+  EXPECT_TRUE(c1.HasFile(test_dir_ + L"\\dir2\\dir2file.txt"));
+  EXPECT_TRUE(c1.HasFile(test_dir_ + L"\\dir2\\inner\\innerfile.txt"));
+
+  // Only enumerate directories.
+  file_util::FileEnumerator f2(test_dir_, true,
+                               file_util::FileEnumerator::DIRECTORIES);
+  FindResultCollector c2(f2);
+  EXPECT_TRUE(c2.HasFile(test_dir_ + L"\\dir1"));
+  EXPECT_TRUE(c2.HasFile(test_dir_ + L"\\dir2"));
+  EXPECT_TRUE(c2.HasFile(test_dir_ + L"\\dir2\\inner"));
+
+  // Enumerate files and directories.
+  file_util::FileEnumerator f3(test_dir_, true,
+      file_util::FileEnumerator::FILES_AND_DIRECTORIES);
+  FindResultCollector c3(f3);
+  EXPECT_TRUE(c3.HasFile(test_dir_ + L"\\dir1"));
+  EXPECT_TRUE(c3.HasFile(test_dir_ + L"\\dir2"));
+  EXPECT_TRUE(c3.HasFile(test_dir_ + L"\\file1.txt"));
+  EXPECT_TRUE(c3.HasFile(test_dir_ + L"\\file2.txt"));
+  EXPECT_TRUE(c3.HasFile(test_dir_ + L"\\dir2\\dir2file.txt"));
+  EXPECT_TRUE(c3.HasFile(test_dir_ + L"\\dir2\\dir2file.txt"));
+  EXPECT_TRUE(c3.HasFile(test_dir_ + L"\\dir2\\inner"));
+  EXPECT_TRUE(c3.HasFile(test_dir_ + L"\\dir2\\inner\\innerfile.txt"));
+
+  // Non-recursive operation.
+  file_util::FileEnumerator f4(test_dir_, false,
+      file_util::FileEnumerator::FILES_AND_DIRECTORIES);
+  FindResultCollector c4(f4);
+  EXPECT_TRUE(c4.HasFile(test_dir_ + L"\\dir1"));
+  EXPECT_TRUE(c4.HasFile(test_dir_ + L"\\dir2"));
+  EXPECT_TRUE(c4.HasFile(test_dir_ + L"\\file1.txt"));
+  EXPECT_TRUE(c4.HasFile(test_dir_ + L"\\file2.txt"));
+
+  // Enumerate with a pattern.
+  file_util::FileEnumerator f5(test_dir_, true,
+      file_util::FileEnumerator::FILES_AND_DIRECTORIES, L"dir*");
+  FindResultCollector c5(f5);
+  EXPECT_TRUE(c5.HasFile(test_dir_ + L"\\dir1"));
+  EXPECT_TRUE(c5.HasFile(test_dir_ + L"\\dir2"));
+  EXPECT_TRUE(c5.HasFile(test_dir_ + L"\\dir2\\dir2file.txt"));
+  EXPECT_TRUE(c5.HasFile(test_dir_ + L"\\dir2\\inner"));
+  EXPECT_TRUE(c5.HasFile(test_dir_ + L"\\dir2\\inner\\innerfile.txt"));
+
+  // Make sure the destructor closes the find handle while in the middle of a
+  // query to allow TearDown to delete the directory.
+  file_util::FileEnumerator f6(test_dir_, true,
+      file_util::FileEnumerator::FILES_AND_DIRECTORIES);
+  EXPECT_FALSE(f6.Next().empty());  // Should have found something
+                                    // (we don't care what).
+}
diff --git a/base/file_version_info.cc b/base/file_version_info.cc
new file mode 100644
index 0000000..90aedd2
--- /dev/null
+++ b/base/file_version_info.cc
@@ -0,0 +1,205 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+
+#include "base/file_version_info.h"
+
+#include "base/logging.h"
+#include "base/path_service.h"
+
+// This has to be last.
+#include <strsafe.h>
+
+FileVersionInfo::FileVersionInfo(void* data, int language, int code_page)
+    : language_(language), code_page_(code_page) {
+  data_.reset((char*) data);
+  fixed_file_info_ = NULL;
+  UINT size;
+  ::VerQueryValue(data_.get(), L"\\", (LPVOID*)&fixed_file_info_, &size);
+}
+
+FileVersionInfo::~FileVersionInfo() {
+  DCHECK(data_.get());
+}
+
+typedef struct {
+  WORD language;
+  WORD code_page;
+} LanguageAndCodePage;
+
+// static
+FileVersionInfo* FileVersionInfo::CreateFileVersionInfoForCurrentModule() {
+  std::wstring app_path;
+  if (!PathService::Get(base::FILE_MODULE, &app_path))
+    return NULL;
+
+  return CreateFileVersionInfo(app_path);
+}
+
+// static
+FileVersionInfo* FileVersionInfo::CreateFileVersionInfo(
+    const std::wstring& file_path) {
+  DWORD dummy;
+  const wchar_t* path = file_path.c_str();
+  DWORD length = ::GetFileVersionInfoSize(path, &dummy);
+  if (length == 0)
+    return NULL;
+
+  void* data = calloc(length, 1);
+  if (!data)
+    return NULL;
+
+  if (!::GetFileVersionInfo(path, dummy, length, data)) {
+    free(data);
+    return NULL;
+  }
+
+  LanguageAndCodePage* translate = NULL;
+  uint32 page_count;
+  BOOL query_result = VerQueryValue(data, L"\\VarFileInfo\\Translation",
+                                   (void**) &translate, &page_count);
+
+  if (query_result && translate) {
+    return new FileVersionInfo(data, translate->language,
+                               translate->code_page);
+
+  } else {
+    free(data);
+    return NULL;
+  }
+}
+
+std::wstring FileVersionInfo::company_name() {
+  return GetStringValue(L"CompanyName");
+}
+
+std::wstring FileVersionInfo::company_short_name() {
+  return GetStringValue(L"CompanyShortName");
+}
+
+std::wstring FileVersionInfo::internal_name() {
+  return GetStringValue(L"InternalName");
+}
+
+std::wstring FileVersionInfo::product_name() {
+  return GetStringValue(L"ProductName");
+}
+
+std::wstring FileVersionInfo::product_short_name() {
+  return GetStringValue(L"ProductShortName");
+}
+
+std::wstring FileVersionInfo::comments() {
+  return GetStringValue(L"Comments");
+}
+
+std::wstring FileVersionInfo::legal_copyright() {
+  return GetStringValue(L"LegalCopyright");
+}
+
+std::wstring FileVersionInfo::product_version() {
+  return GetStringValue(L"ProductVersion");
+}
+
+std::wstring FileVersionInfo::file_description() {
+  return GetStringValue(L"FileDescription");
+}
+
+std::wstring FileVersionInfo::legal_trademarks() {
+  return GetStringValue(L"LegalTrademarks");
+}
+
+std::wstring FileVersionInfo::private_build() {
+  return GetStringValue(L"PrivateBuild");
+}
+
+std::wstring FileVersionInfo::file_version() {
+  return GetStringValue(L"FileVersion");
+}
+
+std::wstring FileVersionInfo::original_filename() {
+  return GetStringValue(L"OriginalFilename");
+}
+
+std::wstring FileVersionInfo::special_build() {
+  return GetStringValue(L"SpecialBuild");
+}
+
+std::wstring FileVersionInfo::last_change() {
+  return GetStringValue(L"LastChange");
+}
+
+bool FileVersionInfo::is_official_build() {
+  return (GetStringValue(L"Official Build").compare(L"1") == 0);
+}
+
+bool FileVersionInfo::GetValue(const wchar_t* name, std::wstring* value_str) {
+
+  WORD lang_codepage[8];
+  int i = 0;
+  // Use the language and codepage from the DLL.
+  lang_codepage[i++] = language_;
+  lang_codepage[i++] = code_page_;
+  // Use the default language and codepage from the DLL.
+  lang_codepage[i++] = ::GetUserDefaultLangID();
+  lang_codepage[i++] = code_page_;
+  // Use the language from the DLL and Latin codepage (most common).
+  lang_codepage[i++] = language_;
+  lang_codepage[i++] = 1252;
+  // Use the default language and Latin codepage (most common).
+  lang_codepage[i++] = ::GetUserDefaultLangID();
+  lang_codepage[i++] = 1252;
+
+  i = 0;
+  while (i < arraysize(lang_codepage)) {
+    wchar_t sub_block[MAX_PATH];
+    WORD language = lang_codepage[i++];
+    WORD code_page = lang_codepage[i++];
+    _snwprintf_s(sub_block, MAX_PATH, MAX_PATH,
+                 L"\\StringFileInfo\\%04x%04x\\%s", language, code_page, name);
+    LPVOID value = NULL;
+    uint32 size;
+    BOOL r = ::VerQueryValue(data_.get(), sub_block, &value, &size);
+    if (r && value) {
+      value_str->assign(static_cast<wchar_t*>(value));
+      return true;
+    }
+  }
+  return false;
+}
+
+std::wstring FileVersionInfo::GetStringValue(const wchar_t* name) {
+  std::wstring str;
+  if (GetValue(name, &str))
+    return str;
+  else
+    return L"";
+}
+
diff --git a/base/file_version_info.h b/base/file_version_info.h
new file mode 100644
index 0000000..8d82e87
--- /dev/null
+++ b/base/file_version_info.h
@@ -0,0 +1,97 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_FILE_VERSION_INFO_H__
+#define BASE_FILE_VERSION_INFO_H__
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+
+// Provides a way to access the version information for a file.
+// This is the information you access when you select a file in the Windows
+// explorer, right-click select Properties, then click the Version tab.
+
+class FileVersionInfo {
+ public:
+  // Creates a FileVersionInfo for the specified path. Returns NULL if something
+  // goes wrong (typically the file does not exit or cannot be opened). The
+  // returned object should be deleted when you are done with it.
+  static FileVersionInfo* CreateFileVersionInfo(const std::wstring& file_path);
+
+  // Creates a FileVersionInfo for the current application. Returns NULL in case
+  // of error. The returned object should be deleted when you are done with it.
+  static FileVersionInfo*
+      FileVersionInfo::CreateFileVersionInfoForCurrentModule();
+
+  ~FileVersionInfo();
+
+  // Accessors to the different version properties.
+  // Returns an empty string if the property is not found.
+  std::wstring company_name();
+  std::wstring company_short_name();
+  std::wstring product_name();
+  std::wstring product_short_name();
+  std::wstring internal_name();
+  std::wstring product_version();
+  std::wstring private_build();
+  std::wstring special_build();
+  std::wstring comments();
+  std::wstring original_filename();
+  std::wstring file_description();
+  std::wstring file_version();
+  std::wstring legal_copyright();
+  std::wstring legal_trademarks();
+  std::wstring last_change();
+  bool is_official_build();
+
+  // Lets you access other properties not covered above.
+  bool GetValue(const wchar_t* name, std::wstring* value);
+
+  // Similar to GetValue but returns a wstring (empty string if the property
+  // does not exist).
+  std::wstring GetStringValue(const wchar_t* name);
+
+  // Get the fixed file info if it exists. Otherwise NULL
+  VS_FIXEDFILEINFO* fixed_file_info() { return fixed_file_info_; }
+
+ private:
+  FileVersionInfo(void* data, int language, int code_page);
+
+  scoped_ptr_malloc<char> data_;
+  int language_;
+  int code_page_;
+  // This is a pointer into the data_ if it exists. Otherwise NULL.
+  VS_FIXEDFILEINFO* fixed_file_info_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(FileVersionInfo);
+};
+
+#endif  // BASE_FILE_VERSION_INFO_H__
diff --git a/base/file_version_info_unittest.cc b/base/file_version_info_unittest.cc
new file mode 100644
index 0000000..272cba8
--- /dev/null
+++ b/base/file_version_info_unittest.cc
@@ -0,0 +1,153 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/scoped_ptr.h"
+#include "base/file_version_info.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class FileVersionInfoTest : public testing::Test {
+};
+
+std::wstring GetTestDataPath() {
+  std::wstring path;
+  PathService::Get(base::DIR_SOURCE_ROOT, &path);
+  file_util::AppendToPath(&path, L"base");
+  file_util::AppendToPath(&path, L"data");
+  file_util::AppendToPath(&path, L"file_version_info_unittest");
+  return path;
+}
+
+}
+
+TEST(FileVersionInfoTest, HardCodedProperties) {
+  const wchar_t* kDLLNames[] = {
+    L"FileVersionInfoTest1.dll"
+  };
+
+  const wchar_t* kExpectedValues[1][15] = {
+      // FileVersionInfoTest.dll
+      L"Goooooogle",                      // company_name
+      L"Google",                          // company_short_name
+      L"This is the product name",        // product_name
+      L"This is the product short name",  // product_short_name
+      L"The Internal Name",               // internal_name
+      L"4.3.2.1",                         // product_version
+      L"Private build property",          // private_build
+      L"Special build property",          // special_build
+      L"This is a particularly interesting comment",  // comments
+      L"This is the original filename",   // original_filename
+      L"This is my file description",     // file_description
+      L"1.2.3.4",                         // file_version
+      L"This is the legal copyright",     // legal_copyright
+      L"This is the legal trademarks",    // legal_trademarks
+      L"This is the last change",         // last_change
+
+  };
+
+  for (int i = 0; i < arraysize(kDLLNames); ++i) {
+    std::wstring dll_path = GetTestDataPath();
+    file_util::AppendToPath(&dll_path, kDLLNames[i]);
+
+    scoped_ptr<FileVersionInfo> version_info(
+        FileVersionInfo::CreateFileVersionInfo(dll_path));
+
+    int j = 0;
+    EXPECT_EQ(kExpectedValues[i][j++], version_info->company_name());
+    EXPECT_EQ(kExpectedValues[i][j++], version_info->company_short_name());
+    EXPECT_EQ(kExpectedValues[i][j++], version_info->product_name());
+    EXPECT_EQ(kExpectedValues[i][j++], version_info->product_short_name());
+    EXPECT_EQ(kExpectedValues[i][j++], version_info->internal_name());
+    EXPECT_EQ(kExpectedValues[i][j++], version_info->product_version());
+    EXPECT_EQ(kExpectedValues[i][j++], version_info->private_build());
+    EXPECT_EQ(kExpectedValues[i][j++], version_info->special_build());
+    EXPECT_EQ(kExpectedValues[i][j++], version_info->comments());
+    EXPECT_EQ(kExpectedValues[i][j++], version_info->original_filename());
+    EXPECT_EQ(kExpectedValues[i][j++], version_info->file_description());
+    EXPECT_EQ(kExpectedValues[i][j++], version_info->file_version());
+    EXPECT_EQ(kExpectedValues[i][j++], version_info->legal_copyright());
+    EXPECT_EQ(kExpectedValues[i][j++], version_info->legal_trademarks());
+    EXPECT_EQ(kExpectedValues[i][j++], version_info->last_change());
+  }
+}
+
+TEST(FileVersionInfoTest, IsOfficialBuild) {
+  const wchar_t* kDLLNames[] = {
+    L"FileVersionInfoTest1.dll",
+    L"FileVersionInfoTest2.dll"
+  };
+
+  const bool kExpected[] = {
+    true,
+    false,
+  };
+
+  // Test consistency check.
+  ASSERT_EQ(arraysize(kDLLNames), arraysize(kExpected));
+
+  for (int i = 0; i < arraysize(kDLLNames); ++i) {
+    std::wstring dll_path = GetTestDataPath();
+    file_util::AppendToPath(&dll_path, kDLLNames[i]);
+
+    scoped_ptr<FileVersionInfo> version_info(
+        FileVersionInfo::CreateFileVersionInfo(dll_path));
+
+    EXPECT_EQ(kExpected[i], version_info->is_official_build());
+  }
+}
+
+TEST(FileVersionInfoTest, CustomProperties) {
+  std::wstring dll_path = GetTestDataPath();
+  file_util::AppendToPath(&dll_path, L"FileVersionInfoTest1.dll");
+
+  scoped_ptr<FileVersionInfo> version_info(
+      FileVersionInfo::CreateFileVersionInfo(dll_path));
+
+  // Test few existing properties.
+  std::wstring str;
+  EXPECT_TRUE(version_info->GetValue(L"Custom prop 1",  &str));
+  EXPECT_EQ(L"Un", str);
+  EXPECT_EQ(L"Un", version_info->GetStringValue(L"Custom prop 1"));
+
+  EXPECT_TRUE(version_info->GetValue(L"Custom prop 2",  &str));
+  EXPECT_EQ(L"Deux", str);
+  EXPECT_EQ(L"Deux", version_info->GetStringValue(L"Custom prop 2"));
+
+  EXPECT_TRUE(version_info->GetValue(L"Custom prop 3",  &str));
+  EXPECT_EQ(L"1600 Amphitheatre Parkway Mountain View, CA 94043", str);
+  EXPECT_EQ(L"1600 Amphitheatre Parkway Mountain View, CA 94043",
+            version_info->GetStringValue(L"Custom prop 3"));
+
+  // Test an non-existing property.
+  EXPECT_FALSE(version_info->GetValue(L"Unknown property",  &str));
+  EXPECT_EQ(L"", version_info->GetStringValue(L"Unknown property"));
+}
diff --git a/base/fix_wp64.h b/base/fix_wp64.h
new file mode 100644
index 0000000..ff82a30
--- /dev/null
+++ b/base/fix_wp64.h
@@ -0,0 +1,100 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Various inline functions and macros to fix compilation of 32 bit target
+// on MSVC with /Wp64 flag enabled.
+
+#ifndef BASE_FIX_WP64_H__
+#define BASE_FIX_WP64_H__
+
+#include <windows.h>
+
+// Platform SDK fixes when building with /Wp64 for a 32 bits target.
+#if !defined(_WIN64) && defined(_Wp64)
+
+#ifdef InterlockedExchangePointer
+#undef InterlockedExchangePointer
+// The problem is that the macro provided for InterlockedExchangePointer() is
+// doing a (LONG) C-style cast that triggers invariably the warning C4312 when
+// building on 32 bits.
+inline void* InterlockedExchangePointer(void* volatile* target, void* value) {
+  return reinterpret_cast<void*>(static_cast<LONG_PTR>(InterlockedExchange(
+      reinterpret_cast<volatile LONG*>(target),
+      static_cast<LONG>(reinterpret_cast<LONG_PTR>(value)))));
+}
+#endif  // #ifdef InterlockedExchangePointer
+
+#ifdef SetWindowLongPtrA
+#undef SetWindowLongPtrA
+// When build on 32 bits, SetWindowLongPtrX() is a macro that redirects to
+// SetWindowLongX(). The problem is that this function takes a LONG argument
+// instead of a LONG_PTR.
+inline LONG_PTR SetWindowLongPtrA(HWND window, int index, LONG_PTR new_long) {
+  return ::SetWindowLongA(window, index, static_cast<LONG>(new_long));
+}
+#endif  // #ifdef SetWindowLongPtrA
+
+#ifdef SetWindowLongPtrW
+#undef SetWindowLongPtrW
+inline LONG_PTR SetWindowLongPtrW(HWND window, int index, LONG_PTR new_long) {
+  return ::SetWindowLongW(window, index, static_cast<LONG>(new_long));
+}
+#endif  // #ifdef SetWindowLongPtrW
+
+#ifdef GetWindowLongPtrA
+#undef GetWindowLongPtrA
+inline LONG_PTR GetWindowLongPtrA(HWND window, int index) {
+  return ::GetWindowLongA(window, index);
+}
+#endif  // #ifdef GetWindowLongPtrA
+
+#ifdef GetWindowLongPtrW
+#undef GetWindowLongPtrW
+inline LONG_PTR GetWindowLongPtrW(HWND window, int index) {
+  return ::GetWindowLongW(window, index);
+}
+#endif  // #ifdef GetWindowLongPtrW
+
+#ifdef SetClassLongPtrA
+#undef SetClassLongPtrA
+inline LONG_PTR SetClassLongPtrA(HWND window, int index, LONG_PTR new_long) {
+  return ::SetClassLongA(window, index, static_cast<LONG>(new_long));
+}
+#endif  // #ifdef SetClassLongPtrA
+
+#ifdef SetClassLongPtrW
+#undef SetClassLongPtrW
+inline LONG_PTR SetClassLongPtrW(HWND window, int index, LONG_PTR new_long) {
+  return ::SetClassLongW(window, index, static_cast<LONG>(new_long));
+}
+#endif  // #ifdef SetClassLongPtrW
+
+#endif  // #if !defined(_WIN64) && defined(_Wp64)
+
+#endif  // BASE_FIX_WP64_H__
diff --git a/base/fixed_string.h b/base/fixed_string.h
new file mode 100644
index 0000000..857b057
--- /dev/null
+++ b/base/fixed_string.h
@@ -0,0 +1,96 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_FIXED_STRING_H__
+#define BASE_FIXED_STRING_H__
+
+#include <string.h>
+
+#include "base/string_util.h"
+
+// This class manages a fixed-size, null-terminated string buffer.  It is meant
+// to be allocated on the stack, and it makes no use of the heap internally.  In
+// most cases you'll just want to use a std::(w)string, but when you need to
+// avoid the heap, you can use this class instead.
+//
+// Methods are provided to read the null-terminated buffer and to append data
+// to the buffer, and once the buffer fills-up, it simply discards any extra
+// append calls.
+//
+// Since this object clips if the internal fixed buffer is exceeded, it is
+// appropriate for exception handlers where the heap may be corrupted. Fixed
+// buffers that overflow onto the heap are provided by Stack[W]String.
+// (see stack_container.h).
+template <class CharT, int MaxSize>
+class FixedString {
+ public:
+  typedef CharTraits<CharT> char_traits;
+
+  FixedString() : index_(0), truncated_(false) {
+    buf_[0] = CharT(0);
+  }
+
+  // Returns true if the Append ever failed.
+  bool was_truncated() const { return truncated_; }
+
+  // Returns the number of characters in the string, excluding the null
+  // terminator.
+  size_t size() const { return index_; }
+
+  // Returns the null-terminated string.
+  const CharT* get() const { return buf_; }
+  CharT* get() { return buf_; }
+
+  // Append an array of characters.  The operation is bounds checked, and if
+  // there is insufficient room, then the was_truncated() flag is set to true.
+  void Append(const CharT* s, size_t n) {
+    if (char_traits::copy_num(buf_ + index_, arraysize(buf_) - index_, s, n)) {
+      index_ += n;
+    } else {
+      truncated_ = true;
+    }
+  }
+
+  // Append a null-terminated string.
+  void Append(const CharT* s) {
+    Append(s, char_traits::length(s));
+  }
+
+  // Append a single character.
+  void Append(CharT c) {
+    Append(&c, 1);
+  }
+
+ private:
+  CharT buf_[MaxSize];
+  size_t index_;
+  bool truncated_;
+};
+
+#endif  // BASE_FIXED_STRING_H__
diff --git a/base/fixed_string_unittest.cc b/base/fixed_string_unittest.cc
new file mode 100644
index 0000000..a7a0767
--- /dev/null
+++ b/base/fixed_string_unittest.cc
@@ -0,0 +1,62 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/fixed_string.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+  class FixedStringTest : public testing::Test {
+  };
+}
+
+TEST(FixedStringTest, TestBasic) {
+  const wchar_t kData[] = L"hello world";
+
+  FixedString<wchar_t, 40> buf;
+
+  buf.Append(kData);
+  EXPECT_EQ(arraysize(kData)-1, buf.size());
+  EXPECT_EQ(0, wcscmp(kData, buf.get()));
+
+  buf.Append(' ');
+  buf.Append(kData);
+  const wchar_t kExpected[] = L"hello world hello world";
+  EXPECT_EQ(arraysize(kExpected)-1, buf.size());
+  EXPECT_EQ(0, wcscmp(kExpected, buf.get()));
+  EXPECT_EQ(false, buf.was_truncated());
+}
+
+// Disable this test in debug builds since overflow asserts.
+TEST(FixedStringTest, TestOverflow) {
+  FixedString<wchar_t, 5> buf;
+  buf.Append(L"hello world");
+  EXPECT_EQ(0, buf.size());
+  EXPECT_EQ(0, buf.get()[0]);
+  EXPECT_EQ(true, buf.was_truncated());
+}
diff --git a/base/gfx/SConscript b/base/gfx/SConscript
new file mode 100644
index 0000000..4ec9e50
--- /dev/null
+++ b/base/gfx/SConscript
@@ -0,0 +1,83 @@
+# Copyright 2008, Google Inc.

+# All rights reserved.

+#

+# Redistribution and use in source and binary forms, with or without

+# modification, are permitted provided that the following conditions are

+# met:

+#

+#    * Redistributions of source code must retain the above copyright

+# notice, this list of conditions and the following disclaimer.

+#    * Redistributions in binary form must reproduce the above

+# copyright notice, this list of conditions and the following disclaimer

+# in the documentation and/or other materials provided with the

+# distribution.

+#    * Neither the name of Google Inc. nor the names of its

+# contributors may be used to endorse or promote products derived from

+# this software without specific prior written permission.

+#

+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+

+Import('env')

+

+env = env.Clone()

+

+env.Prepend(

+    CPPPATH = [

+        '$SKIA_DIR/include',

+        '$SKIA_DIR/include/corecg',

+        '$SKIA_DIR/include/platform',

+        '$ZLIB_DIR',

+        '$LIBPNG_DIR',

+        '$ICU38_DIR/public/common',

+        '$ICU38_DIR/public/i18n',

+        '../..',

+    ],

+    CPPDEFINES = [

+        'PNG_USER_CONFIG',

+        'CHROME_PNG_WRITE_SUPPORT',

+        'U_STATIC_IMPLEMENTATION',

+        'CERT_CHAIN_PARA_HAS_EXTRA_FIELDS',

+        'WIN32_LEAN_AND_MEAN',

+    ],

+    CCFLAGS = [

+        '/TP',

+

+        '/Wp64',

+        '/WX',

+

+        '/wd4503',

+        '/wd4819',

+    ],

+)

+

+input_files = [

+    'bitmap_header.cc',

+    'bitmap_platform_device.cc',

+    'convolver.cc',

+    'font_utils.cc',

+    'image_operations.cc',

+    'native_theme.cc',

+    'platform_canvas.cc',

+    'platform_device.cc',

+    'png_decoder.cc',

+    'png_encoder.cc',

+    'point.cc',

+    'rect.cc',

+    'size.cc',

+    'skia_utils.cc',

+    'uniscribe.cc',

+    'vector_canvas.cc',

+    'vector_device.cc',

+]

+

+env.StaticLibrary('base_gfx', input_files)

diff --git a/base/gfx/bitmap_header.cc b/base/gfx/bitmap_header.cc
new file mode 100644
index 0000000..3c54694
--- /dev/null
+++ b/base/gfx/bitmap_header.cc
@@ -0,0 +1,88 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/gfx/bitmap_header.h"
+
+namespace gfx {
+
+void CreateBitmapHeader(int width, int height, BITMAPINFOHEADER* hdr) {
+  CreateBitmapHeaderWithColorDepth(width, height, 32, hdr);
+}
+
+void CreateBitmapHeaderWithColorDepth(int width, int height, int color_depth,
+                                      BITMAPINFOHEADER* hdr) {
+  // These values are shared with gfx::PlatformDevice
+  hdr->biSize = sizeof(BITMAPINFOHEADER);
+  hdr->biWidth = width;
+  hdr->biHeight = -height;  // minus means top-down bitmap
+  hdr->biPlanes = 1;
+  hdr->biBitCount = color_depth;
+  hdr->biCompression = BI_RGB;  // no compression
+  hdr->biSizeImage = 0;
+  hdr->biXPelsPerMeter = 1;
+  hdr->biYPelsPerMeter = 1;
+  hdr->biClrUsed = 0;
+  hdr->biClrImportant = 0;
+}
+
+
+void CreateBitmapV4Header(int width, int height, BITMAPV4HEADER* hdr) {
+  // Because bmp v4 header is just an extension, we just create a v3 header and
+  // copy the bits over to the v4 header.
+  BITMAPINFOHEADER header_v3;
+  CreateBitmapHeader(width, height, &header_v3);
+  memset(hdr, 0, sizeof(BITMAPV4HEADER));
+  memcpy(hdr, &header_v3, sizeof(BITMAPINFOHEADER));
+
+  // Correct the size of the header and fill in the mask values.
+  hdr->bV4Size = sizeof(BITMAPV4HEADER);
+  hdr->bV4RedMask   = 0x00ff0000;
+  hdr->bV4GreenMask = 0x0000ff00;
+  hdr->bV4BlueMask  = 0x000000ff;
+  hdr->bV4AlphaMask = 0xff000000;
+}
+
+// Creates a monochrome bitmap header.
+void CreateMonochromeBitmapHeader(int width,
+                                  int height,
+                                  BITMAPINFOHEADER* hdr) {
+  hdr->biSize = sizeof(BITMAPINFOHEADER);
+  hdr->biWidth = width;
+  hdr->biHeight = -height;
+  hdr->biPlanes = 1;
+  hdr->biBitCount = 1;
+  hdr->biCompression = BI_RGB;
+  hdr->biSizeImage = 0;
+  hdr->biXPelsPerMeter = 1;
+  hdr->biYPelsPerMeter = 1;
+  hdr->biClrUsed = 0;
+  hdr->biClrImportant = 0;
+}
+
+}  // namespace gfx
diff --git a/base/gfx/bitmap_header.h b/base/gfx/bitmap_header.h
new file mode 100644
index 0000000..e8179c8
--- /dev/null
+++ b/base/gfx/bitmap_header.h
@@ -0,0 +1,56 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_GFX_BITMAP_HEADER_H__
+#define BASE_GFX_BITMAP_HEADER_H__
+
+#include <windows.h>
+
+namespace gfx {
+
+// Creates a BITMAPINFOHEADER structure given the bitmap's size.
+void CreateBitmapHeader(int width, int height, BITMAPINFOHEADER* hdr);
+
+// Creates a BITMAPINFOHEADER structure given the bitmap's size and
+// color depth in bits per pixel.
+void CreateBitmapHeaderWithColorDepth(int width, int height, int color_depth,
+                                      BITMAPINFOHEADER* hdr);
+
+// Creates a BITMAPV4HEADER structure given the bitmap's size.  You probably
+// only need to use BMP V4 if you need transparency (alpha channel). This
+// function sets the AlphaMask to 0xff000000.
+void CreateBitmapV4Header(int width, int height, BITMAPV4HEADER* hdr);
+
+// Creates a monochrome bitmap header.
+void CreateMonochromeBitmapHeader(int width, int height, BITMAPINFOHEADER* hdr);
+
+
+}  // namespace gfx
+
+#endif // BASE_GFX_BITMAP_HEADER_H__
diff --git a/base/gfx/bitmap_platform_device.cc b/base/gfx/bitmap_platform_device.cc
new file mode 100644
index 0000000..0a914e2
--- /dev/null
+++ b/base/gfx/bitmap_platform_device.cc
@@ -0,0 +1,482 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/gfx/bitmap_platform_device.h"
+
+#include "base/gfx/bitmap_header.h"
+#include "base/logging.h"
+#include "SkMatrix.h"
+#include "SkRegion.h"
+#include "SkUtils.h"
+
+namespace gfx {
+
+// When Windows draws text, is sets the fourth byte (which Skia uses for alpha)
+// to zero. This means that if we try compositing with text that Windows has
+// drawn, we get invalid color values (if the alpha is 0, the other channels
+// should be 0 since Skia uses premultiplied colors) and strange results.
+//
+// HTML rendering only requires one bit of transparency. When you ask for a
+// semitransparent div, the div itself is drawn in another layer as completely
+// opaque, and then composited onto the lower layer with a transfer function.
+// The only place an alpha channel is needed is to track what has been drawn
+// and what has not been drawn.
+//
+// Therefore, when we allocate a new device, we fill it with this special
+// color. Because Skia uses premultiplied colors, any color where the alpha
+// channel is smaller than any component is impossible, so we know that no
+// legitimate drawing will produce this color. We use 1 as the alpha value
+// because 0 is produced when Windows draws text (even though it should be
+// opaque).
+//
+// When a layer is done and we want to render it to a lower layer, we use
+// fixupAlphaBeforeCompositing. This replaces all 0 alpha channels with
+// opaque (to fix the text problem), and replaces this magic color value
+// with transparency. The result is something that can be correctly
+// composited. However, once this has been done, no more can be drawn to
+// the layer because fixing the alphas *again* will result in incorrect
+// values.
+static const uint32_t kMagicTransparencyColor = 0x01FFFEFD;
+
+namespace {
+
+// Constrains position and size to fit within available_size. If |size| is -1,
+// all the available_size is used. Returns false if the position is out of
+// available_size.
+bool Constrain(int available_size, int* position, int *size) {
+  if (*size < -2)
+    return false;
+
+  if (*position < 0) {
+    if (*size != -1)
+      *size += *position;
+    *position = 0;
+  }
+  if (*size == 0 || *position >= available_size)
+    return false;
+
+  if (*size > 0) {
+    int overflow = (*position + *size) - available_size;
+    if (overflow > 0) {
+      *size -= overflow;
+    }
+  } else {
+    // Fill up available size.
+    *size = available_size - *position;
+  }
+  return true;
+}
+
+// If the pixel value is 0, it gets set to kMagicTransparencyColor.
+void PrepareAlphaForGDI(uint32_t* pixel) {
+  if (*pixel == 0) {
+    *pixel = kMagicTransparencyColor;
+  }
+}
+
+// If the pixel value is kMagicTransparencyColor, it gets set to 0. Otherwise
+// if the alpha is 0, the alpha is set to 255.
+void PostProcessAlphaForGDI(uint32_t* pixel) {
+  if (*pixel == kMagicTransparencyColor) {
+    *pixel = 0;
+  } else if ((*pixel & 0xFF000000) == 0) {
+    *pixel |= 0xFF000000;
+  }
+}
+
+// Sets the opacity of the specified value to 0xFF.
+void MakeOpaqueAlphaAdjuster(uint32_t* pixel) {
+  *pixel |= 0xFF000000;
+}
+
+// See the declaration of kMagicTransparencyColor at the top of the file.
+void FixupAlphaBeforeCompositing(uint32_t* pixel) {
+  if (*pixel == kMagicTransparencyColor)
+    *pixel = 0;
+  else
+    *pixel |= 0xFF000000;
+}
+
+}  // namespace
+
+class BitmapPlatformDevice::BitmapPlatformDeviceData
+    : public base::RefCounted<BitmapPlatformDeviceData> {
+ public:
+  explicit BitmapPlatformDeviceData(HBITMAP hbitmap);
+
+  // Create/destroy hdc_, which is the memory DC for our bitmap data.
+  HDC GetBitmapDC();
+  void ReleaseBitmapDC();
+  bool IsBitmapDCCreated() const;
+
+  // Sets the transform and clip operations. This will not update the DC,
+  // but will mark the config as dirty. The next call of LoadConfig will
+  // pick up these changes.
+  void SetMatrixClip(const SkMatrix& transform, const SkRegion& region);
+  // The device offset is already modified according to the transformation.
+  void SetDeviceOffset(int x, int y);
+
+  const SkMatrix& transform() const {
+    return transform_;
+  }
+
+ protected:
+  // Loads the current transform (taking into account offset_*_) and clip
+  // into the DC. Can be called even when the DC is NULL (will be a NOP).
+  void LoadConfig();
+
+  // Windows bitmap corresponding to our surface.
+  HBITMAP hbitmap_;
+
+  // Lazily-created DC used to draw into the bitmap, see getBitmapDC.
+  HDC hdc_;
+
+  // Additional offset applied to the transform. See setDeviceOffset().
+  int offset_x_;
+  int offset_y_;
+
+  // True when there is a transform or clip that has not been set to the DC.
+  // The DC is retrieved for every text operation, and the transform and clip
+  // do not change as much. We can save time by not loading the clip and
+  // transform for every one.
+  bool config_dirty_;
+
+  // Translation assigned to the DC: we need to keep track of this separately
+  // so it can be updated even if the DC isn't created yet.
+  SkMatrix transform_;
+
+  // The current clipping
+  SkRegion clip_region_;
+
+ private:
+  friend class base::RefCounted<BitmapPlatformDeviceData>;
+  ~BitmapPlatformDeviceData();
+
+  DISALLOW_EVIL_CONSTRUCTORS(BitmapPlatformDeviceData);
+};
+
+BitmapPlatformDevice::BitmapPlatformDeviceData::BitmapPlatformDeviceData(
+    HBITMAP hbitmap)
+    : hbitmap_(hbitmap),
+      hdc_(NULL),
+      offset_x_(0),
+      offset_y_(0),
+      config_dirty_(true) {  // Want to load the config next time.
+  // Initialize the clip region to the entire bitmap.
+  BITMAP bitmap_data;
+  if (GetObject(hbitmap_, sizeof(BITMAP), &bitmap_data)) {
+    SkIRect rect;
+    rect.set(0, 0, bitmap_data.bmWidth, bitmap_data.bmHeight);
+    clip_region_ = SkRegion(rect);
+  }
+
+  transform_.reset();
+}
+
+BitmapPlatformDevice::BitmapPlatformDeviceData::~BitmapPlatformDeviceData() {
+  if (hdc_)
+    ReleaseBitmapDC();
+
+  // this will free the bitmap data as well as the bitmap handle
+  DeleteObject(hbitmap_);
+}
+
+HDC BitmapPlatformDevice::BitmapPlatformDeviceData::GetBitmapDC() {
+  if (!hdc_) {
+    hdc_ = CreateCompatibleDC(NULL);
+    InitializeDC(hdc_);
+    HGDIOBJ old_bitmap = SelectObject(hdc_, hbitmap_);
+    // When the memory DC is created, its display surface is exactly one
+    // monochrome pixel wide and one monochrome pixel high. Since we select our
+    // own bitmap, we must delete the previous one.
+    DeleteObject(old_bitmap);
+  }
+
+  LoadConfig();
+  return hdc_;
+}
+
+void BitmapPlatformDevice::BitmapPlatformDeviceData::ReleaseBitmapDC() {
+  DCHECK(hdc_);
+  DeleteDC(hdc_);
+  hdc_ = NULL;
+}
+
+bool BitmapPlatformDevice::BitmapPlatformDeviceData::IsBitmapDCCreated() const {
+  return hdc_ != NULL;
+}
+
+
+void BitmapPlatformDevice::BitmapPlatformDeviceData::SetMatrixClip(
+    const SkMatrix& transform,
+    const SkRegion& region) {
+  transform_ = transform;
+  clip_region_ = region;
+  config_dirty_ = true;
+}
+
+void BitmapPlatformDevice::BitmapPlatformDeviceData::SetDeviceOffset(int x,
+                                                                     int y) {
+  offset_x_ = x;
+  offset_y_ = y;
+  config_dirty_ = true;
+}
+
+void BitmapPlatformDevice::BitmapPlatformDeviceData::LoadConfig() {
+  if (!config_dirty_ || !hdc_)
+    return;  // Nothing to do.
+  config_dirty_ = false;
+
+  // Transform.
+  SkMatrix t(transform_);
+  t.postTranslate(SkIntToScalar(-offset_x_), SkIntToScalar(-offset_y_));
+  LoadTransformToDC(hdc_, t);
+  // We don't use transform_ for the clipping region since the translation is
+  // already applied to offset_x_ and offset_y_.
+  t.reset();
+  t.postTranslate(SkIntToScalar(-offset_x_), SkIntToScalar(-offset_y_));
+  LoadClippingRegionToDC(hdc_, clip_region_, t);
+}
+
+// We use this static factory function instead of the regular constructor so
+// that we can create the pixel data before calling the constructor. This is
+// required so that we can call the base class' constructor with the pixel
+// data.
+BitmapPlatformDevice* BitmapPlatformDevice::create(HDC screen_dc,
+                                                   int width,
+                                                   int height,
+                                                   bool is_opaque,
+                                                   HANDLE shared_section) {
+  SkBitmap bitmap;
+
+  // CreateDIBSection appears to get unhappy if we create an empty bitmap, so
+  // we just expand it here.
+  if (width == 0)
+    width = 1;
+  if (height == 0)
+    height = 1;
+
+  BITMAPINFOHEADER hdr;
+  CreateBitmapHeader(width, height, &hdr);
+
+  void* data;
+  HBITMAP hbitmap = CreateDIBSection(screen_dc,
+                                     reinterpret_cast<BITMAPINFO*>(&hdr), 0,
+                                     &data,
+                                     shared_section, 0);
+
+  // If we run out of GDI objects or some other error occurs, we won't get a
+  // bitmap here. This will cause us to crash later because the data pointer is
+  // NULL. To make sure that we can assign blame for those crashes to this code,
+  // we deliberately crash here, even in release mode.
+  CHECK(hbitmap);
+
+  bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
+  bitmap.setPixels(data);
+  bitmap.setIsOpaque(is_opaque);
+
+  if (is_opaque) {
+#ifndef NDEBUG
+    // To aid in finding bugs, we set the background color to something
+    // obviously wrong so it will be noticable when it is not cleared
+    bitmap.eraseARGB(255, 0, 255, 128);  // bright bluish green
+#endif
+  } else {
+    // A transparent layer is requested: fill with our magic "transparent"
+    // color, see the declaration of kMagicTransparencyColor above
+    sk_memset32(static_cast<uint32_t*>(data), kMagicTransparencyColor,
+                width * height);
+  }
+
+  // The device object will take ownership of the HBITMAP.
+  return new BitmapPlatformDevice(new BitmapPlatformDeviceData(hbitmap), bitmap);
+}
+
+// The device will own the HBITMAP, which corresponds to also owning the pixel
+// data. Therefore, we do not transfer ownership to the SkDevice's bitmap.
+BitmapPlatformDevice::BitmapPlatformDevice(BitmapPlatformDeviceData* data,
+                                           const SkBitmap& bitmap)
+    : PlatformDevice(bitmap),
+      data_(data) {
+}
+
+// The copy constructor just adds another reference to the underlying data.
+// We use a const cast since the default Skia definitions don't define the
+// proper constedness that we expect (accessBitmap should really be const).
+BitmapPlatformDevice::BitmapPlatformDevice(const BitmapPlatformDevice& other)
+    : PlatformDevice(
+          const_cast<BitmapPlatformDevice&>(other).accessBitmap(true)),
+      data_(other.data_) {
+}
+
+BitmapPlatformDevice::~BitmapPlatformDevice() {
+}
+
+BitmapPlatformDevice& BitmapPlatformDevice::operator=(
+    const BitmapPlatformDevice& other) {
+  data_ = other.data_;
+  return *this;
+}
+
+HDC BitmapPlatformDevice::getBitmapDC() {
+  return data_->GetBitmapDC();
+}
+
+void BitmapPlatformDevice::setMatrixClip(const SkMatrix& transform,
+                                         const SkRegion& region) {
+  data_->SetMatrixClip(transform, region);
+}
+
+void BitmapPlatformDevice::setDeviceOffset(int x, int y) {
+  data_->SetDeviceOffset(x, y);
+}
+
+void BitmapPlatformDevice::drawToHDC(HDC dc, int x, int y,
+                                     const RECT* src_rect) {
+  bool created_dc = !data_->IsBitmapDCCreated();
+  HDC source_dc = getBitmapDC();
+
+  RECT temp_rect;
+  if (!src_rect) {
+    temp_rect.left = 0;
+    temp_rect.right = width();
+    temp_rect.top = 0;
+    temp_rect.bottom = height();
+    src_rect = &temp_rect;
+  }
+
+  int copy_width = src_rect->right - src_rect->left;
+  int copy_height = src_rect->bottom - src_rect->top;
+
+  // We need to reset the translation for our bitmap or (0,0) won't be in the
+  // upper left anymore
+  SkMatrix identity;
+  identity.reset();
+
+  LoadTransformToDC(source_dc, identity);
+  if (isOpaque()) {
+    BitBlt(dc,
+           x,
+           y,
+           copy_width,
+           copy_height,
+           source_dc,
+           src_rect->left,
+           src_rect->top,
+           SRCCOPY);
+  } else {
+    BLENDFUNCTION blend_function = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
+    AlphaBlend(dc,
+               x,
+               y,
+               copy_width,
+               copy_height,
+               source_dc,
+               src_rect->left,
+               src_rect->top,
+               copy_width,
+               copy_height,
+               blend_function);
+  }
+  LoadTransformToDC(source_dc, data_->transform());
+
+  if (created_dc)
+    data_->ReleaseBitmapDC();
+}
+
+void BitmapPlatformDevice::prepareForGDI(int x, int y, int width, int height) {
+  processPixels<PrepareAlphaForGDI>(x, y, width, height);
+}
+
+void BitmapPlatformDevice::postProcessGDI(int x, int y, int width, int height) {
+  processPixels<PostProcessAlphaForGDI>(x, y, width, height);
+}
+
+void BitmapPlatformDevice::makeOpaque(int x, int y, int width, int height) {
+  processPixels<MakeOpaqueAlphaAdjuster>(x, y, width, height);
+}
+
+void BitmapPlatformDevice::fixupAlphaBeforeCompositing() {
+  const SkBitmap& bitmap = accessBitmap(true);
+  SkAutoLockPixels lock(bitmap);
+  uint32_t* data = bitmap.getAddr32(0, 0);
+
+  size_t words = bitmap.rowBytes() / sizeof(uint32_t) * bitmap.height();
+  for (size_t i = 0; i < words; i++) {
+    if (data[i] == kMagicTransparencyColor)
+      data[i] = 0;
+    else
+      data[i] |= 0xFF000000;
+  }
+}
+
+// Returns the color value at the specified location.
+SkColor BitmapPlatformDevice::getColorAt(int x, int y) {
+  const SkBitmap& bitmap = accessBitmap(false);
+  SkAutoLockPixels lock(bitmap);
+  uint32_t* data = bitmap.getAddr32(0, 0);
+  return static_cast<SkColor>(data[x + y * width()]);
+}
+
+void BitmapPlatformDevice::onAccessBitmap(SkBitmap* bitmap) {
+  // FIXME(brettw) OPTIMIZATION: We should only flush if we know a GDI
+  // operation has occurred on our DC.
+  if (data_->IsBitmapDCCreated())
+    GdiFlush();
+}
+
+template<BitmapPlatformDevice::adjustAlpha adjustor>
+void BitmapPlatformDevice::processPixels(int x,
+                                   int y,
+                                   int width,
+                                   int height) {
+  const SkBitmap& bitmap = accessBitmap(true);
+  DCHECK_EQ(bitmap.config(), SkBitmap::kARGB_8888_Config);
+  const SkMatrix& matrix = data_->transform();
+  int bitmap_start_x = SkScalarRound(matrix.getTranslateX()) + x;
+  int bitmap_start_y = SkScalarRound(matrix.getTranslateY()) + y;
+
+  if (Constrain(bitmap.width(), &bitmap_start_x, &width) &&
+      Constrain(bitmap.height(), &bitmap_start_y, &height)) {
+    SkAutoLockPixels lock(bitmap);
+    DCHECK_EQ(bitmap.rowBytes() % sizeof(uint32_t), 0u);
+    size_t row_words = bitmap.rowBytes() / sizeof(uint32_t);
+    // Set data to the first pixel to be modified.
+    uint32_t* data = bitmap.getAddr32(0, 0) + (bitmap_start_y * row_words) +
+                     bitmap_start_x;
+    for (int i = 0; i < height; i++) {
+      for (int j = 0; j < width; j++) {
+        adjustor(data + j);
+      }
+      data += row_words;
+    }
+  }
+}
+
+}  // namespace gfx
diff --git a/base/gfx/bitmap_platform_device.h b/base/gfx/bitmap_platform_device.h
new file mode 100644
index 0000000..481067a
--- /dev/null
+++ b/base/gfx/bitmap_platform_device.h
@@ -0,0 +1,135 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_GFX_BITMAP_PLATFORM_DEVICE_H__
+#define BASE_GFX_BITMAP_PLATFORM_DEVICE_H__
+
+#include "base/gfx/platform_device.h"
+#include "base/ref_counted.h"
+
+namespace gfx {
+
+// A device is basically a wrapper around SkBitmap that provides a surface for
+// SkCanvas to draw into. Our device provides a surface Windows can also write
+// to. BitmapPlatformDevice creates a bitmap using CreateDIBSection() in a
+// format that Skia supports and can then use this to draw ClearType into, etc.
+// This pixel data is provided to the bitmap that the device contains so that it
+// can be shared.
+//
+// The device owns the pixel data, when the device goes away, the pixel data
+// also becomes invalid. THIS IS DIFFERENT THAN NORMAL SKIA which uses
+// reference counting for the pixel data. In normal Skia, you could assign
+// another bitmap to this device's bitmap and everything will work properly.
+// For us, that other bitmap will become invalid as soon as the device becomes
+// invalid, which may lead to subtle bugs. Therefore, DO NOT ASSIGN THE
+// DEVICE'S PIXEL DATA TO ANOTHER BITMAP, make sure you copy instead.
+class BitmapPlatformDevice : public PlatformDevice {
+ public:
+  // Factory function. The screen DC is used to create the bitmap, and will not
+  // be stored beyond this function. is_opaque should be set if the caller
+  // knows the bitmap will be completely opaque and allows some optimizations.
+  //
+  // The shared_section parameter is optional (pass NULL for default behavior).
+  // If shared_section is non-null, then it must be a handle to a file-mapping
+  // object returned by CreateFileMapping.  See CreateDIBSection for details.
+  static BitmapPlatformDevice* create(HDC screen_dc,
+                                int width,
+                                int height,
+                                bool is_opaque,
+                                HANDLE shared_section);
+
+  // Copy constructor. When copied, devices duplicate their internal data, so
+  // stay linked. This is because their implementation is very heavyweight
+  // (lots of memory and some GDI objects). If a device has been copied, both
+  // clip rects and other state will stay in sync.
+  //
+  // This means it will NOT work to duplicate a device and assign it to a
+  // canvas, because the two canvases will each set their own clip rects, and
+  // the resulting GDI clip rect will be random.
+  //
+  // Copy constucting and "=" is designed for saving the device or passing it
+  // around to another routine willing to deal with the bitmap data directly.
+  BitmapPlatformDevice(const BitmapPlatformDevice& other);
+  virtual ~BitmapPlatformDevice();
+
+  // See warning for copy constructor above.
+  BitmapPlatformDevice& operator=(const BitmapPlatformDevice& other);
+
+  // Retrieves the bitmap DC, which is the memory DC for our bitmap data. The
+  // bitmap DC is lazy created.
+  virtual HDC getBitmapDC();
+  virtual void setMatrixClip(const SkMatrix& transform, const SkRegion& region);
+  virtual void setDeviceOffset(int x, int y);
+
+  virtual void drawToHDC(HDC dc, int x, int y, const RECT* src_rect);
+  virtual void prepareForGDI(int x, int y, int width, int height);
+  virtual void postProcessGDI(int x, int y, int width, int height);
+  virtual void makeOpaque(int x, int y, int width, int height);
+  virtual void fixupAlphaBeforeCompositing();
+  virtual bool IsVectorial() { return false; }
+
+  // Returns the color value at the specified location. This does not
+  // consider any transforms that may be set on the device.
+  SkColor getColorAt(int x, int y);
+
+ protected:
+  // Flushes the Windows device context so that the pixel data can be accessed
+  // directly by Skia. Overridden from SkDevice, this is called when Skia
+  // starts accessing pixel data.
+  virtual void onAccessBitmap(SkBitmap* bitmap);
+
+ private:
+  // Function pointer used by the processPixels method for setting the alpha
+  // value of a particular pixel.
+  typedef void (*adjustAlpha)(uint32_t* pixel);
+
+  // Reference counted data that can be shared between multiple devices. This
+  // allows copy constructors and operator= for devices to work properly. The
+  // bitmaps used by the base device class are already refcounted and copyable.
+  class BitmapPlatformDeviceData;
+
+  // Private constructor.
+  BitmapPlatformDevice(BitmapPlatformDeviceData* data, const SkBitmap& bitmap);
+
+  // Loops through each of the pixels in the specified range, invoking
+  // adjustor for the alpha value of each pixel. If |width| or |height| are -1,
+  // the available width/height is used.
+  template<adjustAlpha adjustor>
+  void processPixels(int x,
+                     int y,
+                     int width,
+                     int height);
+
+  // Data associated with this device, guaranteed non-null.
+  scoped_refptr<BitmapPlatformDeviceData> data_;
+};
+
+}  // namespace gfx
+
+#endif  // BASE_GFX_BITMAP_PLATFORM_DEVICE_H__
diff --git a/base/gfx/convolver.cc b/base/gfx/convolver.cc
new file mode 100644
index 0000000..e56493e
--- /dev/null
+++ b/base/gfx/convolver.cc
@@ -0,0 +1,359 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <algorithm>
+
+#include "base/basictypes.h"
+#include "base/gfx/convolver.h"
+#include "base/logging.h"
+
+namespace gfx {
+
+namespace {
+
+// Converts the argument to an 8-bit unsigned value by clamping to the range
+// 0-255.
+inline uint8 ClampTo8(int32 a) {
+  if (static_cast<uint32>(a) < 256)
+    return a;  // Avoid the extra check in the common case.
+  if (a < 0)
+    return 0;
+  return 255;
+}
+
+// Stores a list of rows in a circular buffer. The usage is you write into it
+// by calling AdvanceRow. It will keep track of which row in the buffer it
+// should use next, and the total number of rows added.
+class CircularRowBuffer {
+ public:
+  // The number of pixels in each row is given in |source_row_pixel_width|.
+  // The maximum number of rows needed in the buffer is |max_y_filter_size|
+  // (we only need to store enough rows for the biggest filter).
+  //
+  // We use the |first_input_row| to compute the coordinates of all of the
+  // following rows returned by Advance().
+  CircularRowBuffer(int dest_row_pixel_width, int max_y_filter_size,
+                    int first_input_row)
+      : row_byte_width_(dest_row_pixel_width * 4),
+        num_rows_(max_y_filter_size),
+        next_row_(0),
+        next_row_coordinate_(first_input_row) {
+    buffer_.resize(row_byte_width_ * max_y_filter_size);
+    row_addresses_.resize(num_rows_);
+  }
+
+  // Moves to the next row in the buffer, returning a pointer to the beginning
+  // of it.
+  uint8* AdvanceRow() {
+    uint8* row = &buffer_[next_row_ * row_byte_width_];
+    next_row_coordinate_++;
+
+    // Set the pointer to the next row to use, wrapping around if necessary.
+    next_row_++;
+    if (next_row_ == num_rows_)
+      next_row_ = 0;
+    return row;
+  }
+
+  // Returns a pointer to an "unrolled" array of rows. These rows will start
+  // at the y coordinate placed into |*first_row_index| and will continue in
+  // order for the maximum number of rows in this circular buffer.
+  //
+  // The |first_row_index_| may be negative. This means the circular buffer
+  // starts before the top of the image (it hasn't been filled yet).
+  uint8* const* GetRowAddresses(int* first_row_index) {
+    // Example for a 4-element circular buffer holding coords 6-9.
+    //   Row 0   Coord 8
+    //   Row 1   Coord 9
+    //   Row 2   Coord 6  <- next_row_ = 2, next_row_coordinate_ = 10.
+    //   Row 3   Coord 7
+    //
+    // The "next" row is also the first (lowest) coordinate. This computation
+    // may yield a negative value, but that's OK, the math will work out
+    // since the user of this buffer will compute the offset relative
+    // to the first_row_index and the negative rows will never be used.
+    *first_row_index = next_row_coordinate_ - num_rows_;
+
+    int cur_row = next_row_;
+    for (int i = 0; i < num_rows_; i++) {
+      row_addresses_[i] = &buffer_[cur_row * row_byte_width_];
+
+      // Advance to the next row, wrapping if nexessary.
+      cur_row++;
+      if (cur_row == num_rows_)
+        cur_row = 0;
+    }
+    return &row_addresses_[0];
+  }
+
+ private:
+  // The buffer storing the rows. They are packed, each one row_byte_width_.
+  std::vector<uint8> buffer_;
+
+  // Number of bytes per row in the |buffer_|.
+  int row_byte_width_;
+
+  // The number of rows available in the buffer.
+  int num_rows_;
+
+  // The next row index we should write into. This wraps around as the
+  // circular buffer is used.
+  int next_row_;
+
+  // The y coordinate of the |next_row_|. This is incremented each time a
+  // new row is appended and does not wrap.
+  int next_row_coordinate_;
+
+  // Buffer used by GetRowAddresses().
+  std::vector<uint8*> row_addresses_;
+};
+
+// Convolves horizontally along a single row. The row data is given in
+// |src_data| and continues for the num_values() of the filter.
+template<bool has_alpha>
+void ConvolveHorizontally(const uint8* src_data,
+                          const ConvolusionFilter1D& filter,
+                          unsigned char* out_row) {
+  // Loop over each pixel on this row in the output image.
+  int num_values = filter.num_values();
+  for (int out_x = 0; out_x < num_values; out_x++) {
+    // Get the filter that determines the current output pixel.
+    int filter_offset, filter_length;
+    const int16* filter_values =
+        filter.FilterForValue(out_x, &filter_offset, &filter_length);
+
+    // Compute the first pixel in this row that the filter affects. It will
+    // touch |filter_length| pixels (4 bytes each) after this.
+    const uint8* row_to_filter = &src_data[filter_offset * 4];
+
+    // Apply the filter to the row to get the destination pixel in |accum|.
+    int32 accum[4] = {0};
+    for (int filter_x = 0; filter_x < filter_length; filter_x++) {
+      int16 cur_filter = filter_values[filter_x];
+      accum[0] += cur_filter * row_to_filter[filter_x * 4 + 0];
+      accum[1] += cur_filter * row_to_filter[filter_x * 4 + 1];
+      accum[2] += cur_filter * row_to_filter[filter_x * 4 + 2];
+      if (has_alpha)
+        accum[3] += cur_filter * row_to_filter[filter_x * 4 + 3];
+    }
+
+    // Bring this value back in range. All of the filter scaling factors
+    // are in fixed point with kShiftBits bits of fractional part.
+    accum[0] >>= ConvolusionFilter1D::kShiftBits;
+    accum[1] >>= ConvolusionFilter1D::kShiftBits;
+    accum[2] >>= ConvolusionFilter1D::kShiftBits;
+    if (has_alpha)
+      accum[3] >>= ConvolusionFilter1D::kShiftBits;
+
+    // Store the new pixel.
+    out_row[out_x * 4 + 0] = ClampTo8(accum[0]);
+    out_row[out_x * 4 + 1] = ClampTo8(accum[1]);
+    out_row[out_x * 4 + 2] = ClampTo8(accum[2]);
+    if (has_alpha)
+      out_row[out_x * 4 + 3] = ClampTo8(accum[3]);
+  }
+}
+
+// Does vertical convolusion to produce one output row. The filter values and
+// length are given in the first two parameters. These are applied to each
+// of the rows pointed to in the |source_data_rows| array, with each row
+// being |pixel_width| wide.
+//
+// The output must have room for |pixel_width * 4| bytes.
+template<bool has_alpha>
+void ConvolveVertically(const int16* filter_values,
+                        int filter_length,
+                        uint8* const* source_data_rows,
+                        int pixel_width,
+                        uint8* out_row) {
+  // We go through each column in the output and do a vertical convolusion,
+  // generating one output pixel each time.
+  for (int out_x = 0; out_x < pixel_width; out_x++) {
+    // Compute the number of bytes over in each row that the current column
+    // we're convolving starts at. The pixel will cover the next 4 bytes.
+    int byte_offset = out_x * 4;
+
+    // Apply the filter to one column of pixels.
+    int32 accum[4] = {0};
+    for (int filter_y = 0; filter_y < filter_length; filter_y++) {
+      int16 cur_filter = filter_values[filter_y];
+      accum[0] += cur_filter * source_data_rows[filter_y][byte_offset + 0];
+      accum[1] += cur_filter * source_data_rows[filter_y][byte_offset + 1];
+      accum[2] += cur_filter * source_data_rows[filter_y][byte_offset + 2];
+      if (has_alpha)
+        accum[3] += cur_filter * source_data_rows[filter_y][byte_offset + 3];
+    }
+
+    // Bring this value back in range. All of the filter scaling factors
+    // are in fixed point with kShiftBits bits of precision.
+    accum[0] >>= ConvolusionFilter1D::kShiftBits;
+    accum[1] >>= ConvolusionFilter1D::kShiftBits;
+    accum[2] >>= ConvolusionFilter1D::kShiftBits;
+    if (has_alpha)
+      accum[3] >>= ConvolusionFilter1D::kShiftBits;
+
+    // Store the new pixel.
+    out_row[byte_offset + 0] = ClampTo8(accum[0]);
+    out_row[byte_offset + 1] = ClampTo8(accum[1]);
+    out_row[byte_offset + 2] = ClampTo8(accum[2]);
+    if (has_alpha) {
+      uint8 alpha = ClampTo8(accum[3]);
+
+      // Make sure the alpha channel doesn't come out larger than any of the
+      // color channels. We use premultipled alpha channels, so this should
+      // never happen, but rounding errors will cause this from time to time.
+      // These "impossible" colors will cause overflows (and hence random pixel
+      // values) when the resulting bitmap is drawn to the screen.
+      //
+      // We only need to do this when generating the final output row (here).
+      int max_color_channel = std::max(out_row[byte_offset + 0],
+          std::max(out_row[byte_offset + 1], out_row[byte_offset + 2]));
+      if (alpha < max_color_channel)
+        out_row[byte_offset + 3] = max_color_channel;
+      else
+        out_row[byte_offset + 3] = alpha;
+    } else {
+      // No alpha channel, the image is opqaue.
+      out_row[byte_offset + 3] = 0xff;
+    }
+  }
+}
+
+}  // namespace
+
+// ConvolusionFilter1D ---------------------------------------------------------
+
+void ConvolusionFilter1D::AddFilter(int filter_offset,
+                                    const float* filter_values,
+                                    int filter_length) {
+  FilterInstance instance;
+  instance.data_location = static_cast<int>(filter_values_.size());
+  instance.offset = filter_offset;
+  instance.length = filter_length;
+  filters_.push_back(instance);
+
+  DCHECK(filter_length > 0);
+  for (int i = 0; i < filter_length; i++)
+    filter_values_.push_back(FloatToFixed(filter_values[i]));
+
+  max_filter_ = std::max(max_filter_, filter_length);
+}
+
+void ConvolusionFilter1D::AddFilter(int filter_offset,
+                                    const int16* filter_values,
+                                    int filter_length) {
+  FilterInstance instance;
+  instance.data_location = static_cast<int>(filter_values_.size());
+  instance.offset = filter_offset;
+  instance.length = filter_length;
+  filters_.push_back(instance);
+
+  DCHECK(filter_length > 0);
+  for (int i = 0; i < filter_length; i++)
+    filter_values_.push_back(filter_values[i]);
+
+  max_filter_ = std::max(max_filter_, filter_length);
+}
+
+// BGRAConvolve2D -------------------------------------------------------------
+
+void BGRAConvolve2D(const uint8* source_data,
+                    int source_byte_row_stride,
+                    bool source_has_alpha,
+                    const ConvolusionFilter1D& filter_x,
+                    const ConvolusionFilter1D& filter_y,
+                    uint8* output) {
+  int max_y_filter_size = filter_y.max_filter();
+
+  // The next row in the input that we will generate a horizontally
+  // convolved row for. If the filter doesn't start at the beginning of the
+  // image (this is the case when we are only resizing a subset), then we
+  // don't want to generate any output rows before that. Compute the starting
+  // row for convolusion as the first pixel for the first vertical filter.
+  int filter_offset, filter_length;
+  const int16* filter_values =
+      filter_y.FilterForValue(0, &filter_offset, &filter_length);
+  int next_x_row = filter_offset;
+
+  // We loop over each row in the input doing a horizontal convolusion. This
+  // will result in a horizontally convolved image. We write the results into
+  // a circular buffer of convolved rows and do vertical convolusion as rows
+  // are available. This prevents us from having to store the entire
+  // intermediate image and helps cache coherency.
+  CircularRowBuffer row_buffer(filter_x.num_values(), max_y_filter_size,
+                               filter_offset);
+
+  // Loop over every possible output row, processing just enough horizontal
+  // convolusions to run each subsequent vertical convolusion.
+  int output_row_byte_width = filter_x.num_values() * 4;
+  int num_output_rows = filter_y.num_values();
+  for (int out_y = 0; out_y < num_output_rows; out_y++) {
+    filter_values = filter_y.FilterForValue(out_y,
+                                            &filter_offset, &filter_length);
+
+    // Generate output rows until we have enough to run the current filter.
+    while (next_x_row < filter_offset + filter_length) {
+      if (source_has_alpha) {
+        ConvolveHorizontally<true>(
+            &source_data[next_x_row * source_byte_row_stride],
+            filter_x, row_buffer.AdvanceRow());
+      } else {
+        ConvolveHorizontally<false>(
+            &source_data[next_x_row * source_byte_row_stride],
+            filter_x, row_buffer.AdvanceRow());
+      }
+      next_x_row++;
+    }
+
+    // Compute where in the output image this row of final data will go.
+    uint8* cur_output_row = &output[out_y * output_row_byte_width];
+
+    // Get the list of rows that the circular buffer has, in order.
+    int first_row_in_circular_buffer;
+    uint8* const* rows_to_convolve =
+        row_buffer.GetRowAddresses(&first_row_in_circular_buffer);
+
+    // Now compute the start of the subset of those rows that the filter
+    // needs.
+    uint8* const* first_row_for_filter =
+        &rows_to_convolve[filter_offset - first_row_in_circular_buffer];
+
+    if (source_has_alpha) {
+      ConvolveVertically<true>(filter_values, filter_length,
+                               first_row_for_filter,
+                               filter_x.num_values(), cur_output_row);
+    } else {
+      ConvolveVertically<false>(filter_values, filter_length,
+                                first_row_for_filter,
+                                filter_x.num_values(), cur_output_row);
+    }
+  }
+}
+
+}  // namespace gfx
\ No newline at end of file
diff --git a/base/gfx/convolver.h b/base/gfx/convolver.h
new file mode 100644
index 0000000..8a2310e
--- /dev/null
+++ b/base/gfx/convolver.h
@@ -0,0 +1,156 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_GFX_CONVOLVER_H__
+#define BASE_GFX_CONVOLVER_H__
+
+#include <vector>
+
+#include "base/basictypes.h"
+
+namespace gfx {
+
+// Represents a filter in one dimension. Each output pixel has one entry in this
+// object for the filter values contributing to it. You build up the filter
+// list by calling AddFilter for each output pixel (in order).
+//
+// We do 2-dimensional convolusion by first convolving each row by one
+// ConvolusionFilter1D, then convolving each column by another one.
+//
+// Entries are stored in fixed point, shifted left by kShiftBits.
+class ConvolusionFilter1D {
+ public:
+  // The number of bits that fixed point values are shifted by.
+  enum { kShiftBits = 14 };
+
+  ConvolusionFilter1D() : max_filter_(0) {
+  }
+
+  // Convert between floating point and our fixed point representation.
+  static inline int16 FloatToFixed(float f) {
+    return static_cast<int16>(f * (1 << kShiftBits));
+  }
+  static inline unsigned char FixedToChar(int16 x) {
+    return static_cast<unsigned char>(x >> kShiftBits);
+  }
+
+  // Returns the maximum pixel span of a filter.
+  int max_filter() const { return max_filter_; }
+
+  // Returns the number of filters in this filter. This is the dimension of the
+  // output image.
+  int num_values() const { return static_cast<int>(filters_.size()); }
+
+  // Appends the given list of scaling values for generating a given output
+  // pixel. |filter_offset| is the distance from the edge of the image to where
+  // the scaling factors start. The scaling factors apply to the source pixels
+  // starting from this position, and going for the next |filter_length| pixels.
+  //
+  // You will probably want to make sure your input is normalized (that is,
+  // all entries in |filter_values| sub to one) to prevent affecting the overall
+  // brighness of the image.
+  //
+  // The filter_length must be > 0.
+  //
+  // This version will automatically convert your input to fixed point.
+  void AddFilter(int filter_offset,
+                 const float* filter_values,
+                 int filter_length);
+
+  // Same as the above version, but the input is already fixed point.
+  void AddFilter(int filter_offset,
+                 const int16* filter_values,
+                 int filter_length);
+
+  // Retrieves a filter for the given |value_offset|, a position in the output
+  // image in the direction we're convolving. The offset and length of the
+  // filter values are put into the corresponding out arguments (see AddFilter
+  // above for what these mean), and a pointer to the first scaling factor is
+  // returned. There will be |filter_length| values in this array.
+  inline const int16* FilterForValue(int value_offset,
+                                     int* filter_offset,
+                                     int* filter_length) const {
+    const FilterInstance& filter = filters_[value_offset];
+    *filter_offset = filter.offset;
+    *filter_length = filter.length;
+    return &filter_values_[filter.data_location];
+  }
+
+ private:
+  struct FilterInstance {
+    // Offset within filter_values for this instance of the filter.
+    int data_location;
+
+    // Distance from the left of the filter to the center. IN PIXELS
+    int offset;
+
+    // Number of values in this filter instance.
+    int length;
+  };
+
+  // Stores the information for each filter added to this class.
+  std::vector<FilterInstance> filters_;
+
+  // We store all the filter values in this flat list, indexed by
+  // |FilterInstance.data_location| to avoid the mallocs required for storing
+  // each one separately.
+  std::vector<int16> filter_values_;
+
+  // The maximum size of any filter we've added.
+  int max_filter_;
+};
+
+// Does a two-dimensional convolusion on the given source image.
+//
+// It is assumed the source pixel offsets referenced in the input filters
+// reference only valid pixels, so the source image size is not required. Each
+// row of the source image starts |source_byte_row_stride| after the previous
+// one (this allows you to have rows with some padding at the end).
+//
+// The result will be put into the given output buffer. The destination image
+// size will be xfilter.num_values() * yfilter.num_values() pixels. It will be
+// in rows of exactly xfilter.num_values() * 4 bytes.
+//
+// |source_has_alpha| is a hint that allows us to avoid doing computations on
+// the alpha channel if the image is opaque. If you don't know, set this to
+// true and it will work properly, but setting this to false will be a few
+// percent faster if you know the image is opaque.
+//
+// The layout in memory is assumed to be 4-bytes per pixel in B-G-R-A order
+// (this is ARGB when loaded into 32-bit words on a little-endian machine).
+void BGRAConvolve2D(const uint8* source_data,
+                    int source_byte_row_stride,
+                    bool source_has_alpha,
+                    const ConvolusionFilter1D& xfilter,
+                    const ConvolusionFilter1D& yfilter,
+                    uint8* output);
+
+}  // namespace gfx
+
+#endif  // BASE_GFX_CONVOLVER_H__
diff --git a/base/gfx/convolver_unittest.cc b/base/gfx/convolver_unittest.cc
new file mode 100644
index 0000000..b9d210a
--- /dev/null
+++ b/base/gfx/convolver_unittest.cc
@@ -0,0 +1,151 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <string.h>
+#include <time.h>
+#include <vector>
+
+#include "base/gfx/convolver.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gfx {
+
+namespace {
+
+// Fills the given filter with impulse functions for the range 0->num_entries.
+  void FillImpulseFilter(int num_entries, ConvolusionFilter1D* filter) {
+  float one = 1.0f;
+  for (int i = 0; i < num_entries; i++)
+    filter->AddFilter(i, &one, 1);
+}
+
+// Filters the given input with the impulse function, and verifies that it
+// does not change.
+void TestImpulseConvolusion(const unsigned char* data, int width, int height) {
+  int byte_count = width * height * 4;
+
+  ConvolusionFilter1D filter_x;
+  FillImpulseFilter(width, &filter_x);
+
+  ConvolusionFilter1D filter_y;
+  FillImpulseFilter(height, &filter_y);
+
+  std::vector<unsigned char> output;
+  output.resize(byte_count);
+  BGRAConvolve2D(data, width * 4, true, filter_x, filter_y, &output[0]);
+
+  // Output should exactly match input.
+  EXPECT_EQ(0, memcmp(data, &output[0], byte_count));
+}
+
+// Fills the destination filter with a box filter averaging every two pixels
+// to produce the output.
+void FillBoxFilter(int size, ConvolusionFilter1D* filter) {
+  const float box[2] = { 0.5, 0.5 };
+  for (int i = 0; i < size; i++)
+    filter->AddFilter(i * 2, box, 2);
+}
+
+}  // namespace
+
+// Tests that each pixel, when set and run through the impulse filter, does
+// not change.
+TEST(Convolver, Impulse) {
+  // We pick an "odd" size that is not likely to fit on any boundaries so that
+  // we can see if all the widths and paddings are handled properly.
+  int width = 15;
+  int height = 31;
+  int byte_count = width * height * 4;
+  std::vector<unsigned char> input;
+  input.resize(byte_count);
+
+  unsigned char* input_ptr = &input[0];
+  for (int y = 0; y < height; y++) {
+    for (int x = 0; x < width; x++) {
+      for (int channel = 0; channel < 3; channel++) {
+        memset(input_ptr, 0, byte_count);
+        input_ptr[(y * width + x) * 4 + channel] = 0xff;
+        // Always set the alpha channel or it will attempt to "fix" it for us.
+        input_ptr[(y * width + x) * 4 + 3] = 0xff;
+        TestImpulseConvolusion(input_ptr, width, height);
+      }
+    }
+  }
+}
+
+// Tests that using a box filter to halve an image results in every square of 4
+// pixels in the original get averaged to a pixel in the output.
+TEST(Convolver, Halve) {
+  static const int kSize = 16;
+
+  int src_width = kSize;
+  int src_height = kSize;
+  int src_row_stride = src_width * 4;
+  int src_byte_count = src_row_stride * src_height;
+  std::vector<unsigned char> input;
+  input.resize(src_byte_count);
+
+  int dest_width = src_width / 2;
+  int dest_height = src_height / 2;
+  int dest_byte_count = dest_width * dest_height * 4;
+  std::vector<unsigned char> output;
+  output.resize(dest_byte_count);
+
+  // First fill the array with a bunch of random data.
+  srand(static_cast<unsigned>(time(NULL)));
+  for (int i = 0; i < src_byte_count; i++)
+    input[i] = rand() * 255 / RAND_MAX;
+
+  // Compute the filters.
+  ConvolusionFilter1D filter_x, filter_y;
+  FillBoxFilter(dest_width, &filter_x);
+  FillBoxFilter(dest_height, &filter_y);
+
+  // Do the convolusion.
+  BGRAConvolve2D(&input[0], src_width, true, filter_x, filter_y, &output[0]);
+
+  // Compute the expected results and check, allowing for a small difference
+  // to account for rounding errors.
+  for (int y = 0; y < dest_height; y++) {
+    for (int x = 0; x < dest_width; x++) {
+      for (int channel = 0; channel < 4; channel++) {
+        int src_offset = (y * 2 * src_row_stride + x * 2 * 4) + channel;
+        int value = input[src_offset] +  // Top left source pixel.
+                    input[src_offset + 4] +  // Top right source pixel.
+                    input[src_offset + src_row_stride] +  // Lower left.
+                    input[src_offset + src_row_stride + 4];  // Lower right.
+        value /= 4;  // Average.
+        int difference = value - output[(y * dest_width + x) * 4 + channel];
+        EXPECT_TRUE(difference >= -1 || difference <= 1);
+      }
+    }
+  }
+}
+
+}  // namespace gfx
\ No newline at end of file
diff --git a/base/gfx/font_utils.cc b/base/gfx/font_utils.cc
new file mode 100644
index 0000000..340dbbc
--- /dev/null
+++ b/base/gfx/font_utils.cc
@@ -0,0 +1,305 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/gfx/font_utils.h"
+
+#include <limits>
+#include <map>
+
+#include "base/gfx/uniscribe.h"
+#include "base/logging.h"
+#include "base/singleton.h"
+#include "base/string_util.h"
+#include "unicode/locid.h"
+
+namespace gfx {
+
+namespace {
+
+// hash_map has extra cost with no sizable gain for a small number of integer
+// key items. When the map size becomes much bigger (which will be later as
+// more scripts are added) and this turns out to be prominent in the profile, we
+// may consider switching to hash_map (or just an array if we support all the
+// scripts)
+typedef std::map<UScriptCode, const wchar_t*> ScriptToFontMap;
+
+struct ScriptToFontMapSingletonTraits
+    : public DefaultSingletonTraits<ScriptToFontMap> {
+  static ScriptToFontMap* New() {
+    struct FontMap {
+      UScriptCode script;
+      const wchar_t* family;
+    };
+
+    const static FontMap font_map[] = {
+      {USCRIPT_SIMPLIFIED_HAN, L"simsun"},
+      {USCRIPT_TRADITIONAL_HAN, L"pmingliu"},
+      {USCRIPT_HIRAGANA, L"ms pgothic"},
+      {USCRIPT_KATAKANA, L"ms pgothic"},
+      {USCRIPT_KATAKANA_OR_HIRAGANA, L"ms pgothic"},
+      {USCRIPT_HANGUL, L"gulim"},
+      {USCRIPT_THAI, L"tahoma"},
+      {USCRIPT_HEBREW, L"david"},
+      {USCRIPT_ARABIC, L"simplified arabic"},
+      {USCRIPT_DEVANAGARI, L"mangal"},
+      {USCRIPT_BENGALI, L"vrinda"},
+      {USCRIPT_GURMUKHI, L"raavi"},
+      {USCRIPT_GUJARATI, L"shruti"},
+      {USCRIPT_ORIYA, L"kalinga"},
+      {USCRIPT_TAMIL, L"latha"},
+      {USCRIPT_TELUGU, L"gautami"},
+      {USCRIPT_KANNADA, L"tunga"},
+      {USCRIPT_MALAYALAM, L"kartika"},
+      {USCRIPT_LAO, L"dokchampa"},
+      {USCRIPT_TIBETAN, L"microsoft himalaya"},
+      {USCRIPT_GEORGIAN, L"sylfaen"},
+      {USCRIPT_ARMENIAN, L"sylfaen"},
+      {USCRIPT_ETHIOPIC, L"nyala"},
+      {USCRIPT_CANADIAN_ABORIGINAL, L"euphemia"},
+      {USCRIPT_CHEROKEE, L"plantagenet cherokee"},
+      {USCRIPT_YI, L"microsoft yi balti"},
+      {USCRIPT_SINHALA, L"iskoola pota"},
+      {USCRIPT_SYRIAC, L"estrangelo edessa"},
+      {USCRIPT_KHMER, L"daunpenh"},
+      {USCRIPT_THAANA, L"mv boli"},
+      {USCRIPT_MONGOLIAN, L"mongolian balti"},
+      // For common, perhaps we should return a font
+      // for the current application/system locale.
+      //{USCRIPT_COMMON, L"times new roman"}
+    };
+
+    ScriptToFontMap* new_instance = new ScriptToFontMap;
+    // Cannot recover from OOM so that there's no need to check.
+    for (int i = 0; i < arraysize(font_map); ++i)
+      (*new_instance)[font_map[i].script] = font_map[i].family;
+
+    // Initialize the locale-dependent mapping.
+    // Since Chrome synchronizes the ICU default locale with its UI locale,
+    // this ICU locale tells the current UI locale of Chrome.
+    Locale locale = Locale::getDefault();
+    ScriptToFontMap::const_iterator iter;
+    if (locale == Locale::getKorean()) {
+      iter = new_instance->find(USCRIPT_HANGUL);
+    } else if (locale == Locale::getJapanese()) {
+      iter = new_instance->find(USCRIPT_KATAKANA_OR_HIRAGANA);
+    } else if (locale == Locale::getTraditionalChinese()) {
+      iter = new_instance->find(USCRIPT_TRADITIONAL_HAN);
+    } else {
+      iter = new_instance->find(USCRIPT_SIMPLIFIED_HAN);
+    }
+    if (iter != new_instance->end())
+      (*new_instance)[USCRIPT_HAN] = iter->second;
+
+    return new_instance;
+  }
+};
+
+Singleton<ScriptToFontMap, ScriptToFontMapSingletonTraits> script_font_map;
+
+const int kUndefinedAscent = std::numeric_limits<int>::min();
+
+// Given an HFONT, return the ascent. If GetTextMetrics fails,
+// kUndefinedAscent is returned, instead.
+int GetAscent(HFONT hfont) {
+  HDC dc = GetDC(NULL);
+  HGDIOBJ oldFont = SelectObject(dc, hfont);
+  TEXTMETRIC tm;
+  BOOL got_metrics = GetTextMetrics(dc, &tm);
+  SelectObject(dc, oldFont);
+  ReleaseDC(NULL, dc);
+  return got_metrics ? tm.tmAscent : kUndefinedAscent;
+}
+
+struct FontData {
+  FontData() : hfont(NULL), ascent(kUndefinedAscent), script_cache(NULL) {}
+  HFONT hfont;
+  int ascent;
+  mutable SCRIPT_CACHE script_cache;
+};
+
+// Again, using hash_map does not earn us much here.
+// page_cycler_test intl2 gave us a 'better' result with map than with hash_map
+// even though they're well-within 1-sigma of each other so that the difference
+// is not significant. On the other hand, some pages in intl2 seem to
+// take longer to load with map in the 1st pass. Need to experiment further.
+typedef std::map<std::wstring, FontData*> FontDataCache;
+struct FontDataCacheSingletonTraits
+    : public DefaultSingletonTraits<FontDataCache> {
+  static void Delete(FontDataCache* cache) {
+    FontDataCache::iterator iter = cache->begin();
+    while (iter != cache->end()) {
+      SCRIPT_CACHE script_cache = iter->second->script_cache;
+      if (script_cache)
+        ScriptFreeCache(&script_cache);
+      delete iter->second;
+      ++iter;
+    }
+    delete cache;
+  }
+};
+
+}  // namespace
+
+// TODO(jungshik) : this is font fallback code version 0.1
+//  - Cover all the scripts
+//  - Get the default font for each script/generic family from the
+//    preference instead of hardcoding in the source.
+//  - Support generic families (from FontDescription)
+//  - If the default font for a script is not available,
+//    use EnumFontFamilies or similar APIs to come up with a list of
+//    fonts supporting the script and cache the result.
+//  - Consider using UnicodeSet (or UnicodeMap) to keep track of which
+//    character is supported by which font
+//  - Update script_font_cache in response to WM_FONTCHANGE
+
+const wchar_t* GetFontFamilyForScript(UScriptCode script,
+                                      GenericFamilyType generic) {
+  ScriptToFontMap::const_iterator iter = script_font_map->find(script);
+  const wchar_t* family = NULL;
+  if (iter != script_font_map->end()) {
+    family = iter->second;
+  }
+  return family;
+}
+
+// TODO(jungshik)
+//  - Handle 'Inherited', 'Common' and 'Unknown'
+//    (see http://www.unicode.org/reports/tr24/#Usage_Model )
+//    For 'Inherited' and 'Common', perhaps we need to
+//    accept another parameter indicating the previous family
+//    and just return it.
+//  - All the characters (or characters up to the point a single
+//    font can cover) need to be taken into account
+const wchar_t* GetFallbackFamily(const wchar_t* characters,
+                                 int length,
+                                 GenericFamilyType generic) {
+  DCHECK(characters && characters[0] && length > 0);
+  UScriptCode script = USCRIPT_COMMON;
+
+  // Sometimes characters common to script (e.g. space) is at
+  // the beginning of a string so that we need to skip them
+  // to get a font required to render the string.
+  int i = 0;
+  UChar32 ucs4 = 0;
+  while (i < length && script == USCRIPT_COMMON ||
+         script == USCRIPT_INVALID_CODE) {
+    U16_NEXT(characters, i, length, ucs4);
+    UErrorCode err = U_ZERO_ERROR;
+    script = uscript_getScript(ucs4, &err);
+    // silently ignore the error
+  }
+
+  // hack for full width ASCII. Japanese are most fond of full-width ASCII.
+  // TODO(jungshik) find a better way !
+  if (0xFF00 < ucs4 && ucs4 < 0xFF5F)
+    return L"ms pgothic";
+
+  const wchar_t* family = GetFontFamilyForScript(script, generic);
+  if (!family) {
+    int plane = ucs4 >> 16;
+    switch (plane) {
+      case 1:
+        family = L"code2001";
+        break;
+      case 2:
+        family = L"simsun ext b";
+        break;
+      default:
+        family = L"arial unicode ms";
+    }
+  }
+
+  return family;
+}
+
+
+
+// Be aware that this is not thread-safe.
+bool GetDerivedFontData(const wchar_t *family,
+                        int style,
+                        LOGFONT *logfont,
+                        int *ascent,
+                        HFONT *hfont,
+                        SCRIPT_CACHE **script_cache) {
+  DCHECK(logfont && family && *family);
+  // Using |Singleton| here is not free, but the intl2 page cycler test
+  // does not show any noticeable difference with and without it. Leaking
+  // the contents of FontDataCache (especially SCRIPT_CACHE) at the end
+  // of a renderer process may not be a good idea. We may use
+  // atexit(). However, with no noticeable performance difference, |Singleton|
+  // is cleaner, I believe.
+  FontDataCache* font_data_cache =
+      Singleton<FontDataCache, FontDataCacheSingletonTraits>::get();
+  // TODO(jungshik) : This comes up pretty high in the profile so that
+  // we need to measure whether using SHA256 (after coercing all the
+  // fields to char*) is faster than StringPrintf.
+  std::wstring font_key = StringPrintf(L"%1d:%d:%s", style, logfont->lfHeight,
+                                       family);
+  FontDataCache::const_iterator iter = font_data_cache->find(font_key);
+  FontData *derived;
+  if (iter == font_data_cache->end()) {
+    DCHECK(wcslen(family) < LF_FACESIZE);
+    wcscpy_s(logfont->lfFaceName, LF_FACESIZE, family);
+    // TODO(jungshik): CreateFontIndirect always comes up with
+    // a font even if there's no font matching the name. Need to
+    // check it against what we actually want (as is done in FontCacheWin.cpp)
+    derived = new FontData;
+    derived->hfont = CreateFontIndirect(logfont);
+    // GetAscent may return kUndefinedAscent, but we still want to
+    // cache it so that we won't have to call CreateFontIndirect once
+    // more for HFONT next time.
+    derived->ascent = GetAscent(derived->hfont);
+    (*font_data_cache)[font_key] = derived;
+  } else {
+    derived = iter->second;
+    // Last time, GetAscent failed so that only HFONT was
+    // cached. Try once more assuming that TryPreloadFont
+    // was called by a caller between calls.
+    if (kUndefinedAscent == derived->ascent)
+      derived->ascent = GetAscent(derived->hfont);
+  }
+  *hfont = derived->hfont;
+  *ascent = derived->ascent;
+  *script_cache = &(derived->script_cache);
+  return *ascent != kUndefinedAscent;
+}
+
+int GetStyleFromLogfont(const LOGFONT* logfont) {
+  // TODO(jungshik) : consider defining UNDEFINED or INVALID for style and
+  //                  returning it when logfont is NULL
+  if (!logfont) {
+    NOTREACHED();
+    return FONT_STYLE_NORMAL;
+  }
+  return (logfont->lfItalic ? FONT_STYLE_ITALIC : FONT_STYLE_NORMAL) |
+         (logfont->lfUnderline ? FONT_STYLE_UNDERLINED : FONT_STYLE_NORMAL) |
+         (logfont->lfWeight >= 700 ? FONT_STYLE_BOLD : FONT_STYLE_NORMAL);
+}
+
+}  // namespace gfx
diff --git a/base/gfx/font_utils.h b/base/gfx/font_utils.h
new file mode 100644
index 0000000..e06b588
--- /dev/null
+++ b/base/gfx/font_utils.h
@@ -0,0 +1,105 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// A collection of utilities for font handling.
+
+#ifndef BASE_GFX_FONT_UTILS_H__
+#define BASE_GFX_FONT_UTILS_H__
+
+#include <usp10.h>
+#include <wchar.h>
+#include <windows.h>
+
+#include <unicode/uscript.h>
+
+namespace gfx {
+
+// The order of family types needs to be exactly the same as
+// WebCore::FontDescription::GenericFamilyType. We may lift that restriction
+// when we make webkit_glue::WebkitGenericToChromeGenericFamily more
+// intelligent.
+enum GenericFamilyType {
+  GENERIC_FAMILY_NONE = 0,
+  GENERIC_FAMILY_STANDARD,
+  GENERIC_FAMILY_SERIF,
+  GENERIC_FAMILY_SANSSERIF,
+  GENERIC_FAMILY_MONOSPACE,
+  GENERIC_FAMILY_CURSIVE,
+  GENERIC_FAMILY_FANTASY
+};
+
+// Return a font family that supports a script and belongs to |generic| font family.
+// It can retun NULL and a caller has to implement its own fallback.
+const wchar_t* GetFontFamilyForScript(UScriptCode script,
+                                      GenericFamilyType generic);
+
+// Return a font family that can render |characters| based on
+// what script characters belong to.
+const wchar_t* GetFallbackFamily(const wchar_t *characters, int length,
+                                 GenericFamilyType generic);
+
+// Derive a new HFONT by replacing lfFaceName of LOGFONT with |family|,
+// calculate the ascent for the derived HFONT, and initialize SCRIPT_CACHE
+// in FontData.
+// |style| is only used for cache key generation. |style| is
+// bit-wise OR of BOLD(1), UNDERLINED(2) and ITALIC(4) and
+// should match what's contained in LOGFONT. It should be calculated
+// by calling GetStyleFromLogFont.
+// Returns false if the font is not accessible, in which case |ascent| field
+// of |fontdata| is set to kUndefinedAscent.
+// Be aware that this is not thread-safe.
+// TODO(jungshik): Instead of having three out params, we'd better have one
+// (|*FontData|), but somehow it mysteriously messes up the layout for
+// certain complex script pages (e.g. hi.wikipedia.org) and also crashes
+// at the start-up if recently visited page list includes pages with complex
+// scripts in their title. Moreover, somehow the very first-pass of
+// intl2 page-cycler test is noticeably slower with one out param than
+// the current version although the subsequent 9 passes take about the
+// same time.
+bool GetDerivedFontData(const wchar_t *family,
+                        int style,
+                        LOGFONT *logfont,
+                        int *ascent,
+                        HFONT *hfont,
+                        SCRIPT_CACHE **script_cache);
+
+enum {
+  FONT_STYLE_NORMAL = 0,
+  FONT_STYLE_BOLD = 1,
+  FONT_STYLE_ITALIC = 2,
+  FONT_STYLE_UNDERLINED = 4
+};
+
+// Derive style (bit-wise OR of FONT_STYLE_BOLD, FONT_STYLE_UNDERLINED, and
+// FONT_STYLE_ITALIC) from LOGFONT. Returns 0 if |*logfont| is NULL.
+int GetStyleFromLogfont(const LOGFONT *logfont);
+
+}  // namespace gfx
+
+#endif // BASE_GFX_FONT_UTILS_H__
diff --git a/base/gfx/image_operations.cc b/base/gfx/image_operations.cc
new file mode 100644
index 0000000..4dde08c
--- /dev/null
+++ b/base/gfx/image_operations.cc
@@ -0,0 +1,387 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+#define _USE_MATH_DEFINES
+#include <cmath>
+#include <limits>
+#include <vector>
+
+#include "base/gfx/image_operations.h"
+
+#include "base/gfx/convolver.h"
+#include "base/gfx/rect.h"
+#include "base/gfx/size.h"
+#include "base/logging.h"
+#include "base/stack_container.h"
+#include "SkBitmap.h"
+
+namespace gfx {
+
+namespace {
+
+// Returns the ceiling/floor as an integer.
+inline int CeilInt(float val) {
+  return static_cast<int>(ceil(val));
+}
+inline int FloorInt(float val) {
+  return static_cast<int>(floor(val));
+}
+
+// Filter function computation -------------------------------------------------
+
+// Evaluates the box filter, which goes from -0.5 to +0.5.
+float EvalBox(float x) {
+  return (x >= -0.5f && x < 0.5f) ? 1.0f : 0.0f;
+}
+
+// Evaluates the Lanczos filter of the given filter size window for the given
+// position.
+//
+// |filter_size| is the width of the filter (the "window"), outside of which
+// the value of the function is 0. Inside of the window, the value is the
+// normalized sinc function:
+//   lanczos(x) = sinc(x) * sinc(x / filter_size);
+// where
+//   sinc(x) = sin(pi*x) / (pi*x);
+float EvalLanczos(int filter_size, float x) {
+  if (x <= -filter_size || x >= filter_size)
+    return 0.0f;  // Outside of the window.
+  if (x > -std::numeric_limits<float>::epsilon() &&
+      x < std::numeric_limits<float>::epsilon())
+    return 1.0f;  // Special case the discontinuity at the origin.
+  float xpi = x * static_cast<float>(M_PI);
+  return (sin(xpi) / xpi) *  // sinc(x)
+          sin(xpi / filter_size) / (xpi / filter_size);  // sinc(x/filter_size)
+}
+
+// ResizeFilter ----------------------------------------------------------------
+
+// Encapsulates computation and storage of the filters required for one complete
+// resize operation.
+class ResizeFilter {
+ public:
+  ResizeFilter(ImageOperations::ResizeMethod method,
+               const Size& src_full_size,
+               const Size& dest_size,
+               const Rect& dest_subset);
+
+  // Returns the bounds in the input bitmap of data that is used in the output.
+  // The filter offsets are within this rectangle.
+  const Rect& src_depend() { return src_depend_; }
+
+  // Returns the filled filter values.
+  const ConvolusionFilter1D& x_filter() { return x_filter_; }
+  const ConvolusionFilter1D& y_filter() { return y_filter_; }
+
+ private:
+  // Returns the number of pixels that the filer spans, in filter space (the
+  // destination image).
+  float GetFilterSupport(float scale) {
+    switch (method_) {
+      case ImageOperations::RESIZE_BOX:
+        // The box filter just scales with the image scaling.
+        return 0.5f;  // Only want one side of the filter = /2.
+      case ImageOperations::RESIZE_LANCZOS3:
+        // The lanczos filter takes as much space in the source image in
+        // each direction as the size of the window = 3 for Lanczos3.
+        return 3.0f;
+      default:
+        NOTREACHED();
+        return 1.0f;
+    }
+  }
+
+  // Computes one set of filters either horizontally or vertically. The caller
+  // will specify the "min" and "max" rather than the bottom/top and
+  // right/bottom so that the same code can be re-used in each dimension.
+  //
+  // |src_depend_lo| and |src_depend_size| gives the range for the source
+  // depend rectangle (horizontally or vertically at the caller's discretion
+  // -- see above for what this means).
+  //
+  // Likewise, the range of destination values to compute and the scale factor
+  // for the transform is also specified.
+  void ComputeFilters(int src_size,
+                      int dest_subset_lo, int dest_subset_size,
+                      float scale, float src_support,
+                      ConvolusionFilter1D* output);
+
+  // Computes the filter value given the coordinate in filter space.
+  inline float ComputeFilter(float pos) {
+    switch (method_) {
+      case ImageOperations::RESIZE_BOX:
+        return EvalBox(pos);
+      case ImageOperations::RESIZE_LANCZOS3:
+        return EvalLanczos(3, pos);
+      default:
+        NOTREACHED();
+        return 0;
+    }
+  }
+
+  ImageOperations::ResizeMethod method_;
+
+  // Subset of source the filters will touch.
+  Rect src_depend_;
+
+  // Size of the filter support on one side only in the destination space.
+  // See GetFilterSupport.
+  float x_filter_support_;
+  float y_filter_support_;
+
+  // Subset of scaled destination bitmap to compute.
+  Rect out_bounds_;
+
+  ConvolusionFilter1D x_filter_;
+  ConvolusionFilter1D y_filter_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(ResizeFilter);
+};
+
+ResizeFilter::ResizeFilter(ImageOperations::ResizeMethod method,
+                           const Size& src_full_size,
+                           const Size& dest_size,
+                           const Rect& dest_subset)
+    : method_(method),
+      out_bounds_(dest_subset) {
+  float scale_x = static_cast<float>(dest_size.width()) /
+                  static_cast<float>(src_full_size.width());
+  float scale_y = static_cast<float>(dest_size.height()) /
+                  static_cast<float>(src_full_size.height());
+
+  x_filter_support_ = GetFilterSupport(scale_x);
+  y_filter_support_ = GetFilterSupport(scale_y);
+
+  gfx::Rect src_full(0, 0, src_full_size.width(), src_full_size.height());
+  gfx::Rect dest_full(0, 0,
+                      static_cast<int>(src_full_size.width() * scale_x + 0.5),
+                      static_cast<int>(src_full_size.height() * scale_y + 0.5));
+
+  // Support of the filter in source space.
+  float src_x_support = x_filter_support_ / scale_x;
+  float src_y_support = y_filter_support_ / scale_y;
+
+  ComputeFilters(src_full_size.width(), dest_subset.x(), dest_subset.width(),
+                 scale_x, src_x_support, &x_filter_);
+  ComputeFilters(src_full_size.height(), dest_subset.y(), dest_subset.height(),
+                 scale_y, src_y_support, &y_filter_);
+}
+
+void ResizeFilter::ComputeFilters(int src_size,
+                                  int dest_subset_lo, int dest_subset_size,
+                                  float scale, float src_support,
+                                  ConvolusionFilter1D* output) {
+  int dest_subset_hi = dest_subset_lo + dest_subset_size;  // [lo, hi)
+
+  // When we're doing a magnification, the scale will be larger than one. This
+  // means the destination pixels are much smaller than the source pixels, and
+  // that the range covered by the filter won't necessarily cover any source
+  // pixel boundaries. Therefore, we use these clamped values (max of 1) for
+  // some computations.
+  float clamped_src_support = std::min(1.0f, src_support);
+  float clamped_scale = std::min(1.0f, scale);
+
+  // Speed up the divisions below by turning them into multiplies.
+  float inv_scale = 1.0f / scale;
+
+  StackVector<float, 64> filter_values;
+  StackVector<int16, 64> fixed_filter_values;
+
+  // Loop over all pixels in the output range. We will generate one set of
+  // filter values for each one. Those values will tell us how to blend the
+  // source pixels to compute the destination pixel.
+  for (int dest_subset_i = dest_subset_lo; dest_subset_i < dest_subset_hi;
+       dest_subset_i++) {
+    // Reset the arrays. We don't declare them inside so they can re-use the
+    // same malloc-ed buffer.
+    filter_values->clear();
+    fixed_filter_values->clear();
+
+    // This is the pixel in the source directly under the pixel in the dest.
+    float src_pixel = dest_subset_i * inv_scale;
+
+    // Compute the (inclusive) range of source pixels the filter covers.
+    int src_begin = std::max(0, FloorInt(src_pixel - src_support));
+    int src_end = std::min(src_size - 1, CeilInt(src_pixel + src_support));
+
+    // Compute the unnormalized filter value at each location of the source
+    // it covers.
+    float filter_sum = 0.0f;  // Sub of the filter values for normalizing.
+    for (int cur_filter_pixel = src_begin; cur_filter_pixel <= src_end;
+         cur_filter_pixel++) {
+      // Distance from the center of the filter, this is the filter coordinate
+      // in source space.
+      float src_filter_pos = cur_filter_pixel - src_pixel;
+
+      // Since the filter really exists in dest space, map it there.
+      float dest_filter_pos = src_filter_pos * clamped_scale;
+
+      // Compute the filter value at that location.
+      float filter_value = ComputeFilter(dest_filter_pos);
+      filter_values->push_back(filter_value);
+
+      filter_sum += filter_value;
+    }
+    DCHECK(!filter_values->empty()) << "We should always get a filter!";
+
+    // The filter must be normalized so that we don't affect the brightness of
+    // the image. Convert to normalized fixed point.
+    int16 fixed_sum = 0;
+    for (size_t i = 0; i < filter_values->size(); i++) {
+      int16 cur_fixed = output->FloatToFixed(filter_values[i] / filter_sum);
+      fixed_sum += cur_fixed;
+      fixed_filter_values->push_back(cur_fixed);
+    }
+
+    // The conversion to fixed point will leave some rounding errors, which
+    // we add back in to avoid affecting the brightness of the image. We
+    // arbitrarily add this to the center of the filter array (this won't always
+    // be the center of the filter function since it could get clipped on the
+    // edges, but it doesn't matter enough to worry about that case).
+    int16 leftovers = output->FloatToFixed(1.0f) - fixed_sum;
+    fixed_filter_values[fixed_filter_values->size() / 2] += leftovers;
+
+    // Now it's ready to go.
+    output->AddFilter(src_begin, &fixed_filter_values[0],
+                      static_cast<int>(fixed_filter_values->size()));
+  }
+}
+
+}  // namespace
+
+// Resize ----------------------------------------------------------------------
+
+// static
+SkBitmap ImageOperations::Resize(const SkBitmap& source,
+                                 ResizeMethod method,
+                                 const Size& dest_size,
+                                 const Rect& dest_subset) {
+  DCHECK(Rect(dest_size.width(), dest_size.height()).Contains(dest_subset)) <<
+      "The supplied subset does not fall within the destination image.";
+
+  // If the size of source or destination is 0, i.e. 0x0, 0xN or Nx0, just
+  // return empty
+  if (source.width() < 1 || source.height() < 1 ||
+      dest_size.width() < 1 || dest_size.height() < 1)
+      return SkBitmap();
+
+  SkAutoLockPixels locker(source);
+
+  ResizeFilter filter(method, Size(source.width(), source.height()),
+                      dest_size, dest_subset);
+
+  // Get a source bitmap encompassing this touched area. We construct the
+  // offsets and row strides such that it looks like a new bitmap, while
+  // referring to the old data.
+  const uint8* source_subset =
+      reinterpret_cast<const uint8*>(source.getPixels());
+
+  // Convolve into the result.
+  SkBitmap result;
+  result.setConfig(SkBitmap::kARGB_8888_Config,
+                   dest_subset.width(), dest_subset.height());
+  result.allocPixels();
+  BGRAConvolve2D(source_subset, static_cast<int>(source.rowBytes()),
+                 !source.isOpaque(), filter.x_filter(), filter.y_filter(),
+                 static_cast<unsigned char*>(result.getPixels()));
+
+  // Preserve the "opaque" flag for use as an optimization later.
+  result.setIsOpaque(source.isOpaque());
+
+  return result;
+}
+
+// static
+SkBitmap ImageOperations::Resize(const SkBitmap& source,
+                                 ResizeMethod method,
+                                 const Size& dest_size) {
+  Rect dest_subset(0, 0, dest_size.width(), dest_size.height());
+  return Resize(source, method, dest_size, dest_subset);
+}
+
+// static
+SkBitmap ImageOperations::CreateBlendedBitmap(const SkBitmap& first,
+                                              const SkBitmap& second,
+                                              double alpha) {
+  DCHECK(alpha <= 1 && alpha >= 0);
+  DCHECK(first.width() == second.width());
+  DCHECK(first.height() == second.height());
+  DCHECK(first.bytesPerPixel() == second.bytesPerPixel());
+  DCHECK(first.config() == SkBitmap::kARGB_8888_Config);
+
+  // Optimize for case where we won't need to blend anything.
+  static const double alpha_min = 1.0 / 255;
+  static const double alpha_max = 254.0 / 255;
+  if (alpha < alpha_min) {
+    return first;
+  } else if (alpha > alpha_max) {
+    return second;
+  }
+
+  SkAutoLockPixels lock_first(first);
+  SkAutoLockPixels lock_second(second);
+
+  SkBitmap blended;
+  blended.setConfig(SkBitmap::kARGB_8888_Config, first.width(),
+                    first.height(), 0);
+  blended.allocPixels();
+  blended.eraseARGB(0, 0, 0, 0);
+
+  double first_alpha = 1 - alpha;
+
+  for (int y = 0; y < first.height(); y++) {
+    uint32* first_row = first.getAddr32(0, y);
+    uint32* second_row = second.getAddr32(0, y);
+    uint32* dst_row = blended.getAddr32(0, y);
+
+    for (int x = 0; x < first.width(); x++) {
+      uint32 first_pixel = first_row[x];
+      uint32 second_pixel = second_row[x];
+
+      int a = static_cast<int>(
+          SkColorGetA(first_pixel) * first_alpha +
+          SkColorGetA(second_pixel) * alpha);
+      int r = static_cast<int>(
+          SkColorGetR(first_pixel) * first_alpha +
+          SkColorGetR(second_pixel) * alpha);
+      int g = static_cast<int>(
+          SkColorGetG(first_pixel) * first_alpha +
+          SkColorGetG(second_pixel) * alpha);
+      int b = static_cast<int>(
+          SkColorGetB(first_pixel) * first_alpha +
+          SkColorGetB(second_pixel) * alpha);
+
+      dst_row[x] = SkColorSetARGB(a, r, g, b);
+    }
+  }
+
+  return blended;
+}
+
+}  // namespace gfx
diff --git a/base/gfx/image_operations.h b/base/gfx/image_operations.h
new file mode 100644
index 0000000..5d3eeef
--- /dev/null
+++ b/base/gfx/image_operations.h
@@ -0,0 +1,87 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_GFX_IMAGE_OPERATIONS_H__
+#define BASE_GFX_IMAGE_OPERATIONS_H__
+
+#include "base/basictypes.h"
+#include "base/gfx/rect.h"
+
+class SkBitmap;
+
+namespace gfx {
+
+class ImageOperations {
+ public:
+  enum ResizeMethod {
+    // Box filter. This is a weighted average of all of the pixels touching
+    // the destination pixel. For enlargement, this is nearest neighbor.
+    //
+    // You probably don't want this, it is here for testing since it is easy to
+    // compute. Use RESIZE_LANCZOS3 instead.
+    RESIZE_BOX,
+
+    // 3-cycle Lanczos filter. This is tall in the middle, goes negative on
+    // each side, then oscillates 2 more times. It gives nice sharp edges.
+    RESIZE_LANCZOS3,
+  };
+
+  // Resizes the given source bitmap using the specified resize method, so that
+  // the entire image is (dest_size) big. The dest_subset is the rectangle in
+  // this destination image that should actually be returned.
+  //
+  // The output image will be (dest_subset.width(), dest_subset.height()). This
+  // will save work if you do not need the entire bitmap.
+  //
+  // The destination subset must be smaller than the destination image.
+  static SkBitmap Resize(const SkBitmap& source,
+                         ResizeMethod method,
+                         const Size& dest_size,
+                         const Rect& dest_subset);
+
+  // Alternate version for resizing and returning the entire bitmap rather than
+  // a subset.
+  static SkBitmap Resize(const SkBitmap& source,
+                         ResizeMethod method,
+                         const Size& dest_size);
+
+
+  // Create a bitmap that is a blend of two others. The alpha argument
+  // specifies the opacity of the second bitmap. The provided bitmaps must
+  // use have the kARGB_8888_Config config and be of equal dimensions.
+  static SkBitmap CreateBlendedBitmap(const SkBitmap& first,
+                                      const SkBitmap& second,
+                                      double alpha);
+ private:
+  ImageOperations();  // Class for scoping only.
+};
+
+}  // namespace gfx
+
+#endif  // BASE_GFX_IMAGE_OPERATIONS_H__
diff --git a/base/gfx/image_operations_unittest.cc b/base/gfx/image_operations_unittest.cc
new file mode 100644
index 0000000..1c2b40f
--- /dev/null
+++ b/base/gfx/image_operations_unittest.cc
@@ -0,0 +1,172 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <stdlib.h>
+
+#include "base/gfx/image_operations.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "SkBitmap.h"
+
+namespace {
+
+// Computes the average pixel value for the given range, inclusive.
+uint32_t AveragePixel(const SkBitmap& bmp,
+                      int x_min, int x_max,
+                      int y_min, int y_max) {
+  float accum[4] = {0, 0, 0, 0};
+  int count = 0;
+  for (int y = y_min; y <= y_max; y++) {
+    for (int x = x_min; x <= x_max; x++) {
+      uint32_t cur = *bmp.getAddr32(x, y);
+      accum[0] += SkColorGetB(cur);
+      accum[1] += SkColorGetG(cur);
+      accum[2] += SkColorGetR(cur);
+      accum[3] += SkColorGetA(cur);
+      count++;
+    }
+  }
+
+  return SkColorSetARGB(static_cast<unsigned char>(accum[3] / count),
+                        static_cast<unsigned char>(accum[2] / count),
+                        static_cast<unsigned char>(accum[1] / count),
+                        static_cast<unsigned char>(accum[0] / count));
+}
+
+// Returns true if each channel of the given two colors are "close." This is
+// used for comparing colors where rounding errors may cause off-by-one.
+bool ColorsClose(uint32_t a, uint32_t b) {
+  return abs(static_cast<int>(SkColorGetB(a) - SkColorGetB(b))) < 2 &&
+         abs(static_cast<int>(SkColorGetG(a) - SkColorGetG(b))) < 2 &&
+         abs(static_cast<int>(SkColorGetR(a) - SkColorGetR(b))) < 2 &&
+         abs(static_cast<int>(SkColorGetA(a) - SkColorGetA(b))) < 2;
+}
+
+void FillDataToBitmap(int w, int h, SkBitmap* bmp) {
+  bmp->setConfig(SkBitmap::kARGB_8888_Config, w, h);
+  bmp->allocPixels();
+
+  unsigned char* src_data =
+      reinterpret_cast<unsigned char*>(bmp->getAddr32(0, 0));
+  for (int i = 0; i < w * h; i++) {
+    src_data[i * 4 + 0] = static_cast<unsigned char>(i % 255);
+    src_data[i * 4 + 1] = static_cast<unsigned char>(i % 255);
+    src_data[i * 4 + 2] = static_cast<unsigned char>(i % 255);
+    src_data[i * 4 + 3] = static_cast<unsigned char>(i % 255);
+  }
+}
+
+}  // namespace
+
+// Makes the bitmap 50% the size as the original using a box filter. This is
+// an easy operation that we can check the results for manually.
+TEST(ImageOperations, Halve) {
+  // Make our source bitmap.
+  int src_w = 30, src_h = 38;
+  SkBitmap src;
+  FillDataToBitmap(src_w, src_h, &src);
+
+  // Do a halving of the full bitmap.
+  SkBitmap actual_results = gfx::ImageOperations::Resize(
+      src, gfx::ImageOperations::RESIZE_BOX, gfx::Size(src_w / 2, src_h / 2));
+  ASSERT_EQ(src_w / 2, actual_results.width());
+  ASSERT_EQ(src_h / 2, actual_results.height());
+
+  // Compute the expected values & compare.
+  SkAutoLockPixels lock(actual_results);
+  for (int y = 0; y < actual_results.height(); y++) {
+    for (int x = 0; x < actual_results.width(); x++) {
+      int first_x = std::max(0, x * 2 - 1);
+      int last_x = std::min(src_w - 1, x * 2);
+
+      int first_y = std::max(0, y * 2 - 1);
+      int last_y = std::min(src_h - 1, y * 2);
+
+      uint32_t expected_color = AveragePixel(src,
+                                             first_x, last_x, first_y, last_y);
+      EXPECT_TRUE(ColorsClose(expected_color, *actual_results.getAddr32(x, y)));
+    }
+  }
+}
+
+TEST(ImageOperations, HalveSubset) {
+  // Make our source bitmap.
+  int src_w = 16, src_h = 34;
+  SkBitmap src;
+  FillDataToBitmap(src_w, src_h, &src);
+
+  // Do a halving of the full bitmap.
+  SkBitmap full_results = gfx::ImageOperations::Resize(
+      src, gfx::ImageOperations::RESIZE_BOX, gfx::Size(src_w / 2, src_h / 2));
+  ASSERT_EQ(src_w / 2, full_results.width());
+  ASSERT_EQ(src_h / 2, full_results.height());
+
+  // Now do a halving of a a subset, recall the destination subset is in the
+  // destination coordinate system (max = half of the original image size).
+  gfx::Rect subset_rect(2, 3, 3, 6);
+  SkBitmap subset_results = gfx::ImageOperations::Resize(
+      src, gfx::ImageOperations::RESIZE_BOX,
+      gfx::Size(src_w / 2, src_h / 2), subset_rect);
+  ASSERT_EQ(subset_rect.width(), subset_results.width());
+  ASSERT_EQ(subset_rect.height(), subset_results.height());
+
+  // The computed subset and the corresponding subset of the original image
+  // should be the same.
+  SkAutoLockPixels full_lock(full_results);
+  SkAutoLockPixels subset_lock(subset_results);
+  for (int y = 0; y < subset_rect.height(); y++) {
+    for (int x = 0; x < subset_rect.width(); x++) {
+      ASSERT_EQ(
+          *full_results.getAddr32(x + subset_rect.x(), y + subset_rect.y()),
+          *subset_results.getAddr32(x, y));
+    }
+  }
+}
+
+// Resamples an iamge to the same image, it should give almost the same result.
+TEST(ImageOperations, ResampleToSame) {
+  // Make our source bitmap.
+  int src_w = 16, src_h = 34;
+  SkBitmap src;
+  FillDataToBitmap(src_w, src_h, &src);
+
+  // Do a resize of the full bitmap to the same size. The lanczos filter is good
+  // enough that we should get exactly the same image for output.
+  SkBitmap results = gfx::ImageOperations::Resize(
+      src, gfx::ImageOperations::RESIZE_LANCZOS3, gfx::Size(src_w, src_h));
+  ASSERT_EQ(src_w, results.width());
+  ASSERT_EQ(src_h, results.height());
+
+  SkAutoLockPixels src_lock(src);
+  SkAutoLockPixels results_lock(results);
+  for (int y = 0; y < src_h; y++) {
+    for (int x = 0; x < src_w; x++) {
+      EXPECT_EQ(*src.getAddr32(x, y), *results.getAddr32(x, y));
+    }
+  }
+}
\ No newline at end of file
diff --git a/base/gfx/img_resize_perftest.cc b/base/gfx/img_resize_perftest.cc
new file mode 100644
index 0000000..43f8deb
--- /dev/null
+++ b/base/gfx/img_resize_perftest.cc
@@ -0,0 +1,94 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <stdlib.h>
+#include <time.h>
+
+#include "base/perftimer.h"
+#include "base/gfx/convolver.h"
+#include "base/gfx/image_operations.h"
+#include "base/gfx/image_resizer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+void FillRandomData(char* dest, int byte_count) {
+  srand(static_cast<unsigned>(time(NULL)));
+  for (int i = 0; i < byte_count; i++)
+    dest[i] = rand() * 255 / RAND_MAX;
+}
+
+}  // namespace
+
+// Old code gives [1521, 1519]ms for this, 4000x4000 -> 2100x2100 lanczos8
+
+TEST(ImageResizePerf, BigFilter) {
+  static const int kSrcWidth = 4000;
+  static const int kSrcHeight = 4000;
+  static const int kSrcByteSize = kSrcWidth * kSrcHeight * 4;
+
+  SkBitmap src_bmp;
+  src_bmp.setConfig(SkBitmap::kARGB_8888_Config, kSrcWidth, kSrcHeight);
+  src_bmp.allocPixels();
+  FillRandomData(reinterpret_cast<char*>(src_bmp.getAddr32(0, 0)),
+                 kSrcByteSize);
+
+  // Make the dest size > 1/2 so the 50% optimization doesn't kick in.
+  static const int kDestWidth = 1400;
+  static const int kDestHeight = 1400;
+
+  PerfTimeLogger resize_timer("resize");
+  gfx::ImageResizer resizer(gfx::ImageResizer::LANCZOS3);
+  SkBitmap dest = resizer.Resize(src_bmp, kDestWidth, kDestHeight);
+}
+
+// The original image filter we were using took 523ms for this test, while this
+// one takes 857ms.
+// TODO(brettw) make this at least 64% faster.
+TEST(ImageOperationPerf, BigFilter) {
+  static const int kSrcWidth = 4000;
+  static const int kSrcHeight = 4000;
+  static const int kSrcByteSize = kSrcWidth * kSrcHeight * 4;
+
+  SkBitmap src_bmp;
+  src_bmp.setConfig(SkBitmap::kARGB_8888_Config, kSrcWidth, kSrcHeight);
+  src_bmp.allocPixels();
+  src_bmp.setIsOpaque(true);
+  FillRandomData(reinterpret_cast<char*>(src_bmp.getAddr32(0, 0)),
+                 kSrcByteSize);
+
+  // Make the dest size > 1/2 so the 50% optimization doesn't kick in.
+  static const int kDestWidth = 1400;
+  static const int kDestHeight = 1400;
+
+  PerfTimeLogger resize_timer("resize");
+  SkBitmap dest = gfx::ImageOperations::Resize(src_bmp,
+      gfx::ImageOperations::RESIZE_LANCZOS3, (float)kDestWidth / (float)kSrcWidth,
+      (float)kDestHeight / (float)kSrcHeight);
+}
diff --git a/base/gfx/native_theme.cc b/base/gfx/native_theme.cc
new file mode 100644
index 0000000..8ab4ca3
--- /dev/null
+++ b/base/gfx/native_theme.cc
@@ -0,0 +1,626 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/gfx/native_theme.h"
+
+#include <windows.h>
+#include <uxtheme.h>
+#include <vsstyle.h>
+#include <vssym32.h>
+
+#include "base/gfx/bitmap_header.h"
+#include "base/gfx/platform_canvas.h"
+#include "base/gfx/skia_utils.h"
+#include "base/gfx/rect.h"
+#include "base/logging.h"
+#include "base/scoped_handle.h"
+#include "skia/include/SkShader.h"
+
+namespace gfx {
+
+/* static */
+const NativeTheme* NativeTheme::instance() {
+  // The global NativeTheme instance.
+  static const NativeTheme s_native_theme;
+  return &s_native_theme;
+}
+
+NativeTheme::NativeTheme()
+    : theme_dll_(LoadLibrary(L"uxtheme.dll")),
+      draw_theme_(NULL),
+      draw_theme_ex_(NULL),
+      get_theme_color_(NULL),
+      get_theme_content_rect_(NULL),
+      get_theme_part_size_(NULL),
+      open_theme_(NULL),
+      close_theme_(NULL),
+      set_theme_properties_(NULL),
+      is_theme_active_(NULL),
+      get_theme_int_(NULL) {
+  if (theme_dll_) {
+    draw_theme_ = reinterpret_cast<DrawThemeBackgroundPtr>(
+        GetProcAddress(theme_dll_, "DrawThemeBackground"));
+    draw_theme_ex_ = reinterpret_cast<DrawThemeBackgroundExPtr>(
+        GetProcAddress(theme_dll_, "DrawThemeBackgroundEx"));
+    get_theme_color_ = reinterpret_cast<GetThemeColorPtr>(
+        GetProcAddress(theme_dll_, "GetThemeColor"));
+    get_theme_content_rect_ = reinterpret_cast<GetThemeContentRectPtr>(
+        GetProcAddress(theme_dll_, "GetThemeBackgroundContentRect"));
+    get_theme_part_size_ = reinterpret_cast<GetThemePartSizePtr>(
+        GetProcAddress(theme_dll_, "GetThemePartSize"));
+    open_theme_ = reinterpret_cast<OpenThemeDataPtr>(
+        GetProcAddress(theme_dll_, "OpenThemeData"));
+    close_theme_ = reinterpret_cast<CloseThemeDataPtr>(
+        GetProcAddress(theme_dll_, "CloseThemeData"));
+    set_theme_properties_ = reinterpret_cast<SetThemeAppPropertiesPtr>(
+        GetProcAddress(theme_dll_, "SetThemeAppProperties"));
+    is_theme_active_ = reinterpret_cast<IsThemeActivePtr>(
+        GetProcAddress(theme_dll_, "IsThemeActive"));
+    get_theme_int_ = reinterpret_cast<GetThemeIntPtr>(
+        GetProcAddress(theme_dll_, "GetThemeInt"));
+  }
+  memset(theme_handles_, 0, sizeof(theme_handles_));
+}
+
+NativeTheme::~NativeTheme() {
+  if (theme_dll_) {
+    CloseHandles();
+    FreeLibrary(theme_dll_);
+  }
+}
+
+HRESULT NativeTheme::PaintButton(HDC hdc,
+                                 int part_id,
+                                 int state_id,
+                                 int classic_state,
+                                 RECT* rect) const {
+  HANDLE handle = GetThemeHandle(BUTTON);
+  if (handle && draw_theme_)
+    return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+
+  // Draw it manually.
+  // All pressed states have both low bits set, and no other states do.
+  const bool focused = ((state_id & PBS_PRESSED) == PBS_PRESSED);
+  if ((part_id == BP_PUSHBUTTON) && focused) {
+    // BP_PUSHBUTTON has a focus rect drawn around the outer edge, and the
+    // button itself is shrunk by 1 pixel.
+    HBRUSH brush = GetSysColorBrush(COLOR_3DDKSHADOW);
+    if (brush) {
+      FrameRect(hdc, rect, brush);
+      InflateRect(rect, -1, -1);
+    }
+  }
+
+  DrawFrameControl(hdc, rect, DFC_BUTTON, classic_state);
+
+  // BP_RADIOBUTTON, BP_CHECKBOX, BP_GROUPBOX and BP_USERBUTTON have their
+  // focus drawn over the control.
+  if ((part_id != BP_PUSHBUTTON) && focused)
+    DrawFocusRect(hdc, rect);
+
+  return S_OK;
+}
+
+HRESULT NativeTheme::PaintTextField(HDC hdc,
+                                    int part_id,
+                                    int state_id,
+                                    int classic_state,
+                                    RECT* rect,
+                                    COLORREF color,
+                                    bool fill_content_area,
+                                    bool draw_edges) const {
+  // TODO(ojan): http://b/1210017 Figure out how to give the ability to
+  // exclude individual edges from being drawn.
+
+  HANDLE handle = GetThemeHandle(TEXTFIELD);
+  // TODO(mpcomplete): can we detect if the color is specified by the user,
+  // and if not, just use the system color?
+  // CreateSolidBrush() accepts a RGB value but alpha must be 0.
+  HBRUSH bg_brush = CreateSolidBrush(color);
+  HRESULT hr;
+  // DrawThemeBackgroundEx was introduced in XP SP2, so that it's possible
+  // draw_theme_ex_ is NULL and draw_theme_ is non-null.
+  if (handle && (draw_theme_ex_ || (draw_theme_ && draw_edges))) {
+    if (draw_theme_ex_) {
+      static DTBGOPTS omit_border_options = {
+        sizeof(DTBGOPTS),
+        DTBG_OMITBORDER,
+        {0,0,0,0}
+      };
+      DTBGOPTS* draw_opts = draw_edges ? NULL : &omit_border_options;
+      hr = draw_theme_ex_(handle, hdc, part_id, state_id, rect, draw_opts);
+    } else {
+      hr = draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+    }
+
+    // TODO(maruel): Need to be fixed if get_theme_content_rect_ is NULL.
+    if (fill_content_area && get_theme_content_rect_) {
+      RECT content_rect;
+      hr = get_theme_content_rect_(handle, hdc, part_id, state_id, rect,
+                                   &content_rect);
+      FillRect(hdc, &content_rect, bg_brush);
+    }
+  } else {
+    // Draw it manually.
+    if (draw_edges)
+      DrawEdge(hdc, rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
+
+    if (fill_content_area) {
+      FillRect(hdc, rect, (classic_state & DFCS_INACTIVE) ?
+                   reinterpret_cast<HBRUSH>(COLOR_BTNFACE + 1) : bg_brush);
+    }
+    hr = S_OK;
+  }
+  DeleteObject(bg_brush);
+  return hr;
+}
+
+HRESULT NativeTheme::PaintMenuList(HDC hdc,
+                                   int part_id,
+                                   int state_id,
+                                   int classic_state,
+                                   RECT* rect) const {
+  HANDLE handle = GetThemeHandle(MENULIST);
+  if (handle && draw_theme_)
+    return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+
+  // Draw it manually.
+  DrawFrameControl(hdc, rect, DFC_SCROLL, DFCS_SCROLLCOMBOBOX | classic_state);
+  return S_OK;
+}
+
+HRESULT NativeTheme::PaintScrollbarArrow(HDC hdc,
+                                         int state_id,
+                                         int classic_state,
+                                         RECT* rect) const {
+  HANDLE handle = GetThemeHandle(SCROLLBAR);
+  if (handle && draw_theme_)
+    return draw_theme_(handle, hdc, SBP_ARROWBTN, state_id, rect, NULL);
+
+  // Draw it manually.
+  DrawFrameControl(hdc, rect, DFC_SCROLL, classic_state);
+  return S_OK;
+}
+
+HRESULT NativeTheme::PaintScrollbarTrack(HDC hdc,
+                                         int part_id,
+                                         int state_id,
+                                         int classic_state,
+                                         RECT* target_rect,
+                                         RECT* align_rect,
+                                         PlatformCanvas* canvas) const {
+  HANDLE handle = GetThemeHandle(SCROLLBAR);
+  if (handle && draw_theme_)
+    return draw_theme_(handle, hdc, part_id, state_id, target_rect, NULL);
+
+  // Draw it manually.
+  const DWORD colorScrollbar = GetSysColor(COLOR_SCROLLBAR);
+  const DWORD color3DFace = GetSysColor(COLOR_3DFACE);
+  if ((colorScrollbar != color3DFace) &&
+      (colorScrollbar != GetSysColor(COLOR_WINDOW))) {
+    FillRect(hdc, target_rect, reinterpret_cast<HBRUSH>(COLOR_SCROLLBAR + 1));
+  } else {
+    // Create a 2x2 checkerboard pattern using the 3D face and highlight
+    // colors.
+    SkColor face = COLORREFToSkColor(color3DFace);
+    SkColor highlight = COLORREFToSkColor(GetSysColor(COLOR_3DHILIGHT));
+    SkColor buffer[] = { face, highlight, highlight, face };
+    SkBitmap bitmap;
+    bitmap.setConfig(SkBitmap::kARGB_8888_Config, 2, 2);
+    bitmap.setPixels(buffer);
+    SkShader* shader = SkShader::CreateBitmapShader(bitmap,
+                                                    SkShader::kRepeat_TileMode,
+                                                    SkShader::kRepeat_TileMode);
+
+    // Draw that pattern into the target rect, setting the origin to the top
+    // left corner of the scrollbar track (so the checked rect below the thumb
+    // aligns properly with the portion above the thumb).
+    SkMatrix matrix;
+    matrix.setTranslate(SkIntToScalar(align_rect->left),
+                        SkIntToScalar(align_rect->top));
+    shader->setLocalMatrix(matrix);
+    SkPaint paint;
+    paint.setShader(shader)->unref();
+    canvas->drawIRect(RECTToSkIRect(*target_rect), paint);
+  }
+  if (classic_state & DFCS_PUSHED)
+    InvertRect(hdc, target_rect);
+  return S_OK;
+}
+
+HRESULT NativeTheme::PaintScrollbarThumb(HDC hdc,
+                                         int part_id,
+                                         int state_id,
+                                         int classic_state,
+                                         RECT* rect) const {
+  HANDLE handle = GetThemeHandle(SCROLLBAR);
+  if (handle && draw_theme_)
+    return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+
+  // Draw it manually.
+  if ((part_id == SBP_THUMBBTNHORZ) || (part_id == SBP_THUMBBTNVERT))
+    DrawEdge(hdc, rect, EDGE_RAISED, BF_RECT | BF_MIDDLE);
+  // Classic mode doesn't have a gripper.
+  return S_OK;
+}
+
+HRESULT NativeTheme::PaintStatusGripper(HDC hdc,
+                                        int part_id,
+                                        int state_id,
+                                        int classic_state,
+                                        RECT* rect) const {
+  HANDLE handle = GetThemeHandle(STATUS);
+  if (handle && draw_theme_) {
+    // Paint the status bar gripper.  There doesn't seem to be a
+    // standard gripper in Windows for the space between
+    // scrollbars.  This is pretty close, but it's supposed to be
+    // painted over a status bar.
+    return draw_theme_(handle, hdc, SP_GRIPPER, 0, rect, 0);
+  }
+
+  // Draw a windows classic scrollbar gripper.
+  DrawFrameControl(hdc, rect, DFC_SCROLL, DFCS_SCROLLSIZEGRIP);
+  return S_OK;
+}
+
+HRESULT NativeTheme::PaintDialogBackground(HDC hdc, bool active,
+                                           RECT* rect) const {
+  HANDLE handle = GetThemeHandle(WINDOW);
+  if (handle && draw_theme_) {
+    return draw_theme_(handle, hdc, WP_DIALOG,
+                       active ? FS_ACTIVE : FS_INACTIVE, rect, NULL);
+  }
+
+  // Classic just renders a flat color background.
+  FillRect(hdc, rect, reinterpret_cast<HBRUSH>(COLOR_3DFACE + 1));
+  return S_OK;
+}
+
+HRESULT NativeTheme::PaintTabPanelBackground(HDC hdc, RECT* rect) const {
+  HANDLE handle = GetThemeHandle(TAB);
+  if (handle && draw_theme_)
+    return draw_theme_(handle, hdc, TABP_BODY, 0, rect, NULL);
+
+  // Classic just renders a flat color background.
+  FillRect(hdc, rect, reinterpret_cast<HBRUSH>(COLOR_3DFACE + 1));
+  return S_OK;
+}
+
+HRESULT NativeTheme::PaintListBackground(HDC hdc,
+                                         bool enabled,
+                                         RECT* rect) const {
+  HANDLE handle = GetThemeHandle(LIST);
+  if (handle && draw_theme_)
+    return draw_theme_(handle, hdc, 1, TS_NORMAL, rect, NULL);
+
+  // Draw it manually.
+  HBRUSH bg_brush = GetSysColorBrush(COLOR_WINDOW);
+  FillRect(hdc, rect, bg_brush);
+  DrawEdge(hdc, rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
+  return S_OK;
+}
+
+bool NativeTheme::IsThemingActive() const {
+  if (is_theme_active_)
+    return !!is_theme_active_();
+  return false;
+}
+
+HRESULT NativeTheme::PaintMenuArrow(ThemeName theme,
+                                    HDC hdc,
+                                    int part_id,
+                                    int state_id,
+                                    RECT* rect,
+                                    MenuArrowDirection arrow_direction,
+                                    bool is_highlighted) const {
+  HANDLE handle = GetThemeHandle(MENU);
+  if (handle && draw_theme_) {
+    if (arrow_direction == RIGHT_POINTING_ARROW) {
+      return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+    } else {
+      // There is no way to tell the uxtheme API to draw a left pointing arrow;
+      // it doesn't have a flag equivalent to DFCS_MENUARROWRIGHT.  But they
+      // are needed for RTL locales on Vista.  So use a memory DC and mirror
+      // the region with GDI's StretchBlt.
+      Rect r(*rect);
+      ScopedHDC mem_dc(CreateCompatibleDC(hdc));
+      ScopedBitmap mem_bitmap(CreateCompatibleBitmap(hdc, r.width(),
+                                                     r.height()));
+      HGDIOBJ old_bitmap = SelectObject(mem_dc, mem_bitmap);
+      // Copy and horizontally mirror the background from hdc into mem_dc. Use
+      // a negative-width source rect, starting at the rightmost pixel.
+      StretchBlt(mem_dc, 0, 0, r.width(), r.height(),
+                 hdc, r.right()-1, r.y(), -r.width(), r.height(), SRCCOPY);
+      // Draw the arrow.
+      RECT theme_rect = {0, 0, r.width(), r.height()};
+      HRESULT result = draw_theme_(handle, mem_dc, part_id,
+                                   state_id, &theme_rect, NULL);
+      // Copy and mirror the result back into mem_dc.
+      StretchBlt(hdc, r.x(), r.y(), r.width(), r.height(),
+                 mem_dc, r.width()-1, 0, -r.width(), r.height(), SRCCOPY);
+      SelectObject(mem_dc, old_bitmap);
+      return result;
+    }
+  }
+
+  // For some reason, Windows uses the name DFCS_MENUARROWRIGHT to indicate a
+  // left pointing arrow. This makes the following 'if' statement slightly
+  // counterintuitive.
+  UINT state;
+  if (arrow_direction == RIGHT_POINTING_ARROW)
+    state = DFCS_MENUARROW;
+  else
+    state = DFCS_MENUARROWRIGHT;
+  return PaintFrameControl(hdc, rect, DFC_MENU, state, is_highlighted);
+}
+
+HRESULT NativeTheme::PaintMenuBackground(ThemeName theme,
+                                         HDC hdc,
+                                         int part_id,
+                                         int state_id,
+                                         RECT* rect) const {
+  HANDLE handle = GetThemeHandle(MENU);
+  if (handle && draw_theme_) {
+    HRESULT result = draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+    FrameRect(hdc, rect, GetSysColorBrush(COLOR_3DSHADOW));
+    return result;
+  }
+
+  FillRect(hdc, rect, GetSysColorBrush(COLOR_MENU));
+  DrawEdge(hdc, rect, EDGE_RAISED, BF_RECT);
+  return S_OK;
+}
+
+HRESULT NativeTheme::PaintMenuCheckBackground(ThemeName theme,
+                                              HDC hdc,
+                                              int part_id,
+                                              int state_id,
+                                              RECT* rect) const {
+  HANDLE handle = GetThemeHandle(MENU);
+  if (handle && draw_theme_)
+    return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+  // Nothing to do for background.
+  return S_OK;
+}
+
+HRESULT NativeTheme::PaintMenuCheck(ThemeName theme,
+                                    HDC hdc,
+                                    int part_id,
+                                    int state_id,
+                                    RECT* rect,
+                                    bool is_highlighted) const {
+  HANDLE handle = GetThemeHandle(MENU);
+  if (handle && draw_theme_) {
+    return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+  }
+  return PaintFrameControl(hdc, rect, DFC_MENU, DFCS_MENUCHECK, is_highlighted);
+}
+
+HRESULT NativeTheme::PaintMenuGutter(HDC hdc,
+                                     int part_id,
+                                     int state_id,
+                                     RECT* rect) const {
+  HANDLE handle = GetThemeHandle(MENU);
+  if (handle && draw_theme_)
+    return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+  return E_NOTIMPL;
+}
+
+HRESULT NativeTheme::PaintMenuSeparator(HDC hdc,
+                                        int part_id,
+                                        int state_id,
+                                        RECT* rect) const {
+  HANDLE handle = GetThemeHandle(MENU);
+  if (handle && draw_theme_)
+    return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+  DrawEdge(hdc, rect, EDGE_ETCHED, BF_TOP);
+  return S_OK;
+}
+
+HRESULT NativeTheme::PaintMenuItemBackground(ThemeName theme,
+                                             HDC hdc,
+                                             int part_id,
+                                             int state_id,
+                                             bool selected,
+                                             RECT* rect) const {
+  HANDLE handle = GetThemeHandle(MENU);
+  if (handle && draw_theme_)
+    return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+  if (selected)
+    FillRect(hdc, rect, GetSysColorBrush(COLOR_HIGHLIGHT));
+  return S_OK;
+}
+
+HRESULT NativeTheme::GetThemePartSize(ThemeName theme_name,
+                                      HDC hdc,
+                                      int part_id,
+                                      int state_id,
+                                      RECT* rect,
+                                      int ts,
+                                      SIZE* size) const {
+  HANDLE handle = GetThemeHandle(theme_name);
+  if (handle && get_theme_part_size_)
+    return get_theme_part_size_(handle, hdc, part_id, state_id, rect, ts, size);
+
+  return E_NOTIMPL;
+}
+
+HRESULT NativeTheme::GetThemeColor(ThemeName theme,
+                                   int part_id,
+                                   int state_id,
+                                   int prop_id,
+                                   SkColor* color) const {
+  HANDLE handle = GetThemeHandle(theme);
+  if (handle && get_theme_color_) {
+    COLORREF color_ref;
+    if (get_theme_color_(handle, part_id, state_id, prop_id, &color_ref) ==
+        S_OK) {
+      *color = gfx::COLORREFToSkColor(color_ref);
+      return S_OK;
+    }
+  }
+  return E_NOTIMPL;
+}
+
+SkColor NativeTheme::GetThemeColorWithDefault(ThemeName theme,
+                                              int part_id,
+                                              int state_id,
+                                              int prop_id,
+                                              int default_sys_color) const {
+  SkColor color;
+  if (GetThemeColor(theme, part_id, state_id, prop_id, &color) != S_OK)
+    color = gfx::COLORREFToSkColor(GetSysColor(default_sys_color));
+  return color;
+}
+
+HRESULT NativeTheme::GetThemeInt(ThemeName theme,
+                                 int part_id,
+                                 int state_id,
+                                 int prop_id,
+                                 int *value) const {
+  HANDLE handle = GetThemeHandle(theme);
+  if (handle && get_theme_int_)
+    return get_theme_int_(handle, part_id, state_id, prop_id, value);
+  return E_NOTIMPL;
+}
+
+Size NativeTheme::GetThemeBorderSize(ThemeName theme) const {
+  // For simplicity use the wildcard state==0, part==0, since it works
+  // for the cases we currently depend on.
+  int border;
+  if (GetThemeInt(theme, 0, 0, TMT_BORDERSIZE, &border) == S_OK)
+    return Size(border, border);
+  else
+    return Size(GetSystemMetrics(SM_CXEDGE), GetSystemMetrics(SM_CYEDGE));
+}
+
+
+void NativeTheme::DisableTheming() const {
+  if (!set_theme_properties_)
+    return;
+  set_theme_properties_(0);
+}
+
+HRESULT NativeTheme::PaintFrameControl(HDC hdc,
+                                       RECT* rect,
+                                       UINT type,
+                                       UINT state,
+                                       bool is_highlighted) const {
+  const int width = rect->right - rect->left;
+  const int height = rect->bottom - rect->top;
+
+  // DrawFrameControl for menu arrow/check wants a monochrome bitmap.
+  ScopedBitmap mask_bitmap(CreateBitmap(width, height, 1, 1, NULL));
+
+  if (mask_bitmap == NULL)
+    return E_OUTOFMEMORY;
+
+  ScopedHDC bitmap_dc(CreateCompatibleDC(NULL));
+  HGDIOBJ org_bitmap = SelectObject(bitmap_dc, mask_bitmap);
+  RECT local_rect = { 0, 0, width, height };
+  DrawFrameControl(bitmap_dc, &local_rect, type, state);
+
+  // We're going to use BitBlt with a b&w mask. This results in using the dest
+  // dc's text color for the black bits in the mask, and the dest dc's
+  // background color for the white bits in the mask. DrawFrameControl draws the
+  // check in black, and the background in white.
+  COLORREF old_bg_color =
+      SetBkColor(hdc,
+                 GetSysColor(is_highlighted ? COLOR_HIGHLIGHT : COLOR_MENU));
+  COLORREF old_text_color =
+      SetTextColor(hdc,
+                   GetSysColor(is_highlighted ? COLOR_HIGHLIGHTTEXT :
+                                                COLOR_MENUTEXT));
+  BitBlt(hdc, rect->left, rect->top, width, height, bitmap_dc, 0, 0, SRCCOPY);
+  SetBkColor(hdc, old_bg_color);
+  SetTextColor(hdc, old_text_color);
+
+  SelectObject(bitmap_dc, org_bitmap);
+
+  return S_OK;
+}
+
+void NativeTheme::CloseHandles() const
+{
+  if (!close_theme_)
+    return;
+
+  for (int i = 0; i < LAST; ++i) {
+    if (theme_handles_[i])
+      close_theme_(theme_handles_[i]);
+      theme_handles_[i] = NULL;
+  }
+}
+
+HANDLE NativeTheme::GetThemeHandle(ThemeName theme_name) const
+{
+  if (!open_theme_ || theme_name < 0 || theme_name >= LAST)
+    return 0;
+
+  if (theme_handles_[theme_name])
+    return theme_handles_[theme_name];
+
+  // Not found, try to load it.
+  HANDLE handle = 0;
+  switch (theme_name) {
+  case NativeTheme::BUTTON:
+    handle = open_theme_(NULL, L"Button");
+    break;
+  case NativeTheme::TEXTFIELD:
+    handle = open_theme_(NULL, L"Edit");
+    break;
+  case NativeTheme::MENULIST:
+    handle = open_theme_(NULL, L"Combobox");
+    break;
+  case NativeTheme::SCROLLBAR:
+    handle = open_theme_(NULL, L"Scrollbar");
+    break;
+  case NativeTheme::STATUS:
+    handle = open_theme_(NULL, L"Status");
+    break;
+  case NativeTheme::MENU:
+    handle = open_theme_(NULL, L"Menu");
+    break;
+  case NativeTheme::WINDOW:
+    handle = open_theme_(NULL, L"Window");
+    break;
+  case NativeTheme::TAB:
+    handle = open_theme_(NULL, L"Tab");
+    break;
+  case NativeTheme::LIST:
+    handle = open_theme_(NULL, L"Listview");
+    break;
+  default:
+    NOTREACHED();
+  }
+  theme_handles_[theme_name] = handle;
+  return handle;
+}
+
+}  // namespace gfx
diff --git a/base/gfx/native_theme.h b/base/gfx/native_theme.h
new file mode 100644
index 0000000..918a282
--- /dev/null
+++ b/base/gfx/native_theme.h
@@ -0,0 +1,310 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// A wrapper class for working with custom XP/Vista themes provided in
+// uxtheme.dll.  This is a singleton class that can be grabbed using
+// NativeTheme::instance().
+// For more information on visual style parts and states, see:
+// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/userex/topics/partsandstates.asp
+
+#ifndef BASE_GFX_NATIVE_THEME_H__
+#define BASE_GFX_NATIVE_THEME_H__
+
+#include <windows.h>
+#include <uxtheme.h>
+#include "base/basictypes.h"
+#include "base/gfx/size.h"
+#include "skia/include/SkColor.h"
+
+namespace gfx {
+class PlatformCanvas;
+
+// TODO: Define class member enums to replace part_id and state_id parameters
+// that are currently defined in <vssym32.h>. Afterward, classic_state should
+// be removed and class users wouldn't need to include <vssym32.h> anymore.
+// This would enable HOT state on non-themed UI (like when RDP'ing) and would
+// simplify usage.
+// TODO: This class should probably be changed to be platform independent at
+// the same time.
+class NativeTheme {
+ public:
+  enum ThemeName {
+    BUTTON,
+    TEXTFIELD,
+    MENULIST,
+    SCROLLBAR,
+    STATUS,
+    MENU,
+    WINDOW,
+    TAB,
+    LIST,
+    LAST
+  };
+
+  // This enumeration is used within PaintMenuArrow in order to indicate the
+  // direction the menu arrow should point to.
+  enum MenuArrowDirection {
+    LEFT_POINTING_ARROW,
+    RIGHT_POINTING_ARROW
+  };
+
+  typedef HRESULT (WINAPI* DrawThemeBackgroundPtr)(HANDLE theme,
+                                                   HDC hdc,
+                                                   int part_id,
+                                                   int state_id,
+                                                   const RECT* rect,
+                                                   const RECT* clip_rect);
+  typedef HRESULT (WINAPI* DrawThemeBackgroundExPtr)(HANDLE theme,
+                                                     HDC hdc,
+                                                     int part_id,
+                                                     int state_id,
+                                                     const RECT* rect,
+                                                     const DTBGOPTS* opts);
+  typedef HRESULT (WINAPI* GetThemeColorPtr)(HANDLE hTheme,
+                                             int part_id,
+                                             int state_id,
+                                             int prop_id,
+                                             COLORREF* color);
+  typedef HRESULT (WINAPI* GetThemeContentRectPtr)(HANDLE hTheme,
+                                                   HDC hdc,
+                                                   int part_id,
+                                                   int state_id,
+                                                   const RECT* rect,
+                                                   RECT* content_rect);
+  typedef HRESULT (WINAPI* GetThemePartSizePtr)(HANDLE hTheme,
+                                                HDC hdc,
+                                                int part_id,
+                                                int state_id,
+                                                RECT* rect,
+                                                int ts,
+                                                SIZE* size);
+  typedef HANDLE (WINAPI* OpenThemeDataPtr)(HWND window,
+                                            LPCWSTR class_list);
+  typedef HRESULT (WINAPI* CloseThemeDataPtr)(HANDLE theme);
+
+  typedef void (WINAPI* SetThemeAppPropertiesPtr) (DWORD flags);
+  typedef BOOL (WINAPI* IsThemeActivePtr)();
+  typedef HRESULT (WINAPI* GetThemeIntPtr)(HANDLE hTheme,
+                                           int part_id,
+                                           int state_id,
+                                           int prop_id,
+                                           int *value);
+
+  HRESULT PaintButton(HDC hdc,
+                      int part_id,
+                      int state_id,
+                      int classic_state,
+                      RECT* rect) const;
+
+  HRESULT PaintTextField(HDC hdc,
+                         int part_id,
+                         int state_id,
+                         int classic_state,
+                         RECT* rect,
+                         COLORREF color,
+                         bool fill_content_area,
+                         bool draw_edges) const;
+
+  HRESULT PaintMenuList(HDC hdc,
+                        int part_id,
+                        int state_id,
+                        int classic_state,
+                        RECT* rect) const;
+
+  // Paints a scrollbar arrow.  |classic_state| should have the appropriate
+  // classic part number ORed in already.
+  HRESULT PaintScrollbarArrow(HDC hdc,
+                              int state_id,
+                              int classic_state,
+                              RECT* rect) const;
+
+  // Paints a scrollbar track section.  |align_rect| is only used in classic
+  // mode, and makes sure the checkerboard pattern in |target_rect| is aligned
+  // with one presumed to be in |align_rect|.
+  HRESULT PaintScrollbarTrack(HDC hdc,
+                              int part_id,
+                              int state_id,
+                              int classic_state,
+                              RECT* target_rect,
+                              RECT* align_rect,
+                              PlatformCanvas* canvas) const;
+
+  // |arrow_direction| determines whether the arrow is pointing to the left or
+  // to the right. In RTL locales, sub-menus open from right to left and
+  // therefore the menu arrow should point to the left and not to the right.
+  HRESULT PaintMenuArrow(ThemeName theme,
+                         HDC hdc,
+                         int part_id,
+                         int state_id,
+                         RECT* rect,
+                         MenuArrowDirection arrow_direction,
+                         bool is_highlighted) const;
+
+  HRESULT PaintMenuBackground(ThemeName theme,
+                              HDC hdc,
+                              int part_id,
+                              int state_id,
+                              RECT* rect) const;
+
+  HRESULT PaintMenuCheck(ThemeName theme,
+                         HDC hdc,
+                         int part_id,
+                         int state_id,
+                         RECT* rect,
+                         bool is_highlighted) const;
+
+  HRESULT PaintMenuCheckBackground(ThemeName theme,
+                                   HDC hdc,
+                                   int part_id,
+                                   int state_id,
+                                   RECT* rect) const;
+
+  HRESULT PaintMenuGutter(HDC hdc,
+                          int part_id,
+                          int state_id,
+                          RECT* rect) const;
+
+  HRESULT PaintMenuSeparator(HDC hdc,
+                             int part_id,
+                             int state_id,
+                             RECT* rect) const;
+
+  HRESULT PaintMenuItemBackground(ThemeName theme,
+                                  HDC hdc,
+                                  int part_id,
+                                  int state_id,
+                                  bool selected,
+                                  RECT* rect) const;
+
+  // Paints a scrollbar thumb or gripper.
+  HRESULT PaintScrollbarThumb(HDC hdc,
+                              int part_id,
+                              int state_id,
+                              int classic_state,
+                              RECT* rect) const;
+
+  HRESULT PaintStatusGripper(HDC hdc,
+                             int part_id,
+                             int state_id,
+                             int classic_state,
+                             RECT* rect) const;
+
+  HRESULT PaintDialogBackground(HDC dc, bool active, RECT* rect) const;
+
+  HRESULT PaintTabPanelBackground(HDC dc, RECT* rect) const;
+
+  HRESULT PaintListBackground(HDC dc, bool enabled, RECT* rect) const;
+
+  bool IsThemingActive() const;
+
+  HRESULT GetThemePartSize(ThemeName themeName,
+                           HDC hdc,
+                           int part_id,
+                           int state_id,
+                           RECT* rect,
+                           int ts,
+                           SIZE* size) const;
+
+  HRESULT GetThemeColor(ThemeName theme,
+                        int part_id,
+                        int state_id,
+                        int prop_id,
+                        SkColor* color) const;
+
+  // Get the theme color if theming is enabled.  If theming is unsupported
+  // for this part, use Win32's GetSysColor to find the color specified
+  // by default_sys_color.
+  SkColor GetThemeColorWithDefault(ThemeName theme,
+                                   int part_id,
+                                   int state_id,
+                                   int prop_id,
+                                   int default_sys_color) const;
+
+  HRESULT GetThemeInt(ThemeName theme,
+                      int part_id,
+                      int state_id,
+                      int prop_id,
+                      int *result) const;
+
+  // Get the thickness of the border associated with the specified theme,
+  // defaulting to GetSystemMetrics edge size if themes are disabled.
+  // In Classic Windows, borders are typically 2px; on XP+, they are 1px.
+  Size GetThemeBorderSize(ThemeName theme) const;
+
+  // Disables all theming for top-level windows in the entire process, from
+  // when this method is called until the process exits.  All the other
+  // methods in this class will continue to work, but their output will ignore
+  // the user's theme. This is meant for use when running tests that require
+  // consistent visual results.
+  void DisableTheming() const;
+
+  // Closes cached theme handles so we can unload the DLL or update our UI
+  // for a theme change.
+  void CloseHandles() const;
+
+  // Gets our singleton instance.
+  static const NativeTheme* instance();
+
+ private:
+  NativeTheme();
+  ~NativeTheme();
+
+  HRESULT PaintFrameControl(HDC hdc,
+                            RECT* rect,
+                            UINT type,
+                            UINT state,
+                            bool is_highlighted) const;
+
+  // Returns a handle to the theme data.
+  HANDLE GetThemeHandle(ThemeName theme_name) const;
+
+  // Function pointers into uxtheme.dll.
+  DrawThemeBackgroundPtr draw_theme_;
+  DrawThemeBackgroundExPtr draw_theme_ex_;
+  GetThemeColorPtr get_theme_color_;
+  GetThemeContentRectPtr get_theme_content_rect_;
+  GetThemePartSizePtr get_theme_part_size_;
+  OpenThemeDataPtr open_theme_;
+  CloseThemeDataPtr close_theme_;
+  SetThemeAppPropertiesPtr set_theme_properties_;
+  IsThemeActivePtr is_theme_active_;
+  GetThemeIntPtr get_theme_int_;
+
+  // Handle to uxtheme.dll.
+  HMODULE theme_dll_;
+
+  // A cache of open theme handles.
+  mutable HANDLE theme_handles_[LAST];
+
+  DISALLOW_EVIL_CONSTRUCTORS(NativeTheme);
+};
+
+}  // namespace gfx
+
+#endif  // BASE_GFX_NATIVE_THEME_H__
diff --git a/base/gfx/native_theme_unittest.cc b/base/gfx/native_theme_unittest.cc
new file mode 100644
index 0000000..6ed50e4
--- /dev/null
+++ b/base/gfx/native_theme_unittest.cc
@@ -0,0 +1,36 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/gfx/native_theme.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(NativeThemeTest, Init) {
+  ASSERT_TRUE(gfx::NativeTheme::instance() != NULL);
+}
diff --git a/base/gfx/platform_canvas.cc b/base/gfx/platform_canvas.cc
new file mode 100644
index 0000000..a421069
--- /dev/null
+++ b/base/gfx/platform_canvas.cc
@@ -0,0 +1,105 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/gfx/platform_canvas.h"
+
+#include "base/gfx/bitmap_platform_device.h"
+#include "base/logging.h"
+
+namespace gfx {
+
+PlatformCanvas::PlatformCanvas() : SkCanvas() {
+}
+
+PlatformCanvas::PlatformCanvas(int width, int height, bool is_opaque)
+    : SkCanvas() {
+  initialize(width, height, is_opaque, NULL);
+}
+
+PlatformCanvas::PlatformCanvas(int width,
+                               int height,
+                               bool is_opaque,
+                               HANDLE shared_section)
+    : SkCanvas() {
+  initialize(width, height, is_opaque, shared_section);
+}
+
+PlatformCanvas::~PlatformCanvas() {
+}
+
+void PlatformCanvas::initialize(int width,
+                                int height,
+                                bool is_opaque,
+                                HANDLE shared_section) {
+  SkDevice* device =
+      createPlatformDevice(width, height, is_opaque, shared_section);
+  setDevice(device);
+  device->unref(); // was created with refcount 1, and setDevice also refs
+}
+
+HDC PlatformCanvas::beginPlatformPaint() {
+  return getTopPlatformDevice().getBitmapDC();
+}
+
+void PlatformCanvas::endPlatformPaint() {
+  // we don't clear the DC here since it will be likely to be used again
+  // flushing will be done in onAccessBitmap
+}
+
+PlatformDevice& PlatformCanvas::getTopPlatformDevice() const {
+  // All of our devices should be our special PlatformDevice.
+  SkCanvas::LayerIter iter(const_cast<PlatformCanvas*>(this), false);
+  return *static_cast<PlatformDevice*>(iter.device());
+}
+
+SkDevice* PlatformCanvas::createDevice(SkBitmap::Config config,
+                                       int width,
+                                       int height,
+                                       bool is_opaque, bool isForLayer) {
+  DCHECK(config == SkBitmap::kARGB_8888_Config);
+  return createPlatformDevice(width, height, is_opaque, NULL);
+}
+
+SkDevice* PlatformCanvas::createPlatformDevice(int width,
+                                               int height,
+                                               bool is_opaque,
+                                               HANDLE shared_section) {
+  HDC screen_dc = GetDC(NULL);
+  SkDevice* device = BitmapPlatformDevice::create(screen_dc, width, height,
+                                                  is_opaque, shared_section);
+  ReleaseDC(NULL, screen_dc);
+  return device;
+}
+
+SkDevice* PlatformCanvas::setBitmapDevice(const SkBitmap&) {
+  NOTREACHED();
+  return NULL;
+}
+
+}  // namespace gfx
\ No newline at end of file
diff --git a/base/gfx/platform_canvas.h b/base/gfx/platform_canvas.h
new file mode 100644
index 0000000..c024a2f
--- /dev/null
+++ b/base/gfx/platform_canvas.h
@@ -0,0 +1,209 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_GFX_PLATFORM_CANVAS_H__
+#define BASE_GFX_PLATFORM_CANVAS_H__
+
+#include "base/gfx/platform_device.h"
+#include "base/basictypes.h"
+
+#include "SkCanvas.h"
+
+namespace gfx {
+
+// This class is a specialization of the regular SkCanvas that is designed to
+// work with a gfx::PlatformDevice to manage platform-specific drawing. It
+// allows using both Skia operations and platform-specific operations.
+class PlatformCanvas : public SkCanvas {
+ public:
+  // Set is_opaque if you are going to erase the bitmap and not use
+  // tranparency: this will enable some optimizations.  The shared_section
+  // parameter is passed to gfx::PlatformDevice::create.  See it for details.
+  //
+  // If you use the version with no arguments, you MUST call initialize()
+  PlatformCanvas();
+  PlatformCanvas(int width, int height, bool is_opaque);
+  PlatformCanvas(int width, int height, bool is_opaque, HANDLE shared_section);
+  virtual ~PlatformCanvas();
+
+  // For two-part init, call if you use the no-argument constructor above
+  void initialize(int width, int height, bool is_opaque, HANDLE shared_section);
+
+  // These calls should surround calls to platform drawing routines, the DC
+  // returned by beginPlatformPaint is the DC that can be used to draw into.
+  // Call endPlatformPaint when you are done and want to use Skia operations
+  // again; this will synchronize the bitmap to Windows.
+  virtual HDC beginPlatformPaint();
+  virtual void endPlatformPaint();
+
+  // Returns the platform device pointer of the topmost rect with a non-empty
+  // clip. In practice, this is usually either the top layer or nothing, since
+  // we usually set the clip to new layers when we make them.
+  //
+  // If there is no layer that is not all clipped out, this will return a
+  // dummy device so callers do not have to check. If you are concerned about
+  // performance, check the clip before doing any painting.
+  //
+  // This is different than SkCanvas' getDevice, because that returns the
+  // bottommost device.
+  //
+  // Danger: the resulting device should not be saved. It will be invalidated
+  // by the next call to save() or restore().
+  PlatformDevice& getTopPlatformDevice() const;
+
+ protected:
+  // Creates a device store for use by the canvas. We override this so that
+  // the device is always our own so we know that we can use GDI operations
+  // on it. Simply calls into createPlatformDevice().
+  virtual SkDevice* createDevice(SkBitmap::Config, int width, int height,
+                                 bool is_opaque, bool isForLayer);
+
+  // Creates a device store for use by the canvas. By default, it creates a
+  // BitmapPlatformDevice object. Can be overridden to change the object type.
+  virtual SkDevice* createPlatformDevice(int width, int height, bool is_opaque,
+                                         HANDLE shared_section);
+
+ private:
+  // Unimplemented.
+  virtual SkDevice* setBitmapDevice(const SkBitmap& bitmap);
+
+  DISALLOW_EVIL_CONSTRUCTORS(PlatformCanvas);
+};
+
+// A class designed to help with WM_PAINT operations on Windows. It will
+// do BeginPaint/EndPaint on init/destruction, and will create the bitmap and
+// canvas with the correct size and transform for the dirty rect. The bitmap
+// will be automatically painted to the screen on destruction.
+//
+// You MUST call isEmpty before painting to determine if anything needs
+// painting. Sometimes the dirty rect can actually be empty, and this makes
+// the bitmap functions we call unhappy. The caller should not paint in this
+// case.
+//
+// Therefore, all you need to do is:
+//   case WM_PAINT: {
+//     gfx::PlatformCanvasPaint canvas(hwnd);
+//     if (!canvas.isEmpty()) {
+//       ... paint to the canvas ...
+//     }
+//     return 0;
+//   }
+template <class T>
+class CanvasPaintT : public T {
+ public:
+  CanvasPaintT(HWND hwnd) : hwnd_(hwnd), for_paint_(true) {
+    initPaint(true);
+  }
+
+  CanvasPaintT(HWND hwnd, bool opaque) : hwnd_(hwnd), for_paint_(true) {
+    initPaint(opaque);
+  }
+
+  // Creates a CanvasPaintT for the specified region that paints to the
+  // specified dc. This does NOT do BeginPaint/EndPaint.
+  CanvasPaintT(HDC dc, bool opaque, int x, int y, int w, int h)
+      : hwnd_(NULL),
+        paint_dc_(dc),
+        for_paint_(false) {
+    memset(&ps_, 0, sizeof(ps_));
+    ps_.rcPaint.left = x;
+    ps_.rcPaint.right = x + w;
+    ps_.rcPaint.top = y;
+    ps_.rcPaint.bottom = y + h;
+    init(opaque);
+  }
+
+
+  virtual ~CanvasPaintT() {
+    if (!isEmpty()) {
+      restoreToCount(1);
+      // Commit the drawing to the screen
+      getTopPlatformDevice().drawToHDC(paint_dc_,
+                                       ps_.rcPaint.left, ps_.rcPaint.top,
+                                       NULL);
+    }
+    if (for_paint_)
+      EndPaint(hwnd_, &ps_);
+  }
+
+  // Returns true if the invalid region is empty. The caller should call this
+  // function to determine if anything needs painting.
+  bool isEmpty() const {
+    return ps_.rcPaint.right - ps_.rcPaint.left == 0 ||
+           ps_.rcPaint.bottom - ps_.rcPaint.top == 0;
+  }
+
+  // Use to access the Windows painting parameters, especially useful for
+  // getting the bounding rect for painting: paintstruct().rcPaint
+  const PAINTSTRUCT& paintStruct() const {
+    return ps_;
+  }
+
+  // Returns the DC that will be painted to
+  HDC paintDC() const {
+    return paint_dc_;
+  }
+
+ protected:
+  HWND hwnd_;
+  HDC paint_dc_;
+  PAINTSTRUCT ps_;
+
+ private:
+  void initPaint(bool opaque) {
+    paint_dc_ = BeginPaint(hwnd_, &ps_);
+
+    init(opaque);
+  }
+
+  void init(bool opaque) {
+    // FIXME(brettw) for ClearType, we probably want to expand the bounds of
+    // painting by one pixel so that the boundaries will be correct (ClearType
+    // text can depend on the adjacent pixel). Then we would paint just the inset
+    // pixels to the screen.
+    initialize(ps_.rcPaint.right - ps_.rcPaint.left,
+               ps_.rcPaint.bottom - ps_.rcPaint.top, opaque, NULL);
+
+    // This will bring the canvas into the screen coordinate system for the
+    // dirty rect
+    translate(SkIntToScalar(-ps_.rcPaint.left),
+              SkIntToScalar(-ps_.rcPaint.top));
+  }
+
+  // If true, this canvas was created for a BeginPaint.
+  const bool for_paint_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(CanvasPaintT);
+};
+
+typedef CanvasPaintT<PlatformCanvas> PlatformCanvasPaint;
+
+}  // namespace gfx
+
+#endif  // BASE_GFX_PLATFORM_CANVAS_H__
diff --git a/base/gfx/platform_canvas_unittest.cc b/base/gfx/platform_canvas_unittest.cc
new file mode 100644
index 0000000..2d127a7
--- /dev/null
+++ b/base/gfx/platform_canvas_unittest.cc
@@ -0,0 +1,293 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+
+#include "base/gfx/platform_canvas.h"
+#include "base/gfx/platform_device.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "SkColor.h"
+
+namespace gfx {
+
+namespace {
+
+// Return true if the canvas is filled to canvas_color,
+// and contains a single rectangle filled to rect_color.
+bool VerifyRect(const PlatformCanvas& canvas,
+                uint32_t canvas_color, uint32_t rect_color,
+                int x, int y, int w, int h) {
+  PlatformDevice& device = canvas.getTopPlatformDevice();
+  const SkBitmap& bitmap = device.accessBitmap(false);
+  SkAutoLockPixels lock(bitmap);
+
+  for (int cur_y = 0; cur_y < bitmap.height(); cur_y++) {
+    for (int cur_x = 0; cur_x < bitmap.width(); cur_x++) {
+      if (cur_x >= x && cur_x < x + w &&
+          cur_y >= y && cur_y < y + h) {
+        // Inside the square should be rect_color
+        if (*bitmap.getAddr32(cur_x, cur_y) != rect_color)
+          return false;
+      } else {
+        // Outside the square should be canvas_color
+        if (*bitmap.getAddr32(cur_x, cur_y) != canvas_color)
+          return false;
+      }
+    }
+  }
+  return true;
+}
+
+// Checks whether there is a white canvas with a black square at the given
+// location in pixels (not in the canvas coordinate system).
+// TODO(ericroman): rename Square to Rect
+bool VerifyBlackSquare(const PlatformCanvas& canvas, int x, int y, int w, int h) {
+  return VerifyRect(canvas, SK_ColorWHITE, SK_ColorBLACK, x, y, w, h);
+}
+
+// Check that every pixel in the canvas is a single color.
+bool VerifyCanvasColor(const PlatformCanvas& canvas, uint32_t canvas_color) {
+  return VerifyRect(canvas, canvas_color, 0, 0, 0, 0, 0);
+}
+
+void DrawGDIRect(PlatformCanvas& canvas, int x, int y, int w, int h) {
+  HDC dc = canvas.beginPlatformPaint();
+
+  RECT inner_rc;
+  inner_rc.left = x;
+  inner_rc.top = y;
+  inner_rc.right = x + w;
+  inner_rc.bottom = y + h;
+  FillRect(dc, &inner_rc, reinterpret_cast<HBRUSH>(GetStockObject(BLACK_BRUSH)));
+
+  canvas.endPlatformPaint();
+}
+
+// Clips the contents of the canvas to the given rectangle. This will be
+// intersected with any existing clip.
+void AddClip(PlatformCanvas& canvas, int x, int y, int w, int h) {
+  SkRect rect;
+  rect.set(SkIntToScalar(x), SkIntToScalar(y),
+           SkIntToScalar(x + w), SkIntToScalar(y + h));
+  canvas.clipRect(rect);
+}
+
+class LayerSaver {
+ public:
+  LayerSaver(PlatformCanvas& canvas, int x, int y, int w, int h)
+      : canvas_(canvas),
+        x_(x),
+        y_(y),
+        w_(w),
+        h_(h) {
+    SkRect bounds;
+    bounds.set(SkIntToScalar(x_), SkIntToScalar(y_),
+               SkIntToScalar(right()), SkIntToScalar(bottom()));
+    canvas_.saveLayer(&bounds, NULL);
+  }
+
+  ~LayerSaver() {
+    canvas_.getTopPlatformDevice().fixupAlphaBeforeCompositing();
+    canvas_.restore();
+  }
+
+  int x() const { return x_; }
+  int y() const { return y_; }
+  int w() const { return w_; }
+  int h() const { return h_; }
+
+  // Returns the EXCLUSIVE far bounds of the layer.
+  int right() const { return x_ + w_; }
+  int bottom() const { return y_ + h_; }
+
+ private:
+  PlatformCanvas& canvas_;
+  int x_, y_, w_, h_;
+};
+
+// Size used for making layers in many of the below tests.
+const int kLayerX = 2;
+const int kLayerY = 3;
+const int kLayerW = 9;
+const int kLayerH = 7;
+
+// Size used by some tests to draw a rectangle inside the layer.
+const int kInnerX = 4;
+const int kInnerY = 5;
+const int kInnerW = 2;
+const int kInnerH = 3;
+
+}
+
+// This just checks that our checking code is working properly, it just uses
+// regular skia primitives.
+TEST(PlatformCanvas, SkLayer) {
+  // Create the canvas initialized to opaque white.
+  PlatformCanvas canvas(16, 16, true);
+  canvas.drawColor(SK_ColorWHITE);
+
+  // Make a layer and fill it completely to make sure that the bounds are
+  // correct.
+  {
+    LayerSaver layer(canvas, kLayerX, kLayerY, kLayerW, kLayerH);
+    canvas.drawColor(SK_ColorBLACK);
+  }
+  EXPECT_TRUE(VerifyBlackSquare(canvas, kLayerX, kLayerY, kLayerW, kLayerH));
+}
+
+// Test the GDI clipping.
+TEST(PlatformCanvas, GDIClipRegion) {
+  // Initialize a white canvas
+  PlatformCanvas canvas(16, 16, true);
+  canvas.drawColor(SK_ColorWHITE);
+  EXPECT_TRUE(VerifyCanvasColor(canvas, SK_ColorWHITE));
+
+  // Test that initially the canvas has no clip region, by filling it
+  // with a black rectangle.
+  // Note: Don't use LayerSaver, since internally it sets a clip region.
+  DrawGDIRect(canvas, 0, 0, 16, 16);
+  canvas.getTopPlatformDevice().fixupAlphaBeforeCompositing();
+  EXPECT_TRUE(VerifyCanvasColor(canvas, SK_ColorBLACK));
+
+  // Test that intersecting disjoint clip rectangles sets an empty clip region
+  canvas.drawColor(SK_ColorWHITE);
+  EXPECT_TRUE(VerifyCanvasColor(canvas, SK_ColorWHITE));
+  {
+    LayerSaver layer(canvas, 0, 0, 16, 16);
+    AddClip(canvas, 2, 3, 4, 5);
+    AddClip(canvas, 4, 9, 10, 10);
+    DrawGDIRect(canvas, 0, 0, 16, 16);
+  }
+  EXPECT_TRUE(VerifyCanvasColor(canvas, SK_ColorWHITE));
+}
+
+// Test the layers get filled properly by GDI.
+TEST(PlatformCanvas, GDILayer) {
+  // Create the canvas initialized to opaque white.
+  PlatformCanvas canvas(16, 16, true);
+
+  // Make a layer and fill it completely to make sure that the bounds are
+  // correct.
+  canvas.drawColor(SK_ColorWHITE);
+  {
+    LayerSaver layer(canvas, kLayerX, kLayerY, kLayerW, kLayerH);
+    DrawGDIRect(canvas, 0, 0, 100, 100);
+  }
+  EXPECT_TRUE(VerifyBlackSquare(canvas, kLayerX, kLayerY, kLayerW, kLayerH));
+
+  // Make a layer and fill it partially to make sure the translation is correct.
+  canvas.drawColor(SK_ColorWHITE);
+  {
+    LayerSaver layer(canvas, kLayerX, kLayerY, kLayerW, kLayerH);
+    DrawGDIRect(canvas, kInnerX, kInnerY, kInnerW, kInnerH);
+  }
+  EXPECT_TRUE(VerifyBlackSquare(canvas, kInnerX, kInnerY, kInnerW, kInnerH));
+
+  // Add a clip on the layer and fill to make make sure clip is correct.
+  canvas.drawColor(SK_ColorWHITE);
+  {
+    LayerSaver layer(canvas, kLayerX, kLayerY, kLayerW, kLayerH);
+    canvas.save();
+    AddClip(canvas, kInnerX, kInnerY, kInnerW, kInnerH);
+    DrawGDIRect(canvas, 0, 0, 100, 100);
+    canvas.restore();
+  }
+  EXPECT_TRUE(VerifyBlackSquare(canvas, kInnerX, kInnerY, kInnerW, kInnerH));
+
+  // Add a clip and then make the layer to make sure the clip is correct.
+  canvas.drawColor(SK_ColorWHITE);
+  canvas.save();
+  AddClip(canvas, kInnerX, kInnerY, kInnerW, kInnerH);
+  {
+    LayerSaver layer(canvas, kLayerX, kLayerY, kLayerW, kLayerH);
+    DrawGDIRect(canvas, 0, 0, 100, 100);
+  }
+  canvas.restore();
+  EXPECT_TRUE(VerifyBlackSquare(canvas, kInnerX, kInnerY, kInnerW, kInnerH));
+}
+
+// Test that translation + make layer works properly.
+TEST(PlatformCanvas, GDITranslateLayer) {
+  // Create the canvas initialized to opaque white.
+  PlatformCanvas canvas(16, 16, true);
+
+  // Make a layer and fill it completely to make sure that the bounds are
+  // correct.
+  canvas.drawColor(SK_ColorWHITE);
+  canvas.save();
+  canvas.translate(1, 1);
+  {
+    LayerSaver layer(canvas, kLayerX, kLayerY, kLayerW, kLayerH);
+    DrawGDIRect(canvas, 0, 0, 100, 100);
+  }
+  canvas.restore();
+  EXPECT_TRUE(VerifyBlackSquare(canvas, kLayerX + 1, kLayerY + 1,
+                                kLayerW, kLayerH));
+
+  // Translate then make the layer.
+  canvas.drawColor(SK_ColorWHITE);
+  canvas.save();
+  canvas.translate(1, 1);
+  {
+    LayerSaver layer(canvas, kLayerX, kLayerY, kLayerW, kLayerH);
+    DrawGDIRect(canvas, kInnerX, kInnerY, kInnerW, kInnerH);
+  }
+  canvas.restore();
+  EXPECT_TRUE(VerifyBlackSquare(canvas, kInnerX + 1, kInnerY + 1,
+                                kInnerW, kInnerH));
+
+  // Make the layer then translate.
+  canvas.drawColor(SK_ColorWHITE);
+  canvas.save();
+  {
+    LayerSaver layer(canvas, kLayerX, kLayerY, kLayerW, kLayerH);
+    canvas.translate(1, 1);
+    DrawGDIRect(canvas, kInnerX, kInnerY, kInnerW, kInnerH);
+  }
+  canvas.restore();
+  EXPECT_TRUE(VerifyBlackSquare(canvas, kInnerX + 1, kInnerY + 1,
+                                kInnerW, kInnerH));
+
+  // Translate both before and after, and have a clip.
+  canvas.drawColor(SK_ColorWHITE);
+  canvas.save();
+  canvas.translate(1, 1);
+  {
+    LayerSaver layer(canvas, kLayerX, kLayerY, kLayerW, kLayerH);
+    canvas.translate(1, 1);
+    AddClip(canvas, kInnerX, kInnerY, kInnerW, kInnerH);
+    DrawGDIRect(canvas, 0, 0, 100, 100);
+  }
+  canvas.restore();
+  EXPECT_TRUE(VerifyBlackSquare(canvas, kInnerX + 2, kInnerY + 2,
+                                kInnerW, kInnerH));
+}
+
+}  // namespace
diff --git a/base/gfx/platform_device.cc b/base/gfx/platform_device.cc
new file mode 100644
index 0000000..e23ccf3
--- /dev/null
+++ b/base/gfx/platform_device.cc
@@ -0,0 +1,253 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/gfx/platform_device.h"
+
+#include "base/logging.h"
+#include "base/gfx/skia_utils.h"
+#include "SkMatrix.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkUtils.h"
+
+namespace gfx {
+
+PlatformDevice::PlatformDevice(const SkBitmap& bitmap)
+    : SkDevice(bitmap) {
+}
+
+// static
+void PlatformDevice::InitializeDC(HDC context) {
+  // Enables world transformation.
+  // If the GM_ADVANCED graphics mode is set, GDI always draws arcs in the
+  // counterclockwise direction in logical space. This is equivalent to the
+  // statement that, in the GM_ADVANCED graphics mode, both arc control points
+  // and arcs themselves fully respect the device context's world-to-device
+  // transformation.
+  BOOL res = SetGraphicsMode(context, GM_ADVANCED);
+  DCHECK_NE(res, 0);
+
+  // Enables dithering.
+  res = SetStretchBltMode(context, HALFTONE);
+  DCHECK_NE(res, 0);
+  // As per SetStretchBltMode() documentation, SetBrushOrgEx() must be called
+  // right after.
+  res = SetBrushOrgEx(context, 0, 0, NULL);
+  DCHECK_NE(res, 0);
+
+  // Sets up default orientation.
+  res = SetArcDirection(context, AD_CLOCKWISE);
+  DCHECK_NE(res, 0);
+
+  // Sets up default colors.
+  res = SetBkColor(context, RGB(255, 255, 255));
+  DCHECK_NE(res, CLR_INVALID);
+  res = SetTextColor(context, RGB(0, 0, 0));
+  DCHECK_NE(res, CLR_INVALID);
+  res = SetDCBrushColor(context, RGB(255, 255, 255));
+  DCHECK_NE(res, CLR_INVALID);
+  res = SetDCPenColor(context, RGB(0, 0, 0));
+  DCHECK_NE(res, CLR_INVALID);
+
+  // Sets up default transparency.
+  res = SetBkMode(context, OPAQUE);
+  DCHECK_NE(res, 0);
+  res = SetROP2(context, R2_COPYPEN);
+  DCHECK_NE(res, 0);
+}
+
+// static
+void PlatformDevice::LoadPathToDC(HDC context, const SkPath& path) {
+  switch (path.getFillType()) {
+    case SkPath::kWinding_FillType: {
+      int res = SetPolyFillMode(context, WINDING);
+      DCHECK(res != 0);
+      break;
+    }
+    case SkPath::kEvenOdd_FillType: {
+      int res = SetPolyFillMode(context, ALTERNATE);
+      DCHECK(res != 0);
+      break;
+    }
+    default: {
+      NOTREACHED();
+      break;
+    }
+  }
+  BOOL res = BeginPath(context);
+  DCHECK(res != 0);
+
+  CubicPaths paths;
+  if (!SkPathToCubicPaths(&paths, path))
+    return;
+
+  std::vector<POINT> points;
+  for (CubicPaths::const_iterator path(paths.begin()); path != paths.end();
+       ++path) {
+    if (!path->size())
+      continue;
+    // DCHECK_EQ(points.size() % 4, 0);
+    points.resize(0);
+    points.reserve(path->size() * 3 / 4 + 1);
+    points.push_back(SkPointToPOINT(path->front().p[0]));
+    for (CubicPath::const_iterator point(path->begin()); point != path->end();
+       ++point) {
+      // Never add point->p[0]
+      points.push_back(SkPointToPOINT(point->p[1]));
+      points.push_back(SkPointToPOINT(point->p[2]));
+      points.push_back(SkPointToPOINT(point->p[3]));
+    }
+    DCHECK_EQ((points.size() - 1) % 3, 0);
+    // This is slightly inefficient since all straight line and quadratic lines
+    // are "upgraded" to a cubic line.
+    // TODO(maruel):  http://b/1147346 We should use
+    // PolyDraw/PolyBezier/Polyline whenever possible.
+    res = PolyBezier(context, &points.front(),
+                     static_cast<DWORD>(points.size()));
+    DCHECK_NE(res, 0);
+    if (res == 0)
+      break;
+  }
+  if (res == 0) {
+    // Make sure the path is discarded.
+    AbortPath(context);
+  } else {
+    res = EndPath(context);
+    DCHECK(res != 0);
+  }
+}
+
+// static
+void PlatformDevice::LoadTransformToDC(HDC dc, const SkMatrix& matrix) {
+  XFORM xf;
+  xf.eM11 = matrix[SkMatrix::kMScaleX];
+  xf.eM21 = matrix[SkMatrix::kMSkewX];
+  xf.eDx = matrix[SkMatrix::kMTransX];
+  xf.eM12 = matrix[SkMatrix::kMSkewY];
+  xf.eM22 = matrix[SkMatrix::kMScaleY];
+  xf.eDy = matrix[SkMatrix::kMTransY];
+  SetWorldTransform(dc, &xf);
+}
+
+// static
+bool PlatformDevice::SkPathToCubicPaths(CubicPaths* paths,
+                                        const SkPath& skpath) {
+  paths->clear();
+  CubicPath* current_path = NULL;
+  SkPoint current_points[4];
+  CubicPoints points_to_add;
+  SkPath::Iter iter(skpath, false);
+  for (SkPath::Verb verb = iter.next(current_points);
+       verb != SkPath::kDone_Verb;
+       verb = iter.next(current_points)) {
+    switch (verb) {
+      case SkPath::kMove_Verb: {  // iter.next returns 1 point
+        // Ignores it since the point is copied in the next operation. See
+        // SkPath::Iter::next() for reference.
+        paths->push_back(CubicPath());
+        current_path = &paths->back();
+        // Skip point addition.
+        continue;
+      }
+      case SkPath::kLine_Verb: {  // iter.next returns 2 points
+        points_to_add.p[0] = current_points[0];
+        points_to_add.p[1] = current_points[0];
+        points_to_add.p[2] = current_points[1];
+        points_to_add.p[3] = current_points[1];
+        break;
+      }
+      case SkPath::kQuad_Verb: {  // iter.next returns 3 points
+        points_to_add.p[0] = current_points[0];
+        points_to_add.p[1] = current_points[1];
+        points_to_add.p[2] = current_points[2];
+        points_to_add.p[3] = current_points[2];
+        break;
+      }
+      case SkPath::kCubic_Verb: {  // iter.next returns 4 points
+        points_to_add.p[0] = current_points[0];
+        points_to_add.p[1] = current_points[1];
+        points_to_add.p[2] = current_points[2];
+        points_to_add.p[3] = current_points[3];
+        break;
+      }
+      case SkPath::kClose_Verb: {  // iter.next returns 1 point (the last point)
+        paths->push_back(CubicPath());
+        current_path = &paths->back();
+        continue;
+      }
+      case SkPath::kDone_Verb:  // iter.next returns 0 points
+      default: {
+        current_path = NULL;
+        // Will return false.
+        break;
+      }
+    }
+    DCHECK(current_path);
+    if (!current_path) {
+      paths->clear();
+      return false;
+    }
+    current_path->push_back(points_to_add);
+  }
+  return true;
+}
+
+// static
+void PlatformDevice::LoadClippingRegionToDC(HDC context,
+                                            const SkRegion& region,
+                                            const SkMatrix& transformation) {
+  HRGN hrgn;
+  if (region.isEmpty()) {
+    // region can be empty, in which case everything will be clipped.
+    hrgn = CreateRectRgn(0, 0, 0, 0);
+  } else if (region.isRect()) {
+    // Do the transformation.
+    SkRect rect;
+    rect.set(region.getBounds());
+    transformation.mapRect(&rect);
+    SkIRect irect;
+    rect.round(&irect);
+    hrgn = CreateRectRgnIndirect(&SkIRectToRECT(irect));
+  } else {
+    // It is complex.
+    SkPath path;
+    region.getBoundaryPath(&path);
+    // Clip. Note that windows clipping regions are not affected by the
+    // transform so apply it manually.
+    path.transform(transformation);
+    LoadPathToDC(context, path);
+    hrgn = PathToRegion(context);
+  }
+  int result = SelectClipRgn(context, hrgn);
+  DCHECK_NE(result, ERROR);
+  result = DeleteObject(hrgn);
+  DCHECK_NE(result, 0);
+}
+
+}  // namespace gfx
diff --git a/base/gfx/platform_device.h b/base/gfx/platform_device.h
new file mode 100644
index 0000000..c3dc998
--- /dev/null
+++ b/base/gfx/platform_device.h
@@ -0,0 +1,120 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_GFX_PLATFORM_DEVICE_H__
+#define BASE_GFX_PLATFORM_DEVICE_H__
+
+#include <vector>
+
+#include "SkDevice.h"
+
+class SkMatrix;
+class SkPath;
+class SkRegion;
+
+namespace gfx {
+
+// A device is basically a wrapper around SkBitmap that provides a surface for
+// SkCanvas to draw into. Our device provides a surface Windows can also write
+// to. It also provides functionality to play well with GDI drawing functions.
+// This class is abstract and must be subclassed. It provides the basic
+// interface to implement it either with or without a bitmap backend.
+class PlatformDevice : public SkDevice {
+ public:
+  // The DC that corresponds to the bitmap, used for GDI operations drawing
+  // into the bitmap. This is possibly heavyweight, so it should be existant
+  // only during one pass of rendering.
+  virtual HDC getBitmapDC() = 0;
+
+  // Draws to the given screen DC, if the bitmap DC doesn't exist, this will
+  // temporarily create it. However, if you have created the bitmap DC, it will
+  // be more efficient if you don't free it until after this call so it doesn't
+  // have to be created twice.  If src_rect is null, then the entirety of the
+  // source device will be copied.
+  virtual void drawToHDC(HDC dc, int x, int y, const RECT* src_rect) = 0;
+
+  // Invoke before using GDI functions. See description in platform_device.cc
+  // for specifics.
+  // NOTE: x,y,width and height are relative to the current transform.
+  virtual void prepareForGDI(int x, int y, int width, int height) { }
+
+  // Invoke after using GDI functions. See description in platform_device.cc
+  // for specifics.
+  // NOTE: x,y,width and height are relative to the current transform.
+  virtual void postProcessGDI(int x, int y, int width, int height) { }
+
+  // Sets the opacity of each pixel in the specified region to be opaque.
+  virtual void makeOpaque(int x, int y, int width, int height) { }
+
+  // Call this function to fix the alpha channels before compositing this layer
+  // onto another. Internally, the device uses a special alpha method to work
+  // around problems with Windows. This call will put the values into what
+  // Skia expects, so it can be composited onto other layers.
+  //
+  // After this call, no more drawing can be done because the
+  // alpha channels will be "correct", which, if this function is called again
+  // will make them wrong. See the implementation for more discussion.
+  virtual void fixupAlphaBeforeCompositing() { }
+
+  // Returns if the preferred rendering engine is vectorial or bitmap based.
+  virtual bool IsVectorial() = 0;
+
+  // Initializes the default settings and colors in a device context.
+  static void InitializeDC(HDC context);
+
+  // Loads a SkPath into the GDI context. The path can there after be used for
+  // clipping or as a stroke.
+  static void LoadPathToDC(HDC context, const SkPath& path);
+
+  // Loads a SkRegion into the GDI context.
+  static void LoadClippingRegionToDC(HDC context, const SkRegion& region,
+                                     const SkMatrix& transformation);
+
+ protected:
+  // Arrays must be inside structures.
+  struct CubicPoints {
+    SkPoint p[4];
+  };
+  typedef std::vector<CubicPoints> CubicPath;
+  typedef std::vector<CubicPath> CubicPaths;
+
+  // Forwards |bitmap| to SkDevice's constructor.
+  PlatformDevice(const SkBitmap& bitmap);
+
+  // Loads the specified Skia transform into the device context, excluding
+  // perspective (which GDI doesn't support).
+  static void LoadTransformToDC(HDC dc, const SkMatrix& matrix);
+
+  // Transforms SkPath's paths into a series of cubic path.
+  static bool SkPathToCubicPaths(CubicPaths* paths, const SkPath& skpath);
+};
+
+}  // namespace gfx
+
+#endif  // BASE_GFX_PLATFORM_DEVICE_H__
diff --git a/base/gfx/png_codec_unittest.cc b/base/gfx/png_codec_unittest.cc
new file mode 100644
index 0000000..1b510aa
--- /dev/null
+++ b/base/gfx/png_codec_unittest.cc
@@ -0,0 +1,223 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <math.h>
+
+#include "base/gfx/png_encoder.h"
+#include "base/gfx/png_decoder.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+static void MakeRGBImage(int w, int h, std::vector<unsigned char>* dat) {
+  dat->resize(w * h * 3);
+  for (int y = 0; y < h; y++) {
+    for (int x = 0; x < w; x++) {
+      unsigned char* org_px = &(*dat)[(y * w + x) * 3];
+      org_px[0] = x * 3;      // r
+      org_px[1] = x * 3 + 1;  // g
+      org_px[2] = x * 3 + 2;  // b
+    }
+  }
+}
+
+// Set use_transparency to write data into the alpha channel, otherwise it will
+// be filled with 0xff. With the alpha channel stripped, this should yield the
+// same image as MakeRGBImage above, so the code below can make reference
+// images for conversion testing.
+static void MakeRGBAImage(int w, int h, bool use_transparency,
+                          std::vector<unsigned char>* dat) {
+  dat->resize(w * h * 4);
+  for (int y = 0; y < h; y++) {
+    for (int x = 0; x < w; x++) {
+      unsigned char* org_px = &(*dat)[(y * w + x) * 4];
+      org_px[0] = x * 3;      // r
+      org_px[1] = x * 3 + 1;  // g
+      org_px[2] = x * 3 + 2;  // b
+      if (use_transparency)
+        org_px[3] = x*3 + 3;  // a
+      else
+        org_px[3] = 0xFF;     // a (opaque)
+    }
+  }
+}
+
+TEST(PNGCodec, EncodeDecodeRGB) {
+  const int w = 20, h = 20;
+
+  // create an image with known values
+  std::vector<unsigned char> original;
+  MakeRGBImage(w, h, &original);
+
+  // encode
+  std::vector<unsigned char> encoded;
+  EXPECT_TRUE(PNGEncoder::Encode(&original[0], PNGEncoder::FORMAT_RGB, w, h,
+                               w * 3, false, &encoded));
+
+  // decode, it should have the same size as the original
+  std::vector<unsigned char> decoded;
+  int outw, outh;
+  EXPECT_TRUE(PNGDecoder::Decode(&encoded[0], encoded.size(),
+                               PNGDecoder::FORMAT_RGB, &decoded,
+                               &outw, &outh));
+  ASSERT_EQ(w, outw);
+  ASSERT_EQ(h, outh);
+  ASSERT_EQ(original.size(), decoded.size());
+
+  // Images must be equal
+  ASSERT_TRUE(original == decoded);
+}
+
+TEST(PNGCodec, EncodeDecodeRGBA) {
+  const int w = 20, h = 20;
+
+  // create an image with known values, a must be opaque because it will be
+  // lost during encoding
+  std::vector<unsigned char> original;
+  MakeRGBAImage(w, h, true, &original);
+
+  // encode
+  std::vector<unsigned char> encoded;
+  EXPECT_TRUE(PNGEncoder::Encode(&original[0], PNGEncoder::FORMAT_RGBA, w, h,
+                               w * 4, false, &encoded));
+
+  // decode, it should have the same size as the original
+  std::vector<unsigned char> decoded;
+  int outw, outh;
+  EXPECT_TRUE(PNGDecoder::Decode(&encoded[0], encoded.size(),
+                               PNGDecoder::FORMAT_RGBA, &decoded,
+                               &outw, &outh));
+  ASSERT_EQ(w, outw);
+  ASSERT_EQ(h, outh);
+  ASSERT_EQ(original.size(), decoded.size());
+
+  // Images must be exactly equal
+  ASSERT_TRUE(original == decoded);
+}
+
+// Test that corrupted data decompression causes failures.
+TEST(PNGCodec, DecodeCorrupted) {
+  int w = 20, h = 20;
+
+  // Make some random data (an uncompressed image).
+  std::vector<unsigned char> original;
+  MakeRGBImage(w, h, &original);
+
+  // It should fail when given non-JPEG compressed data.
+  std::vector<unsigned char> output;
+  int outw, outh;
+  EXPECT_FALSE(PNGDecoder::Decode(&original[0], original.size(),
+                                PNGDecoder::FORMAT_RGB, &output,
+                                &outw, &outh));
+
+  // Make some compressed data.
+  std::vector<unsigned char> compressed;
+  EXPECT_TRUE(PNGEncoder::Encode(&original[0], PNGEncoder::FORMAT_RGB, w, h,
+                               w * 3, false, &compressed));
+
+  // Try decompressing a truncated version.
+  EXPECT_FALSE(PNGDecoder::Decode(&compressed[0], compressed.size() / 2,
+                                PNGDecoder::FORMAT_RGB, &output,
+                                &outw, &outh));
+
+  // Corrupt it and try decompressing that.
+  for (int i = 10; i < 30; i++)
+    compressed[i] = i;
+  EXPECT_FALSE(PNGDecoder::Decode(&compressed[0], compressed.size(),
+                                PNGDecoder::FORMAT_RGB, &output,
+                                &outw, &outh));
+}
+
+TEST(PNGCodec, EncodeDecodeBGRA) {
+  const int w = 20, h = 20;
+
+  // Create an image with known values, alpha must be opaque because it will be
+  // lost during encoding.
+  std::vector<unsigned char> original;
+  MakeRGBAImage(w, h, true, &original);
+
+  // Encode.
+  std::vector<unsigned char> encoded;
+  EXPECT_TRUE(PNGEncoder::Encode(&original[0], PNGEncoder::FORMAT_BGRA, w, h,
+                               w * 4, false, &encoded));
+
+  // Decode, it should have the same size as the original.
+  std::vector<unsigned char> decoded;
+  int outw, outh;
+  EXPECT_TRUE(PNGDecoder::Decode(&encoded[0], encoded.size(),
+                               PNGDecoder::FORMAT_BGRA, &decoded,
+                               &outw, &outh));
+  ASSERT_EQ(w, outw);
+  ASSERT_EQ(h, outh);
+  ASSERT_EQ(original.size(), decoded.size());
+
+  // Images must be exactly equal.
+  ASSERT_TRUE(original == decoded);
+}
+
+TEST(PNGCodec, StripAddAlpha) {
+  const int w = 20, h = 20;
+
+  // These should be the same except one has a 0xff alpha channel.
+  std::vector<unsigned char> original_rgb;
+  MakeRGBImage(w, h, &original_rgb);
+  std::vector<unsigned char> original_rgba;
+  MakeRGBAImage(w, h, false, &original_rgba);
+
+  // Encode RGBA data as RGB.
+  std::vector<unsigned char> encoded;
+  EXPECT_TRUE(PNGEncoder::Encode(&original_rgba[0], PNGEncoder::FORMAT_RGBA, w, h,
+                               w * 4, true, &encoded));
+
+  // Decode the RGB to RGBA.
+  std::vector<unsigned char> decoded;
+  int outw, outh;
+  EXPECT_TRUE(PNGDecoder::Decode(&encoded[0], encoded.size(),
+                               PNGDecoder::FORMAT_RGBA, &decoded,
+                               &outw, &outh));
+
+  // Decoded and reference should be the same (opaque alpha).
+  ASSERT_EQ(w, outw);
+  ASSERT_EQ(h, outh);
+  ASSERT_EQ(original_rgba.size(), decoded.size());
+  ASSERT_TRUE(original_rgba == decoded);
+
+  // Encode RGBA to RGBA.
+  EXPECT_TRUE(PNGEncoder::Encode(&original_rgba[0], PNGEncoder::FORMAT_RGBA, w, h,
+                               w * 4, false, &encoded));
+
+  // Decode the RGBA to RGB.
+  EXPECT_TRUE(PNGDecoder::Decode(&encoded[0], encoded.size(),
+                               PNGDecoder::FORMAT_RGB, &decoded,
+                               &outw, &outh));
+
+  // It should be the same as our non-alpha-channel reference.
+  ASSERT_EQ(w, outw);
+  ASSERT_EQ(h, outh);
+  ASSERT_EQ(original_rgb.size(), decoded.size());
+  ASSERT_TRUE(original_rgb == decoded);
+}
diff --git a/base/gfx/png_decoder.cc b/base/gfx/png_decoder.cc
new file mode 100644
index 0000000..40ba2e8
--- /dev/null
+++ b/base/gfx/png_decoder.cc
@@ -0,0 +1,376 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/gfx/png_decoder.h"
+
+#include "base/logging.h"
+#include "skia/include/SkBitmap.h"
+
+extern "C" {
+#include "png.h"
+}
+
+namespace {
+
+// Converts BGRA->RGBA and RGBA->BGRA.
+void ConvertBetweenBGRAandRGBA(const unsigned char* input, int pixel_width,
+                               unsigned char* output) {
+  for (int x = 0; x < pixel_width; x++) {
+    const unsigned char* pixel_in = &input[x * 4];
+    unsigned char* pixel_out = &output[x * 4];
+    pixel_out[0] = pixel_in[2];
+    pixel_out[1] = pixel_in[1];
+    pixel_out[2] = pixel_in[0];
+    pixel_out[3] = pixel_in[3];
+  }
+}
+
+void ConvertRGBAtoRGB(const unsigned char* rgba, int pixel_width,
+                      unsigned char* rgb) {
+  for (int x = 0; x < pixel_width; x++) {
+    const unsigned char* pixel_in = &rgba[x * 4];
+    unsigned char* pixel_out = &rgb[x * 3];
+    pixel_out[0] = pixel_in[0];
+    pixel_out[1] = pixel_in[1];
+    pixel_out[2] = pixel_in[2];
+  }
+}
+
+}  // namespace
+
+// Decoder --------------------------------------------------------------------
+//
+// This code is based on WebKit libpng interface (PNGImageDecoder), which is
+// in turn based on the Mozilla png decoder.
+
+namespace {
+
+// Gamma constants: We assume we're on Windows which uses a gamma of 2.2.
+const double kMaxGamma = 21474.83;  // Maximum gamma accepted by png library.
+const double kDefaultGamma = 2.2;
+const double kInverseGamma = 1.0 / kDefaultGamma;
+
+// Maximum pixel dimension we'll try to decode.
+const png_uint_32 kMaxSize = 4096;
+
+class PngDecoderState {
+ public:
+  PngDecoderState(PNGDecoder::ColorFormat ofmt, std::vector<unsigned char>* o)
+      : output_format(ofmt),
+        output_channels(0),
+        output(o),
+        row_converter(NULL),
+        width(0),
+        height(0),
+        done(false) {
+  }
+
+  PNGDecoder::ColorFormat output_format;
+  int output_channels;
+
+  std::vector<unsigned char>* output;
+
+  // Called to convert a row from the library to the correct output format.
+  // When NULL, no conversion is necessary.
+  void (*row_converter)(const unsigned char* in, int w, unsigned char* out);
+
+  // Size of the image, set in the info callback.
+  int width;
+  int height;
+
+  // Set to true when we've found the end of the data.
+  bool done;
+
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(PngDecoderState);
+};
+
+void ConvertRGBtoRGBA(const unsigned char* rgb, int pixel_width,
+                      unsigned char* rgba) {
+  for (int x = 0; x < pixel_width; x++) {
+    const unsigned char* pixel_in = &rgb[x * 3];
+    unsigned char* pixel_out = &rgba[x * 4];
+    pixel_out[0] = pixel_in[0];
+    pixel_out[1] = pixel_in[1];
+    pixel_out[2] = pixel_in[2];
+    pixel_out[3] = 0xff;
+  }
+}
+
+void ConvertRGBtoBGRA(const unsigned char* rgb, int pixel_width,
+                      unsigned char* bgra) {
+  for (int x = 0; x < pixel_width; x++) {
+    const unsigned char* pixel_in = &rgb[x * 3];
+    unsigned char* pixel_out = &bgra[x * 4];
+    pixel_out[0] = pixel_in[2];
+    pixel_out[1] = pixel_in[1];
+    pixel_out[2] = pixel_in[0];
+    pixel_out[3] = 0xff;
+  }
+}
+
+// Called when the png header has been read. This code is based on the WebKit
+// PNGImageDecoder
+void DecodeInfoCallback(png_struct* png_ptr, png_info* info_ptr) {
+  PngDecoderState* state = static_cast<PngDecoderState*>(
+      png_get_progressive_ptr(png_ptr));
+
+  int bit_depth, color_type, interlace_type, compression_type;
+  int filter_type, channels;
+  png_uint_32 w, h;
+  png_get_IHDR(png_ptr, info_ptr, &w, &h, &bit_depth, &color_type,
+               &interlace_type, &compression_type, &filter_type);
+
+  // Bounds check. When the image is unreasonably big, we'll error out and
+  // end up back at the setjmp call when we set up decoding.
+  if (w > kMaxSize || h > kMaxSize)
+    longjmp(png_ptr->jmpbuf, 1);
+  state->width = static_cast<int>(w);
+  state->height = static_cast<int>(h);
+
+  // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA.
+  if (color_type == PNG_COLOR_TYPE_PALETTE ||
+      (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8))
+    png_set_expand(png_ptr);
+
+  // Transparency for paletted images.
+  if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
+    png_set_expand(png_ptr);
+
+  // Convert 16-bit to 8-bit.
+  if (bit_depth == 16)
+    png_set_strip_16(png_ptr);
+
+  // Expand grayscale to RGB.
+  if (color_type == PNG_COLOR_TYPE_GRAY ||
+      color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+    png_set_gray_to_rgb(png_ptr);
+
+  // Deal with gamma and keep it under our control.
+  double gamma;
+  if (png_get_gAMA(png_ptr, info_ptr, &gamma)) {
+    if (gamma <= 0.0 || gamma > kMaxGamma) {
+      gamma = kInverseGamma;
+      png_set_gAMA(png_ptr, info_ptr, gamma);
+    }
+    png_set_gamma(png_ptr, kDefaultGamma, gamma);
+  } else {
+    png_set_gamma(png_ptr, kDefaultGamma, kInverseGamma);
+  }
+
+  // Tell libpng to send us rows for interlaced pngs.
+  if (interlace_type == PNG_INTERLACE_ADAM7)
+    png_set_interlace_handling(png_ptr);
+
+  // Update our info now
+  png_read_update_info(png_ptr, info_ptr);
+  channels = png_get_channels(png_ptr, info_ptr);
+
+  // Pick our row format converter necessary for this data.
+  if (channels == 3) {
+    switch (state->output_format) {
+      case PNGDecoder::FORMAT_RGB:
+        state->row_converter = NULL;  // no conversion necessary
+        state->output_channels = 3;
+        break;
+      case PNGDecoder::FORMAT_RGBA:
+        state->row_converter = &ConvertRGBtoRGBA;
+        state->output_channels = 4;
+        break;
+      case PNGDecoder::FORMAT_BGRA:
+        state->row_converter = &ConvertRGBtoBGRA;
+        state->output_channels = 4;
+        break;
+      default:
+        NOTREACHED() << "Unknown output format";
+        break;
+    }
+  } else if (channels == 4) {
+    switch (state->output_format) {
+      case PNGDecoder::FORMAT_RGB:
+        state->row_converter = &ConvertRGBAtoRGB;
+        state->output_channels = 3;
+        break;
+      case PNGDecoder::FORMAT_RGBA:
+        state->row_converter = NULL;  // no conversion necessary
+        state->output_channels = 4;
+        break;
+      case PNGDecoder::FORMAT_BGRA:
+        state->row_converter = &ConvertBetweenBGRAandRGBA;
+        state->output_channels = 4;
+        break;
+      default:
+        NOTREACHED() << "Unknown output format";
+        break;
+    }
+  } else {
+    NOTREACHED() << "Unknown input channels";
+    longjmp(png_ptr->jmpbuf, 1);
+  }
+
+  state->output->resize(state->width * state->output_channels * state->height);
+}
+
+void DecodeRowCallback(png_struct* png_ptr, png_byte* new_row,
+                       png_uint_32 row_num, int pass) {
+  PngDecoderState* state = static_cast<PngDecoderState*>(
+      png_get_progressive_ptr(png_ptr));
+
+  DCHECK(pass == 0) << "We didn't turn on interlace handling, but libpng is "
+                       "giving us interlaced data.";
+  if (static_cast<int>(row_num) > state->height) {
+    NOTREACHED() << "Invalid row";
+    return;
+  }
+
+  unsigned char* dest = &(*state->output)[
+      state->width * state->output_channels * row_num];
+  if (state->row_converter)
+    state->row_converter(new_row, state->width, dest);
+  else
+    memcpy(dest, new_row, state->width * state->output_channels);
+}
+
+void DecodeEndCallback(png_struct* png_ptr, png_info* info) {
+  PngDecoderState* state = static_cast<PngDecoderState*>(
+      png_get_progressive_ptr(png_ptr));
+
+  // Mark the image as complete, this will tell the Decode function that we
+  // have successfully found the end of the data.
+  state->done = true;
+}
+
+// Automatically destroys the given read structs on destruction to make
+// cleanup and error handling code cleaner.
+class PngReadStructDestroyer {
+ public:
+  PngReadStructDestroyer(png_struct** ps, png_info** pi) : ps_(ps), pi_(pi) {
+  }
+  ~PngReadStructDestroyer() {
+    png_destroy_read_struct(ps_, pi_, NULL);
+  }
+ private:
+  png_struct** ps_;
+  png_info** pi_;
+};
+
+}  // namespace
+
+// static
+bool PNGDecoder::Decode(const unsigned char* input, size_t input_size,
+                      ColorFormat format, std::vector<unsigned char>* output,
+                      int* w, int* h) {
+  if (input_size < 8)
+    return false;  // Input data too small to be a png
+
+  // Have libpng check the signature, it likes the first 8 bytes.
+  if (png_sig_cmp(const_cast<unsigned char*>(input), 0, 8) != 0)
+    return false;
+
+  png_struct* png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
+                                               png_voidp_NULL,
+                                               png_error_ptr_NULL,
+                                               png_error_ptr_NULL);
+  if (!png_ptr)
+    return false;
+
+  png_info* info_ptr = png_create_info_struct(png_ptr);
+  if (!info_ptr) {
+    png_destroy_read_struct(&png_ptr, NULL, NULL);
+    return false;
+  }
+
+  PngReadStructDestroyer destroyer(&png_ptr, &info_ptr);
+  if (setjmp(png_jmpbuf(png_ptr))) {
+    // The destroyer will ensure that the structures are cleaned up in this
+    // case, even though we may get here as a jump from random parts of the
+    // PNG library called below.
+    return false;
+  }
+
+  PngDecoderState state(format, output);
+
+  png_set_progressive_read_fn(png_ptr, &state, &DecodeInfoCallback,
+                              &DecodeRowCallback, &DecodeEndCallback);
+  png_process_data(png_ptr, info_ptr, const_cast<unsigned char*>(input), input_size);
+
+  if (!state.done) {
+    // Fed it all the data but the library didn't think we got all the data, so
+    // this file must be truncated.
+    output->clear();
+    return false;
+  }
+
+  *w = state.width;
+  *h = state.height;
+  return true;
+}
+
+// static
+bool PNGDecoder::Decode(const std::vector<unsigned char>* data,
+                        SkBitmap* bitmap) {
+  DCHECK(bitmap);
+  if (!data || data->empty())
+    return false;
+  int width, height;
+  std::vector<unsigned char> decoded_data;
+  if (PNGDecoder::Decode(&data->front(), data->size(), PNGDecoder::FORMAT_BGRA,
+                         &decoded_data, &width, &height)) {
+    bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
+    bitmap->allocPixels();
+    memcpy(bitmap->getPixels(), &decoded_data.front(), width * height * 4);
+    return true;
+  }
+  return false;
+}
+
+//static
+SkBitmap* PNGDecoder::CreateSkBitmapFromBGRAFormat(
+    std::vector<unsigned char>& bgra, int width, int height) {
+  SkBitmap* bitmap = new SkBitmap();
+  bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
+  bitmap->allocPixels();
+
+  bool opaque = false;
+  unsigned char* bitmap_data =
+      reinterpret_cast<unsigned char*>(bitmap->getAddr32(0, 0));
+  for (int i = width * height * 4 - 4; i >= 0; i -= 4) {
+    unsigned char alpha = bgra[i + 3];
+    if (!opaque && alpha != 255) {
+      opaque = false;
+    }
+    bitmap_data[i + 3] = alpha;
+    bitmap_data[i] = (bgra[i] * alpha) >> 8;
+    bitmap_data[i + 1] = (bgra[i + 1] * alpha) >> 8;
+    bitmap_data[i + 2] = (bgra[i + 2] * alpha) >> 8;
+  }
+
+  bitmap->setIsOpaque(opaque);
+  return bitmap;
+}
diff --git a/base/gfx/png_decoder.h b/base/gfx/png_decoder.h
new file mode 100644
index 0000000..4912187
--- /dev/null
+++ b/base/gfx/png_decoder.h
@@ -0,0 +1,88 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_GFX_PNG_DECODER_H__
+#define BASE_GFX_PNG_DECODER_H__
+
+#include <vector>
+
+#include "base/basictypes.h"
+
+class SkBitmap;
+
+// Interface for decoding PNG data. This is a wrapper around libpng,
+// which has an inconvenient interface for callers. This is currently designed
+// for use in tests only (where we control the files), so the handling isn't as
+// robust as would be required for a browser (see Decode() for more).  WebKit
+// has its own more complicated PNG decoder which handles, among other things,
+// partially downloaded data.
+class PNGDecoder {
+ public:
+  enum ColorFormat {
+    // 3 bytes per pixel (packed), in RGB order regardless of endianness.
+    // This is the native JPEG format.
+    FORMAT_RGB,
+
+    // 4 bytes per pixel, in RGBA order in memory regardless of endianness.
+    FORMAT_RGBA,
+
+    // 4 bytes per pixel, in BGRA order in memory regardless of endianness.
+    // This is the default Windows DIB order.
+    FORMAT_BGRA
+  };
+
+  // Decodes the PNG data contained in input of length input_size. The
+  // decoded data will be placed in *output with the dimensions in *w and *h
+  // on success (returns true). This data will be written in the 'format'
+  // format. On failure, the values of these output variables are undefined.
+  //
+  // This function may not support all PNG types, and it hasn't been tested
+  // with a large number of images, so assume a new format may not work. It's
+  // really designed to be able to read in something written by Encode() above.
+  static bool Decode(const unsigned char* input, size_t input_size,
+                     ColorFormat format, std::vector<unsigned char>* output,
+                     int* w, int* h);
+
+  // A convenience function for decoding PNGs as previously encoded by the PNG
+  // encoder. Chrome encodes png in the format PNGDecoder::FORMAT_BGRA.
+  //
+  // Returns true if data is non-null and can be decoded as a png, false
+  // otherwise.
+  static bool Decode(const std::vector<unsigned char>* data, SkBitmap* icon);
+
+  // Create a SkBitmap from a decoded BGRA DIB. The caller owns the returned
+  // SkBitmap.
+  static SkBitmap* CreateSkBitmapFromBGRAFormat(
+      std::vector<unsigned char>& bgra, int width, int height);
+
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(PNGDecoder);
+};
+
+#endif  // BASE_GFX_PNG_DECODER_H__
diff --git a/base/gfx/png_encoder.cc b/base/gfx/png_encoder.cc
new file mode 100644
index 0000000..ee31a2c
--- /dev/null
+++ b/base/gfx/png_encoder.cc
@@ -0,0 +1,217 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/basictypes.h"
+#include "base/gfx/png_encoder.h"
+#include "base/logging.h"
+
+extern "C" {
+#include "png.h"
+}
+
+namespace {
+
+// Converts BGRA->RGBA and RGBA->BGRA.
+void ConvertBetweenBGRAandRGBA(const unsigned char* input, int pixel_width,
+                               unsigned char* output) {
+  for (int x = 0; x < pixel_width; x++) {
+    const unsigned char* pixel_in = &input[x * 4];
+    unsigned char* pixel_out = &output[x * 4];
+    pixel_out[0] = pixel_in[2];
+    pixel_out[1] = pixel_in[1];
+    pixel_out[2] = pixel_in[0];
+    pixel_out[3] = pixel_in[3];
+  }
+}
+
+void ConvertRGBAtoRGB(const unsigned char* rgba, int pixel_width,
+                      unsigned char* rgb) {
+  for (int x = 0; x < pixel_width; x++) {
+    const unsigned char* pixel_in = &rgba[x * 4];
+    unsigned char* pixel_out = &rgb[x * 3];
+    pixel_out[0] = pixel_in[0];
+    pixel_out[1] = pixel_in[1];
+    pixel_out[2] = pixel_in[2];
+  }
+}
+
+}  // namespace
+
+// Encoder --------------------------------------------------------------------
+//
+// This section of the code is based on nsPNGEncoder.cpp in Mozilla
+// (Copyright 2005 Google Inc.)
+
+namespace {
+
+// Passed around as the io_ptr in the png structs so our callbacks know where
+// to write data.
+struct PngEncoderState {
+  PngEncoderState(std::vector<unsigned char>* o) : out(o) {}
+  std::vector<unsigned char>* out;
+};
+
+// Called by libpng to flush its internal buffer to ours.
+void EncoderWriteCallback(png_structp png, png_bytep data, png_size_t size) {
+  PngEncoderState* state = static_cast<PngEncoderState*>(png_get_io_ptr(png));
+  DCHECK(state->out);
+
+  size_t old_size = state->out->size();
+  state->out->resize(old_size + size);
+  memcpy(&(*state->out)[old_size], data, size);
+}
+
+void ConvertBGRAtoRGB(const unsigned char* bgra, int pixel_width,
+                      unsigned char* rgb) {
+  for (int x = 0; x < pixel_width; x++) {
+    const unsigned char* pixel_in = &bgra[x * 4];
+    unsigned char* pixel_out = &rgb[x * 3];
+    pixel_out[0] = pixel_in[2];
+    pixel_out[1] = pixel_in[1];
+    pixel_out[2] = pixel_in[0];
+  }
+}
+
+// Automatically destroys the given write structs on destruction to make
+// cleanup and error handling code cleaner.
+class PngWriteStructDestroyer {
+ public:
+  PngWriteStructDestroyer(png_struct** ps, png_info** pi) : ps_(ps), pi_(pi) {
+  }
+  ~PngWriteStructDestroyer() {
+    png_destroy_write_struct(ps_, pi_);
+  }
+ private:
+  png_struct** ps_;
+  png_info** pi_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(PngWriteStructDestroyer);
+};
+
+}  // namespace
+
+// static
+bool PNGEncoder::Encode(const unsigned char* input, ColorFormat format,
+                      int w, int h, int row_byte_width,
+                      bool discard_transparency,
+                      std::vector<unsigned char>* output) {
+  // Run to convert an input row into the output row format, NULL means no
+  // conversion is necessary.
+  void (*converter)(const unsigned char* in, int w, unsigned char* out) = NULL;
+
+  int input_color_components, output_color_components;
+  int png_output_color_type;
+  switch (format) {
+    case FORMAT_RGB:
+      input_color_components = 3;
+      output_color_components = 3;
+      png_output_color_type = PNG_COLOR_TYPE_RGB;
+      discard_transparency = false;
+      break;
+
+    case FORMAT_RGBA:
+      input_color_components = 4;
+      if (discard_transparency) {
+        output_color_components = 3;
+        png_output_color_type = PNG_COLOR_TYPE_RGB;
+        converter = ConvertRGBAtoRGB;
+      } else {
+        output_color_components = 4;
+        png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+        converter = NULL;
+      }
+      break;
+
+    case FORMAT_BGRA:
+      input_color_components = 4;
+      if (discard_transparency) {
+        output_color_components = 3;
+        png_output_color_type = PNG_COLOR_TYPE_RGB;
+        converter = ConvertBGRAtoRGB;
+      } else {
+        output_color_components = 4;
+        png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+        converter = ConvertBetweenBGRAandRGBA;
+      }
+      break;
+
+    default:
+      NOTREACHED() << "Unknown pixel format";
+      return false;
+  }
+
+  // Row stride should be at least as long as the length of the data.
+  DCHECK(input_color_components * w <= row_byte_width);
+
+  png_struct* png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
+                                                png_voidp_NULL,
+                                                png_error_ptr_NULL,
+                                                png_error_ptr_NULL);
+  if (!png_ptr)
+    return false;
+  png_info* info_ptr = png_create_info_struct(png_ptr);
+  if (!info_ptr) {
+    png_destroy_write_struct(&png_ptr, NULL);
+    return false;
+  }
+  PngWriteStructDestroyer destroyer(&png_ptr, &info_ptr);
+
+  if (setjmp(png_jmpbuf(png_ptr))) {
+    // The destroyer will ensure that the structures are cleaned up in this
+    // case, even though we may get here as a jump from random parts of the
+    // PNG library called below.
+    return false;
+  }
+
+  // Set our callback for libpng to give us the data.
+  PngEncoderState state(output);
+  png_set_write_fn(png_ptr, &state, EncoderWriteCallback, NULL);
+
+  png_set_IHDR(png_ptr, info_ptr, w, h, 8, png_output_color_type,
+               PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
+               PNG_FILTER_TYPE_DEFAULT);
+  png_write_info(png_ptr, info_ptr);
+
+  if (!converter) {
+    // No conversion needed, give the data directly to libpng.
+    for (int y = 0; y < h; y ++)
+      png_write_row(png_ptr, const_cast<unsigned char*>(&input[y * row_byte_width]));
+  } else {
+    // Needs conversion using a separate buffer.
+    unsigned char* row = new unsigned char[w * output_color_components];
+    for (int y = 0; y < h; y ++) {
+      converter(&input[y * row_byte_width], w, row);
+      png_write_row(png_ptr, row);
+    }
+    delete[] row;
+  }
+
+  png_write_end(png_ptr, info_ptr);
+  return true;
+}
diff --git a/base/gfx/png_encoder.h b/base/gfx/png_encoder.h
new file mode 100644
index 0000000..3129775
--- /dev/null
+++ b/base/gfx/png_encoder.h
@@ -0,0 +1,83 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_GFX_PNG_ENCODER_H__
+#define BASE_GFX_PNG_ENCODER_H__
+
+#include <vector>
+
+#include "base/basictypes.h"
+
+// Interface for encoding PNG data. This is a wrapper around libpng,
+// which has an inconvenient interface for callers. This is currently designed
+// for use in tests only (where we control the files), so the handling isn't as
+// robust as would be required for a browser (see Decode() for more).  WebKit
+// has its own more complicated PNG decoder which handles, among other things,
+// partially downloaded data.
+class PNGEncoder {
+ public:
+  enum ColorFormat {
+    // 3 bytes per pixel (packed), in RGB order regardless of endianness.
+    // This is the native JPEG format.
+    FORMAT_RGB,
+
+    // 4 bytes per pixel, in RGBA order in memory regardless of endianness.
+    FORMAT_RGBA,
+
+    // 4 bytes per pixel, in BGRA order in memory regardless of endianness.
+    // This is the default Windows DIB order.
+    FORMAT_BGRA
+  };
+
+  // Encodes the given raw 'input' data, with each pixel being represented as
+  // given in 'format'. The encoded PNG data will be written into the supplied
+  // vector and true will be returned on success. On failure (false), the
+  // contents of the output buffer are undefined.
+  //
+  // When writing alpha values, the input colors are assumed to be post
+  // multiplied.
+  //
+  // w, h: dimensions of the image
+  // row_byte_width: the width in bytes of each row. This may be greater than
+  //   w * bytes_per_pixel if there is extra padding at the end of each row
+  //   (often, each row is padded to the next machine word).
+  // discard_transparency: when true, and when the input data format includes
+  //   alpha values, these alpha values will be discarded and only RGB will be
+  //   written to the resulting file. Otherwise, alpha values in the input
+  //   will be preserved.
+  static bool Encode(const unsigned char* input, ColorFormat format,
+                     int w, int h, int row_byte_width,
+                     bool discard_transparency,
+                     std::vector<unsigned char>* output);
+
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(PNGEncoder);
+};
+
+#endif  // BASE_GFX_PNG_ENCODER_H__
diff --git a/base/gfx/point.cc b/base/gfx/point.cc
new file mode 100644
index 0000000..7c435a0
--- /dev/null
+++ b/base/gfx/point.cc
@@ -0,0 +1,52 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/gfx/point.h"
+
+#include <windows.h>
+
+namespace gfx {
+
+Point::Point() : x_(0), y_(0) {
+}
+
+Point::Point(int x, int y) : x_(x), y_(y) {
+}
+
+Point::Point(const POINT& point) : x_(point.x), y_(point.y) {
+}
+
+POINT Point::ToPOINT() const {
+  POINT p;
+  p.x = x_;
+  p.y = y_;
+  return p;
+}
+
+}  // namespace gfx
diff --git a/base/gfx/point.h b/base/gfx/point.h
new file mode 100644
index 0000000..3e09a81
--- /dev/null
+++ b/base/gfx/point.h
@@ -0,0 +1,88 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_GFX_POINT_H__
+#define BASE_GFX_POINT_H__
+
+#ifdef UNIT_TEST
+#include <iostream>
+#endif
+
+typedef struct tagPOINT POINT;
+
+namespace gfx {
+
+//
+// A point has an x and y coordinate.
+//
+class Point {
+ public:
+  Point();
+  Point(int x, int y);
+  explicit Point(const POINT& point);
+
+  ~Point() {}
+
+  int x() const { return x_; }
+  int y() const { return y_; }
+
+  void SetPoint(int x, int y) {
+    x_ = x;
+    y_ = y;
+  }
+
+  void set_x(int x) { x_ = x; }
+  void set_y(int y) { y_ = y; }
+
+  bool operator==(const Point& rhs) const {
+    return x_ == rhs.x_ && y_ == rhs.y_;
+  }
+
+  bool operator!=(const Point& rhs) const {
+    return !(*this == rhs);
+  }
+
+  POINT ToPOINT() const;
+
+ private:
+  int x_;
+  int y_;
+};
+
+}  // namespace gfx
+
+#ifdef UNIT_TEST
+
+inline std::ostream& operator<<(std::ostream& out, const gfx::Point& p) {
+  return out << p.x() << "," << p.y();
+}
+
+#endif  // #ifdef UNIT_TEST
+
+#endif // BASE_GFX_POINT_H__
diff --git a/base/gfx/rect.cc b/base/gfx/rect.cc
new file mode 100644
index 0000000..dda7e67
--- /dev/null
+++ b/base/gfx/rect.cc
@@ -0,0 +1,217 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/gfx/rect.h"
+
+#include <windows.h>
+
+#include "base/logging.h"
+
+namespace {
+
+void AdjustAlongAxis(int dst_origin, int dst_size, int* origin, int* size) {
+  if (*origin < dst_origin) {
+    *origin = dst_origin;
+    *size = std::min(dst_size, *size);
+  } else {
+    *size = std::min(dst_size, *size);
+    *origin = std::min(dst_origin + dst_size, *origin + *size) - *size;
+  }
+}
+
+} // namespace
+
+namespace gfx {
+
+Rect::Rect() {
+}
+
+Rect::Rect(int width, int height) {
+  set_width(width);
+  set_height(height);
+}
+
+Rect::Rect(int x, int y, int width, int height)
+    : origin_(x, y) {
+  set_width(width);
+  set_height(height);
+}
+
+Rect::Rect(const RECT& r)
+    : origin_(r.left, r.top) {
+  set_width(r.right - r.left);
+  set_height(r.bottom - r.top);
+}
+
+Rect& Rect::operator=(const RECT& r) {
+  origin_.SetPoint(r.left, r.top);
+  set_width(r.right - r.left);
+  set_height(r.bottom - r.top);
+  return *this;
+}
+
+void Rect::set_width(int width) {
+  if (width < 0) {
+    NOTREACHED();
+    width = 0;
+  }
+
+  size_.set_width(width);
+}
+void Rect::set_height(int height) {
+  if (height < 0) {
+    NOTREACHED();
+    height = 0;
+  }
+
+  size_.set_height(height);
+}
+
+void Rect::SetRect(int x, int y, int width, int height) {
+  origin_.SetPoint(x, y);
+  set_width(width);
+  set_height(height);
+}
+
+void Rect::Inset(int horizontal, int vertical) {
+  set_x(x() + horizontal);
+  set_y(y() + vertical);
+  set_width(std::max(width() - (horizontal * 2), 0));
+  set_height(std::max(height() - (vertical * 2), 0));
+}
+
+void Rect::Offset(int horizontal, int vertical) {
+  set_x(x() + horizontal);
+  set_y(y() + vertical);
+}
+
+bool Rect::IsEmpty() const {
+  return width() == 0 || height() == 0;
+}
+
+bool Rect::operator==(const Rect& other) const {
+  return origin_ == other.origin_ && size_ == other.size_;
+}
+
+RECT Rect::ToRECT() const {
+  RECT r;
+  r.left = x();
+  r.right = right();
+  r.top = y();
+  r.bottom = bottom();
+  return r;
+}
+
+bool Rect::Contains(int point_x, int point_y) const {
+  return (point_x >= x()) && (point_x < right()) &&
+         (point_y >= y()) && (point_y < bottom());
+}
+
+bool Rect::Contains(const Rect& rect) const {
+  return (rect.x() >= x() && rect.right() <= right() &&
+          rect.y() >= y() && rect.bottom() <= bottom());
+}
+
+bool Rect::Intersects(const Rect& rect) const {
+  return !(rect.x() >= right() || rect.right() <= x() ||
+           rect.y() >= bottom() || rect.bottom() <= y());
+}
+
+Rect Rect::Intersect(const Rect& rect) const {
+  int rx = std::max(x(), rect.x());
+  int ry = std::max(y(), rect.y());
+  int rr = std::min(right(), rect.right());
+  int rb = std::min(bottom(), rect.bottom());
+
+  if (rx >= rr || ry >= rb)
+    rx = ry = rr = rb = 0;  // non-intersecting
+
+  return Rect(rx, ry, rr - rx, rb - ry);
+}
+
+Rect Rect::Union(const Rect& rect) const {
+  // special case empty rects...
+  if (IsEmpty())
+    return rect;
+  if (rect.IsEmpty())
+    return *this;
+
+  int rx = std::min(x(), rect.x());
+  int ry = std::min(y(), rect.y());
+  int rr = std::max(right(), rect.right());
+  int rb = std::max(bottom(), rect.bottom());
+
+  return Rect(rx, ry, rr - rx, rb - ry);
+}
+
+Rect Rect::Subtract(const Rect& rect) const {
+  // boundary cases:
+  if (!Intersects(rect))
+    return *this;
+  if (rect.Contains(*this))
+    return Rect();
+
+  int rx = x();
+  int ry = y();
+  int rr = right();
+  int rb = bottom();
+
+  if (rect.y() <= y() && rect.bottom() >= bottom()) {
+    // complete intersection in the y-direction
+    if (rect.x() <= x()) {
+      rx = rect.right();
+    } else {
+      rr = rect.x();
+    }
+  } else if (rect.x() <= x() && rect.right() >= right()) {
+    // complete intersection in the x-direction
+    if (rect.y() <= y()) {
+      ry = rect.bottom();
+    } else {
+      rb = rect.y();
+    }
+  }
+  return Rect(rx, ry, rr - rx, rb - ry);
+}
+
+Rect Rect::AdjustToFit(const Rect& rect) const {
+  int new_x = x();
+  int new_y = y();
+  int new_width = width();
+  int new_height = height();
+  AdjustAlongAxis(rect.x(), rect.width(), &new_x, &new_width);
+  AdjustAlongAxis(rect.y(), rect.height(), &new_y, &new_height);
+  return Rect(new_x, new_y, new_width, new_height);
+}
+
+Point Rect::CenterPoint() const {
+  return Point(x() + (width() + 1) / 2, y() + (height() + 1) / 2);
+}
+
+}  // namespace gfx
diff --git a/base/gfx/rect.h b/base/gfx/rect.h
new file mode 100644
index 0000000..486f799
--- /dev/null
+++ b/base/gfx/rect.h
@@ -0,0 +1,153 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Defines a simple integer rectangle class.  The containment semantics
+// are array-like; that is, the coordinate (x, y) is considered to be
+// contained by the rectangle, but the coordinate (x + width, y) is not.
+// The class will happily let you create malformed rectangles (that is,
+// rectangles with negative width and/or height), but there will be assertions
+// in the operations (such as contain()) to complain in this case.
+
+#ifndef BASE_GFX_RECT_H__
+#define BASE_GFX_RECT_H__
+
+#include "base/gfx/size.h"
+#include "base/gfx/point.h"
+
+typedef struct tagRECT RECT;
+
+namespace gfx {
+
+class Rect {
+ public:
+  Rect();
+  Rect(int width, int height);
+  Rect(int x, int y, int width, int height);
+  explicit Rect(const RECT& r);
+  Rect(const gfx::Point& origin, const gfx::Size& size);
+
+  ~Rect() {}
+
+  Rect& operator=(const RECT& r);
+
+  int x() const { return origin_.x(); }
+  void set_x(int x) { origin_.set_x(x); }
+
+  int y() const { return origin_.y(); }
+  void set_y(int y) { origin_.set_y(y); }
+
+  int width() const { return size_.width(); }
+  void set_width(int width);
+
+  int height() const { return size_.height(); }
+  void set_height(int height);
+
+  const gfx::Point& origin() const { return origin_; }
+  void set_origin(const gfx::Point& origin) { origin_ = origin; }
+
+  const gfx::Size& size() const { return size_; }
+
+  int right() const { return x() + width(); }
+  int bottom() const { return y() + height(); }
+
+  void SetRect(int x, int y, int width, int height);
+
+  // Shrink the rectangle by a horizontal and vertical distance on all sides.
+  void Inset(int horizontal, int vertical);
+
+  // Move the rectangle by a horizontal and vertical distance.
+  void Offset(int horizontal, int vertical);
+
+  // Returns true if the area of the rectangle is zero.
+  bool IsEmpty() const;
+
+  bool operator==(const Rect& other) const;
+
+  bool operator!=(const Rect& other) const {
+    return !(*this == other);
+  }
+
+  // Construct an equivalent Win32 RECT object.
+  RECT ToRECT() const;
+
+  // Returns true if the point identified by point_x and point_y falls inside
+  // this rectangle.  The point (x, y) is inside the rectangle, but the
+  // point (x + width, y + height) is not.
+  bool Contains(int point_x, int point_y) const;
+
+  // Returns true if this rectangle contains the specified rectangle.
+  bool Contains(const Rect& rect) const;
+
+  // Returns true if this rectangle intersects the specified rectangle.
+  bool Intersects(const Rect& rect) const;
+
+  // Computes the intersection of this rectangle with the given rectangle.
+  Rect Intersect(const Rect& rect) const;
+
+  // Computes the union of this rectangle with the given rectangle.  The union
+  // is the smallest rectangle containing both rectangles.
+  Rect Union(const Rect& rect) const;
+
+  // Computes the rectangle resulting from subtracting |rect| from |this|.  If
+  // |rect| does not intersect completely in either the x- or y-direction, then
+  // |*this| is returned.  If |rect| contains |this|, then an empty Rect is
+  // returned.
+  Rect Subtract(const Rect& rect) const;
+
+  // Returns true if this rectangle equals that of the supplied rectangle.
+  bool Equals(const Rect& rect) const {
+    return *this == rect;
+  }
+
+  // Fits as much of the receiving rectangle into the supplied rectangle as
+  // possible, returning the result. For example, if the receiver had
+  // a x-location of 2 and a width of 4, and the supplied rectangle had
+  // an x-location of 0 with a width of 5, the returned rectangle would have
+  // an x-location of 1 with a width of 4.
+  Rect AdjustToFit(const Rect& rect) const;
+
+  // Returns the center of this rectangle.
+  Point CenterPoint() const;
+
+ private:
+  gfx::Point origin_;
+  gfx::Size size_;
+};
+
+}  // namespace gfx
+
+#ifdef UNIT_TEST
+
+inline std::ostream& operator<<(std::ostream& out, const gfx::Rect& r) {
+  return out << r.origin() << " " << r.size();
+}
+
+#endif  // #ifdef UNIT_TEST
+
+#endif  // BASE_GFX_RECT_H__
diff --git a/base/gfx/rect_unittest.cc b/base/gfx/rect_unittest.cc
new file mode 100644
index 0000000..c218cc9
--- /dev/null
+++ b/base/gfx/rect_unittest.cc
@@ -0,0 +1,295 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/basictypes.h"
+#include "base/gfx/rect.h"
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+typedef testing::Test RectTest;
+
+TEST(RectTest, Contains) {
+  static const struct ContainsCase {
+    int rect_x;
+    int rect_y;
+    int rect_width;
+    int rect_height;
+    int point_x;
+    int point_y;
+    bool contained;
+  } contains_cases[] = {
+    {0, 0, 10, 10, 0, 0, true},
+    {0, 0, 10, 10, 5, 5, true},
+    {0, 0, 10, 10, 9, 9, true},
+    {0, 0, 10, 10, 5, 10, false},
+    {0, 0, 10, 10, 10, 5, false},
+    {0, 0, 10, 10, -1, -1, false},
+    {0, 0, 10, 10, 50, 50, false},
+  #ifdef NDEBUG
+    {0, 0, -10, -10, 0, 0, false},
+  #endif  // NDEBUG
+  };
+  for (int i = 0; i < arraysize(contains_cases); ++i) {
+    const ContainsCase& value = contains_cases[i];
+    gfx::Rect rect(value.rect_x, value.rect_y,
+                   value.rect_width, value.rect_height);
+    EXPECT_EQ(value.contained, rect.Contains(value.point_x, value.point_y));
+  }
+}
+
+TEST(RectTest, Intersects) {
+  static const struct {
+    int x1;  // rect 1
+    int y1;
+    int w1;
+    int h1;
+    int x2;  // rect 2
+    int y2;
+    int w2;
+    int h2;
+    bool intersects;
+  } tests[] = {
+    { 0, 0, 0, 0, 0, 0, 0, 0, false },
+    { 0, 0, 10, 10, 0, 0, 10, 10, true },
+    { 0, 0, 10, 10, 10, 10, 10, 10, false },
+    { 10, 10, 10, 10, 0, 0, 10, 10, false },
+    { 10, 10, 10, 10, 5, 5, 10, 10, true },
+    { 10, 10, 10, 10, 15, 15, 10, 10, true },
+    { 10, 10, 10, 10, 20, 15, 10, 10, false },
+    { 10, 10, 10, 10, 21, 15, 10, 10, false }
+  };
+  for (size_t i = 0; i < arraysize(tests); ++i) {
+    gfx::Rect r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1);
+    gfx::Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2);
+    EXPECT_EQ(tests[i].intersects, r1.Intersects(r2));
+  }
+}
+
+TEST(RectTest, Intersect) {
+  static const struct {
+    int x1;  // rect 1
+    int y1;
+    int w1;
+    int h1;
+    int x2;  // rect 2
+    int y2;
+    int w2;
+    int h2;
+    int x3;  // rect 3: the union of rects 1 and 2
+    int y3;
+    int w3;
+    int h3;
+  } tests[] = {
+    { 0, 0, 0, 0,   // zeros
+      0, 0, 0, 0,
+      0, 0, 0, 0 },
+    { 0, 0, 4, 4,   // equal
+      0, 0, 4, 4,
+      0, 0, 4, 4 },
+    { 0, 0, 4, 4,   // neighboring
+      4, 4, 4, 4,
+      0, 0, 0, 0 },
+    { 0, 0, 4, 4,   // overlapping corners
+      2, 2, 4, 4,
+      2, 2, 2, 2 },
+    { 0, 0, 4, 4,   // T junction
+      3, 1, 4, 2,
+      3, 1, 1, 2 },
+    { 3, 0, 2, 2,   // gap
+      0, 0, 2, 2,
+      0, 0, 0, 0 }
+  };
+  for (size_t i = 0; i < arraysize(tests); ++i) {
+    gfx::Rect r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1);
+    gfx::Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2);
+    gfx::Rect r3(tests[i].x3, tests[i].y3, tests[i].w3, tests[i].h3);
+    gfx::Rect ir = r1.Intersect(r2);
+    EXPECT_EQ(r3.x(), ir.x());
+    EXPECT_EQ(r3.y(), ir.y());
+    EXPECT_EQ(r3.width(), ir.width());
+    EXPECT_EQ(r3.height(), ir.height());
+  }
+}
+
+TEST(RectTest, Union) {
+  static const struct {
+    int x1;  // rect 1
+    int y1;
+    int w1;
+    int h1;
+    int x2;  // rect 2
+    int y2;
+    int w2;
+    int h2;
+    int x3;  // rect 3: the union of rects 1 and 2
+    int y3;
+    int w3;
+    int h3;
+  } tests[] = {
+    { 0, 0, 0, 0,
+      0, 0, 0, 0,
+      0, 0, 0, 0 },
+    { 0, 0, 4, 4,
+      0, 0, 4, 4,
+      0, 0, 4, 4 },
+    { 0, 0, 4, 4,
+      4, 4, 4, 4,
+      0, 0, 8, 8 },
+    { 0, 0, 4, 4,
+      0, 5, 4, 4,
+      0, 0, 4, 9 },
+    { 0, 0, 2, 2,
+      3, 3, 2, 2,
+      0, 0, 5, 5 },
+    { 3, 3, 2, 2,   // reverse r1 and r2 from previous test
+      0, 0, 2, 2,
+      0, 0, 5, 5 },
+    { 0, 0, 0, 0,   // union with empty rect
+      2, 2, 2, 2,
+      2, 2, 2, 2 }
+  };
+  for (size_t i = 0; i < arraysize(tests); ++i) {
+    gfx::Rect r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1);
+    gfx::Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2);
+    gfx::Rect r3(tests[i].x3, tests[i].y3, tests[i].w3, tests[i].h3);
+    gfx::Rect u = r1.Union(r2);
+    EXPECT_EQ(r3.x(), u.x());
+    EXPECT_EQ(r3.y(), u.y());
+    EXPECT_EQ(r3.width(), u.width());
+    EXPECT_EQ(r3.height(), u.height());
+  }
+}
+
+TEST(RectTest, Equals) {
+  ASSERT_TRUE(gfx::Rect(0, 0, 0, 0).Equals(gfx::Rect(0, 0, 0, 0)));
+  ASSERT_TRUE(gfx::Rect(1, 2, 3, 4).Equals(gfx::Rect(1, 2, 3, 4)));
+  ASSERT_FALSE(gfx::Rect(0, 0, 0, 0).Equals(gfx::Rect(0, 0, 0, 1)));
+  ASSERT_FALSE(gfx::Rect(0, 0, 0, 0).Equals(gfx::Rect(0, 0, 1, 0)));
+  ASSERT_FALSE(gfx::Rect(0, 0, 0, 0).Equals(gfx::Rect(0, 1, 0, 0)));
+  ASSERT_FALSE(gfx::Rect(0, 0, 0, 0).Equals(gfx::Rect(1, 0, 0, 0)));
+}
+
+TEST(RectTest, AdjustToFit) {
+  static const struct {
+    int x1;  // source
+    int y1;
+    int w1;
+    int h1;
+    int x2;  // target
+    int y2;
+    int w2;
+    int h2;
+    int x3;  // rect 3: results of invoking AdjustToFit
+    int y3;
+    int w3;
+    int h3;
+  } tests[] = {
+    { 0, 0, 2, 2,
+      0, 0, 2, 2,
+      0, 0, 2, 2 },
+    { 2, 2, 3, 3,
+      0, 0, 4, 4,
+      1, 1, 3, 3 },
+    { -1, -1, 5, 5,
+      0, 0, 4, 4,
+      0, 0, 4, 4 },
+    { 2, 2, 4, 4,
+      0, 0, 3, 3,
+      0, 0, 3, 3 },
+    { 2, 2, 1, 1,
+      0, 0, 3, 3,
+      2, 2, 1, 1 }
+  };
+  for (size_t i = 0; i < arraysize(tests); ++i) {
+    gfx::Rect r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1);
+    gfx::Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2);
+    gfx::Rect r3(tests[i].x3, tests[i].y3, tests[i].w3, tests[i].h3);
+    gfx::Rect u(r1.AdjustToFit(r2));
+    EXPECT_EQ(r3.x(), u.x());
+    EXPECT_EQ(r3.y(), u.y());
+    EXPECT_EQ(r3.width(), u.width());
+    EXPECT_EQ(r3.height(), u.height());
+  }
+}
+
+TEST(RectText, Subtract) {
+  // Matching
+  EXPECT_TRUE(
+      gfx::Rect(10, 10, 20, 20).Subtract(
+      gfx::Rect(10, 10, 20, 20)).Equals(
+      gfx::Rect(0, 0, 0, 0)));
+
+  // Contains
+  EXPECT_TRUE(
+      gfx::Rect(10, 10, 20, 20).Subtract(
+      gfx::Rect(5, 5, 30, 30)).Equals(
+      gfx::Rect(0, 0, 0, 0)));
+
+  // No intersection
+  EXPECT_TRUE(
+      gfx::Rect(10, 10, 20, 20).Subtract(
+      gfx::Rect(30, 30, 20, 20)).Equals(
+      gfx::Rect(10, 10, 20, 20)));
+
+  // Not a complete intersection in either direction
+  EXPECT_TRUE(
+      gfx::Rect(10, 10, 20, 20).Subtract(
+      gfx::Rect(15, 15, 20, 20)).Equals(
+      gfx::Rect(10, 10, 20, 20)));
+
+  // Complete intersection in the x-direction
+  EXPECT_TRUE(
+      gfx::Rect(10, 10, 20, 20).Subtract(
+      gfx::Rect(10, 15, 20, 20)).Equals(
+      gfx::Rect(10, 10, 20, 5)));
+
+  // Complete intersection in the x-direction
+  EXPECT_TRUE(
+      gfx::Rect(10, 10, 20, 20).Subtract(
+      gfx::Rect(5, 15, 30, 20)).Equals(
+      gfx::Rect(10, 10, 20, 5)));
+
+  // Complete intersection in the x-direction
+  EXPECT_TRUE(
+      gfx::Rect(10, 10, 20, 20).Subtract(
+      gfx::Rect(5, 5, 30, 20)).Equals(
+      gfx::Rect(10, 25, 20, 5)));
+
+  // Complete intersection in the y-direction
+  EXPECT_TRUE(
+      gfx::Rect(10, 10, 20, 20).Subtract(
+      gfx::Rect(10, 10, 10, 30)).Equals(
+      gfx::Rect(20, 10, 10, 20)));
+
+  // Complete intersection in the y-direction
+  EXPECT_TRUE(
+      gfx::Rect(10, 10, 20, 20).Subtract(
+      gfx::Rect(5, 5, 20, 30)).Equals(
+      gfx::Rect(25, 10, 5, 20)));
+}
diff --git a/base/gfx/size.cc b/base/gfx/size.cc
new file mode 100644
index 0000000..153c29e
--- /dev/null
+++ b/base/gfx/size.cc
@@ -0,0 +1,49 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/gfx/size.h"
+
+#include <windows.h>
+
+#include <ostream>
+
+namespace gfx {
+
+SIZE Size::ToSIZE() const {
+  SIZE s;
+  s.cx = width_;
+  s.cy = height_;
+  return s;
+}
+
+std::ostream& operator<<(std::ostream& out, const gfx::Size& s) {
+  return out << s.width() << "x" << s.height();
+}
+
+}  // namespace gfx
diff --git a/base/gfx/size.h b/base/gfx/size.h
new file mode 100644
index 0000000..cba2e51
--- /dev/null
+++ b/base/gfx/size.h
@@ -0,0 +1,91 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_GFX_SIZE_H__
+#define BASE_GFX_SIZE_H__
+
+#ifdef UNIT_TEST
+#include <iostream>
+#endif
+
+typedef struct tagSIZE SIZE;
+
+namespace gfx {
+
+//
+// A size has width and height values.
+//
+class Size {
+ public:
+  Size() : width_(0), height_(0) {}
+  Size(int width, int height) : width_(width), height_(height) {}
+
+  ~Size() {}
+
+  int width() const { return width_; }
+  int height() const { return height_; }
+
+  void SetSize(int width, int height) {
+    width_ = width;
+    height_ = height;
+  }
+
+  void set_width(int width) { width_ = width; }
+  void set_height(int height) { height_ = height; }
+
+  bool operator==(const Size& s) const {
+    return width_ == s.width_ && height_ == s.height_;
+  }
+
+  bool operator!=(const Size& s) const {
+    return !(*this == s);
+  }
+
+  bool IsEmpty() const {
+    return !width_ && !height_;
+  }
+
+  SIZE ToSIZE() const;
+
+ private:
+  int width_;
+  int height_;
+};
+
+}  // namespace gfx
+
+#ifdef UNIT_TEST
+
+inline std::ostream& operator<<(std::ostream& out, const gfx::Size& s) {
+  return out << s.width() << "x" << s.height();
+}
+
+#endif  // #ifdef UNIT_TEST
+
+#endif // BASE_GFX_SIZE_H__
diff --git a/base/gfx/skia_utils.cc b/base/gfx/skia_utils.cc
new file mode 100644
index 0000000..60e977d
--- /dev/null
+++ b/base/gfx/skia_utils.cc
@@ -0,0 +1,99 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/gfx/skia_utils.h"
+
+#include "base/logging.h"
+#include "SkRect.h"
+#include "SkGradientShader.h"
+
+namespace {
+
+COMPILE_ASSERT(offsetof(RECT, left) == offsetof(SkIRect, fLeft), o1);
+COMPILE_ASSERT(offsetof(RECT, top) == offsetof(SkIRect, fTop), o2);
+COMPILE_ASSERT(offsetof(RECT, right) == offsetof(SkIRect, fRight), o3);
+COMPILE_ASSERT(offsetof(RECT, bottom) == offsetof(SkIRect, fBottom), o4);
+COMPILE_ASSERT(sizeof(RECT().left) == sizeof(SkIRect().fLeft), o5);
+COMPILE_ASSERT(sizeof(RECT().top) == sizeof(SkIRect().fTop), o6);
+COMPILE_ASSERT(sizeof(RECT().right) == sizeof(SkIRect().fRight), o7);
+COMPILE_ASSERT(sizeof(RECT().bottom) == sizeof(SkIRect().fBottom), o8);
+COMPILE_ASSERT(sizeof(RECT) == sizeof(SkIRect), o9);
+
+}  // namespace
+
+namespace gfx {
+
+POINT SkPointToPOINT(const SkPoint& point) {
+  POINT win_point = { SkScalarRound(point.fX), SkScalarRound(point.fY) };
+  return win_point;
+}
+
+SkRect RECTToSkRect(const RECT& rect) {
+  SkRect sk_rect = { SkIntToScalar(rect.left), SkIntToScalar(rect.top),
+                     SkIntToScalar(rect.right), SkIntToScalar(rect.bottom) };
+  return sk_rect;
+}
+
+SkShader* CreateGradientShader(int start_point,
+                               int end_point,
+                               SkColor start_color,
+                               SkColor end_color) {
+  SkColor grad_colors[2] = { start_color, end_color};
+  SkPoint grad_points[2];
+  grad_points[0].set(SkIntToScalar(0), SkIntToScalar(start_point));
+  grad_points[1].set(SkIntToScalar(0), SkIntToScalar(end_point));
+
+  return SkGradientShader::CreateLinear(
+      grad_points, grad_colors, NULL, 2, SkShader::kRepeat_TileMode);
+}
+
+
+SkColor COLORREFToSkColor(COLORREF color) {
+#ifndef _MSC_VER
+  return SkColorSetRGB(GetRValue(color), GetGValue(color), GetBValue(color));
+#else
+  // ARGB = 0xFF000000 | ((0BGR -> RGB0) >> 8)
+  return 0xFF000000u | (_byteswap_ulong(color) >> 8);
+#endif
+}
+
+COLORREF SkColorToCOLORREF(SkColor color) {
+  // Currently, Alpha is always 255 or the color is 0 so there is no need to
+  // demultiply the channels. If this DCHECK() is ever hit, the full
+  // (SkColorGetX(color) * 255 / a) will have to be added in the conversion.
+  DCHECK((0xFF == SkColorGetA(color)) || (0 == color));
+#ifndef _MSC_VER
+  return RGB(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color));
+#else
+  // 0BGR = ((ARGB -> BGRA) >> 8)
+  return (_byteswap_ulong(color) >> 8);
+#endif
+}
+
+}  // namespace gfx
diff --git a/base/gfx/skia_utils.h b/base/gfx/skia_utils.h
new file mode 100644
index 0000000..989a17e
--- /dev/null
+++ b/base/gfx/skia_utils.h
@@ -0,0 +1,80 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_GFX_SKIA_UTILS_H__
+#define BASE_GFX_SKIA_UTILS_H__
+
+#include "SkColor.h"
+#include "SkShader.h"
+
+struct SkIRect;
+struct SkPoint;
+struct SkRect;
+typedef unsigned long DWORD;
+typedef DWORD COLORREF;
+typedef struct tagPOINT POINT;
+typedef struct tagRECT RECT;
+
+namespace gfx {
+
+// Converts a Skia point to a Windows POINT.
+POINT SkPointToPOINT(const SkPoint& point);
+
+// Converts a Windows RECT to a Skia rect.
+SkRect RECTToSkRect(const RECT& rect);
+
+// Converts a Windows RECT to a Skia rect.
+// Both use same in-memory format. Verified by COMPILE_ASSERT() in
+// skia_utils.cc.
+inline const SkIRect& RECTToSkIRect(const RECT& rect) {
+  return reinterpret_cast<const SkIRect&>(rect);
+}
+
+// Converts a Skia rect to a Windows RECT.
+// Both use same in-memory format. Verified by COMPILE_ASSERT() in
+// skia_utils.cc.
+inline const RECT& SkIRectToRECT(const SkIRect& rect) {
+  return reinterpret_cast<const RECT&>(rect);
+}
+
+// Creates a vertical gradient shader. The caller owns the shader.
+SkShader* CreateGradientShader(int start_point,
+                               int end_point,
+                               SkColor start_color,
+                               SkColor end_color);
+
+// Converts COLORREFs (0BGR) to the ARGB layout Skia expects.
+SkColor COLORREFToSkColor(COLORREF color);
+
+// Converts ARGB to COLORREFs (0BGR).
+COLORREF SkColorToCOLORREF(SkColor color);
+
+}  // namespace gfx
+
+#endif
diff --git a/base/gfx/uniscribe.cc b/base/gfx/uniscribe.cc
new file mode 100644
index 0000000..fbfb751
--- /dev/null
+++ b/base/gfx/uniscribe.cc
@@ -0,0 +1,872 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+
+#include "base/gfx/uniscribe.h"
+
+#include "base/gfx/font_utils.h"
+#include "base/logging.h"
+
+namespace gfx {
+
+// This function is used to see where word spacing should be applied inside
+// runs. Note that this must match Font::treatAsSpace so we all agree where
+// and how much space this is, so we don't want to do more general Unicode
+// "is this a word break" thing.
+static bool TreatAsSpace(wchar_t c) {
+ return c == ' ' || c == '\t' || c == '\n' || c == 0x00A0;
+}
+
+// SCRIPT_FONTPROPERTIES contains glyph indices for default, invalid
+// and blank glyphs. Just because ScriptShape succeeds does not mean
+// that a text run is rendered correctly. Some characters may be rendered
+// with default/invalid/blank glyphs. Therefore, we need to check if the glyph
+// array returned by ScriptShape contains any of those glyphs to make
+// sure that the text run is rendered successfully.
+static bool ContainsMissingGlyphs(WORD *glyphs,
+                                  int length,
+                                  SCRIPT_FONTPROPERTIES* properties) {
+  for (int i = 0; i < length; ++i) {
+    if (glyphs[i] == properties->wgDefault ||
+        (glyphs[i] == properties->wgInvalid && glyphs[i] != properties->wgBlank))
+      return true;
+  }
+
+  return false;
+}
+
+// HFONT is the 'incarnation' of 'everything' about font, but it's an opaque
+// handle and we can't directly query it to make a new HFONT sharing
+// its characteristics (height, style, etc) except for family name.
+// This function uses GetObject to convert HFONT back to LOGFONT,
+// resets the fields of LOGFONT and calculates style to use later
+// for the creation of a font identical to HFONT other than family name.
+static void SetLogFontAndStyle(HFONT hfont, LOGFONT *logfont, int *style) {
+  DCHECK(hfont && logfont);
+  if (!hfont || !logfont)
+    return;
+
+  GetObject(hfont, sizeof(LOGFONT), logfont);
+  // We reset these fields to values appropriate for CreateFontIndirect.
+  // while keeping lfHeight, which is the most important value in creating
+  // a new font similar to hfont.
+  logfont->lfWidth = 0;
+  logfont->lfEscapement = 0;
+  logfont->lfOrientation = 0;
+  logfont->lfCharSet = DEFAULT_CHARSET;
+  logfont->lfOutPrecision = OUT_TT_ONLY_PRECIS;
+  logfont->lfQuality = DEFAULT_QUALITY;  // Honor user's desktop settings.
+  logfont->lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
+  if (style)
+    *style = gfx::GetStyleFromLogfont(logfont);
+}
+
+UniscribeState::UniscribeState(const wchar_t* input,
+                               int input_length,
+                               bool is_rtl,
+                               HFONT hfont,
+                               SCRIPT_CACHE* script_cache,
+                               SCRIPT_FONTPROPERTIES* font_properties)
+    : input_(input),
+      input_length_(input_length),
+      is_rtl_(is_rtl),
+      hfont_(hfont),
+      script_cache_(script_cache),
+      font_properties_(font_properties),
+      directional_override_(false),
+      inhibit_ligate_(false),
+      letter_spacing_(0),
+      space_width_(0),
+      word_spacing_(0),
+      ascent_(0) {
+  logfont_.lfFaceName[0] = 0;
+}
+
+UniscribeState::~UniscribeState() {
+}
+
+void UniscribeState::InitWithOptionalLengthProtection(bool length_protection) {
+  // We cap the input length and just don't do anything. We'll allocate a lot
+  // of things of the size of the number of characters, so the allocated memory
+  // will be several times the input length. Plus shaping such a large buffer
+  // may be a form of denial of service. No legitimate text should be this long.
+  // It also appears that Uniscribe flatly rejects very long strings, so we
+  // don't lose anything by doing this.
+  //
+  // The input length protection may be disabled by the unit tests to cause
+  // an error condition.
+  static const int kMaxInputLength = 65535;
+  if (input_length_ == 0 ||
+      (length_protection && input_length_ > kMaxInputLength))
+    return;
+
+  FillRuns();
+  FillShapes();
+  FillScreenOrder();
+}
+
+int UniscribeState::Width() const {
+  int width = 0;
+  for (int item_index = 0; item_index < static_cast<int>(runs_->size());
+       item_index++) {
+    width += AdvanceForItem(item_index);
+  }
+  return width;
+}
+
+void UniscribeState::Justify(int additional_space) {
+  // Count the total number of glyphs we have so we know how big to make the
+  // buffers below.
+  int total_glyphs = 0;
+  for (size_t run = 0; run < runs_->size(); run++) {
+    int run_idx = screen_order_[run];
+    total_glyphs += static_cast<int>(shapes_[run_idx].glyph_length());
+  }
+  if (total_glyphs == 0)
+    return;  // Nothing to do.
+
+  // We make one big buffer in screen order of all the glyphs we are drawing
+  // across runs so that the justification function will adjust evenly across
+  // all glyphs.
+  StackVector<SCRIPT_VISATTR, 64> visattr;
+  visattr->resize(total_glyphs);
+  StackVector<int, 64> advances;
+  advances->resize(total_glyphs);
+  StackVector<int, 64> justify;
+  justify->resize(total_glyphs);
+
+  // Build the packed input.
+  int dest_index = 0;
+  for (size_t run = 0; run < runs_->size(); run++) {
+    int run_idx = screen_order_[run];
+    const Shaping& shaping = shapes_[run_idx];
+
+    for (int i = 0; i < shaping.glyph_length(); i++, dest_index++) {
+      memcpy(&visattr[dest_index], &shaping.visattr[i], sizeof(SCRIPT_VISATTR));
+      advances[dest_index] = shaping.advance[i];
+    }
+  }
+
+  // The documentation for ScriptJustify is wrong, the parameter is the space
+  // to add and not the width of the column you want.
+  const int min_kashida = 1;  // How do we decide what this should be?
+  ScriptJustify(&visattr[0], &advances[0], total_glyphs, additional_space,
+                min_kashida, &justify[0]);
+
+  // Now we have to unpack the justification amounts back into the runs so
+  // the glyph indices match.
+  int global_glyph_index = 0;
+  for (size_t run = 0; run < runs_->size(); run++) {
+    int run_idx = screen_order_[run];
+    Shaping& shaping = shapes_[run_idx];
+
+    shaping.justify->resize(shaping.glyph_length());
+    for (int i = 0; i < shaping.glyph_length(); i++, global_glyph_index++)
+      shaping.justify[i] = justify[global_glyph_index];
+  }
+}
+
+int UniscribeState::CharacterToX(int offset) const {
+  HRESULT hr;
+  DCHECK(offset <= input_length_);
+
+  // Our algorithm is to traverse the items in screen order from left to
+  // right, adding in each item's screen width until we find the item with
+  // the requested character in it.
+  int width = 0;
+  for (size_t screen_idx = 0; screen_idx < runs_->size(); screen_idx++) {
+    // Compute the length of this run.
+    int item_idx = screen_order_[screen_idx];
+    const SCRIPT_ITEM& item = runs_[item_idx];
+    const Shaping& shaping = shapes_[item_idx];
+    int item_length = shaping.char_length();
+
+    if (offset >= item.iCharPos && offset <= item.iCharPos + item_length) {
+      // Character offset is in this run.
+      int char_len = offset - item.iCharPos;
+
+      int cur_x = 0;
+      hr = ScriptCPtoX(char_len, FALSE, item_length, shaping.glyph_length(),
+                       &shaping.logs[0], &shaping.visattr[0],
+                       shaping.effective_advances(), &item.a, &cur_x);
+      if (FAILED(hr))
+        return 0;
+
+      width += cur_x + shaping.pre_padding;
+      DCHECK(width >= 0);
+      return width;
+    }
+
+    // Move to the next item.
+    width += AdvanceForItem(item_idx);
+  }
+  DCHECK(width >= 0);
+  return width;
+}
+
+int UniscribeState::XToCharacter(int x) const {
+  // We iterate in screen order until we find the item with the given pixel
+  // position in it. When we find that guy, we ask Uniscribe for the
+  // character index.
+  HRESULT hr;
+  for (size_t screen_idx = 0; screen_idx < runs_->size(); screen_idx++) {
+    int item_idx = screen_order_[screen_idx];
+    int advance_for_item = AdvanceForItem(item_idx);
+
+    // Note that the run may be empty if shaping failed, so we want to skip
+    // over it.
+    const Shaping& shaping = shapes_[item_idx];
+    int item_length = shaping.char_length();
+    if (x <= advance_for_item && item_length > 0) {
+      // The requested offset is within this item.
+      const SCRIPT_ITEM& item = runs_[item_idx];
+
+      // Account for the leading space we've added to this run that Uniscribe
+      // doesn't know about.
+      x -= shaping.pre_padding;
+
+      int char_x = 0;
+      int trailing;
+      hr = ScriptXtoCP(x, item_length, shaping.glyph_length(),
+                       &shaping.logs[0], &shaping.visattr[0],
+                       shaping.effective_advances(), &item.a, &char_x,
+                       &trailing);
+
+      // The character offset is within the item. We need to add the item's
+      // offset to transform it into the space of the TextRun
+      return char_x + item.iCharPos;
+    }
+
+    // The offset is beyond this item, account for its length and move on.
+    x -= advance_for_item;
+  }
+
+  // Error condition, we don't know what to do if we don't have that X
+  // position in any of our items.
+  return 0;
+}
+
+void UniscribeState::Draw(HDC dc, int x, int y, int from, int to) {
+  HGDIOBJ old_font = 0;
+  int cur_x = x;
+  bool first_run = true;
+
+  for (size_t screen_idx = 0; screen_idx < runs_->size(); screen_idx++) {
+    int item_idx = screen_order_[screen_idx];
+    const SCRIPT_ITEM& item = runs_[item_idx];
+    const Shaping& shaping = shapes_[item_idx];
+
+    // Character offsets within this run. THESE MAY NOT BE IN RANGE and may
+    // be negative, etc. The code below handles this.
+    int from_char = from - item.iCharPos;
+    int to_char = to - item.iCharPos;
+
+    // See if we need to draw any characters in this item.
+    if (shaping.char_length() == 0 ||
+        from_char >= shaping.char_length() || to_char <= 0) {
+      // No chars in this item to display.
+      cur_x += AdvanceForItem(item_idx);
+      continue;
+    }
+
+    // Compute the starting glyph within this span. |from| and |to| are
+    // global offsets that may intersect arbitrarily with our local run.
+    int from_glyph, after_glyph;
+    if (item.a.fRTL) {
+      // To compute the first glyph when going RTL, we use |to|.
+      if (to_char >= shaping.char_length()) {
+        // The end of the text is after (to the left) of us.
+        from_glyph = 0;
+      } else {
+        // Since |to| is exclusive, the first character we draw on the left
+        // is actually the one right before (to the right) of |to|.
+        from_glyph = shaping.logs[to_char - 1];
+      }
+
+      // The last glyph is actually the first character in the range.
+      if (from_char <= 0) {
+        // The first character to draw is before (to the right) of this span,
+        // so draw all the way to the end.
+        after_glyph = shaping.glyph_length();
+      } else {
+        // We want to draw everything up until the character to the right of
+        // |from|. To the right is - 1, so we look that up (remember our
+        // character could be more than one glyph, so we can't look up our
+        // glyph and add one).
+        after_glyph = shaping.logs[from_char - 1];
+      }
+    } else {
+      // Easy case, everybody agrees about directions. We only need to handle
+      // boundary conditions to get a range inclusive at the beginning, and
+      // exclusive at the ending. We have to do some computation to see the
+      // glyph one past the end.
+      from_glyph = shaping.logs[from_char < 0 ? 0 : from_char];
+      if (to_char >= shaping.char_length())
+        after_glyph = shaping.glyph_length();
+      else
+        after_glyph = shaping.logs[to_char];
+    }
+
+    // Account for the characters that were skipped in this run. When
+    // WebKit asks us to draw a subset of the run, it actually tells us
+    // to draw at the X offset of the beginning of the run, since it
+    // doesn't know the internal position of any of our characters.
+    const int* effective_advances = shaping.effective_advances();
+    int inner_offset = 0;
+    for (int i = 0; i < from_glyph; i++)
+      inner_offset += effective_advances[i];
+
+    // Actually draw the glyphs we found.
+    int glyph_count = after_glyph - from_glyph;
+    if (from_glyph >= 0 && glyph_count > 0) {
+      // Account for the preceeding space we need to add to this run. We don't
+      // need to count for the following space because that will be counted
+      // in AdvanceForItem below when we move to the next run.
+      inner_offset += shaping.pre_padding;
+
+      // Pass NULL in when there is no justification.
+      const int* justify = shaping.justify->empty() ?
+          NULL : &shaping.justify[from_glyph];
+
+      if (first_run) {
+        old_font = SelectObject(dc, shaping.hfont_);
+        first_run = false;
+      } else {
+        SelectObject(dc, shaping.hfont_);
+      }
+
+      // TODO(brettw) bug 698452: if a half a character is selected,
+      // we should set up a clip rect so we draw the half of the glyph
+      // correctly.
+      // Fonts with different ascents can be used to render different runs.
+      // 'Across-runs' y-coordinate correction needs to be adjusted
+      // for each font.
+      HRESULT hr = S_FALSE;
+      for (int executions = 0; executions < 2; ++executions) {
+        hr = ScriptTextOut(dc, shaping.script_cache_, cur_x + inner_offset,
+                           y - shaping.ascent_offset_, 0, NULL, &item.a, NULL,
+                           0, &shaping.glyphs[from_glyph],
+                           glyph_count, &shaping.advance[from_glyph],
+                           justify, &shaping.offsets[from_glyph]);
+        if (S_OK != hr && 0 == executions) {
+          // If this ScriptTextOut is called from the renderer it might fail
+          // because the sandbox is preventing it from opening the font files.
+          // If we are running in the renderer, TryToPreloadFont is overridden
+          // to ask the browser to preload the font for us so we can access it.
+          TryToPreloadFont(shaping.hfont_);
+          continue;
+        }
+        break;
+      }
+
+      DCHECK(S_OK == hr);
+
+
+    }
+
+    cur_x += AdvanceForItem(item_idx);
+  }
+
+  if (old_font)
+    SelectObject(dc, old_font);
+}
+
+WORD UniscribeState::FirstGlyphForCharacter(int char_offset) const {
+  // Find the run for the given character.
+  for (int i = 0; i < static_cast<int>(runs_->size()); i++) {
+    int first_char = runs_[i].iCharPos;
+    const Shaping& shaping = shapes_[i];
+    int local_offset = char_offset - first_char;
+    if (local_offset >= 0 && local_offset < shaping.char_length()) {
+      // The character is in this run, return the first glyph for it (should
+      // generally be the only glyph). It seems Uniscribe gives glyph 0 for
+      // empty, which is what we want to return in the "missing" case.
+      size_t glyph_index = shaping.logs[local_offset];
+      if (glyph_index >= shaping.glyphs->size()) {
+        // The glyph should be in this run, but the run has too few actual
+        // characters. This can happen when shaping the run fails, in which
+        // case, we should have no data in the logs at all.
+        DCHECK(shaping.glyphs->empty());
+        return 0;
+      }
+      return shaping.glyphs[glyph_index];
+    }
+  }
+  return 0;
+}
+
+void UniscribeState::FillRuns() {
+  HRESULT hr;
+  runs_->resize(UNISCRIBE_STATE_STACK_RUNS);
+
+  SCRIPT_STATE input_state;
+  input_state.uBidiLevel = is_rtl_;
+  input_state.fOverrideDirection = directional_override_;
+  input_state.fInhibitSymSwap = false;
+  input_state.fCharShape = false;  // Not implemented in Uniscribe
+  input_state.fDigitSubstitute = false;  // Do we want this for Arabic?
+  input_state.fInhibitLigate = inhibit_ligate_;
+  input_state.fDisplayZWG = false;  // Don't draw control characters.
+  input_state.fArabicNumContext = is_rtl_;  // Do we want this for Arabic?
+  input_state.fGcpClusters = false;
+  input_state.fReserved = 0;
+  input_state.fEngineReserved = 0;
+  // The psControl argument to ScriptItemize should be non-NULL for RTL text,
+  // per http://msdn.microsoft.com/en-us/library/ms776532.aspx . So use a
+  // SCRIPT_CONTROL that is set to all zeros.  Zero as a locale ID means the
+  // neutral locale per http://msdn.microsoft.com/en-us/library/ms776294.aspx .
+  static SCRIPT_CONTROL input_control = {0, // uDefaultLanguage    :16;
+                                         0, // fContextDigits      :1;
+                                         0, // fInvertPreBoundDir  :1;
+                                         0, // fInvertPostBoundDir :1;
+                                         0, // fLinkStringBefore   :1;
+                                         0, // fLinkStringAfter    :1;
+                                         0, // fNeutralOverride    :1;
+                                         0, // fNumericOverride    :1;
+                                         0, // fLegacyBidiClass    :1;
+                                         0, // fMergeNeutralItems  :1;
+                                         0};// fReserved           :7;
+  // Calling ScriptApplyDigitSubstitution( NULL, &input_control, &input_state)
+  // here would be appropriate if we wanted to set the language ID, and get
+  // local digit substitution behavior.  For now, don't do it.
+
+  while (true) {
+    int num_items = 0;
+
+    // Ideally, we would have a way to know the runs before and after this
+    // one, and put them into the control parameter of ScriptItemize. This
+    // would allow us to shape characters properly that cross style
+    // boundaries (WebKit bug 6148).
+    //
+    // We tell ScriptItemize that the output list of items is one smaller
+    // than it actually is. According to Mozilla bug 366643, if there is
+    // not enough room in the array on pre-SP2 systems, ScriptItemize will
+    // write one past the end of the buffer.
+    //
+    // ScriptItemize is very strange. It will often require a much larger
+    // ITEM buffer internally than it will give us as output. For example,
+    // it will say a 16-item buffer is not big enough, and will write
+    // interesting numbers into all those items. But when we give it a 32
+    // item buffer and it succeeds, it only has one item output.
+    //
+    // It seems to be doing at least two passes, the first where it puts a
+    // lot of intermediate data into our items, and the second where it
+    // collates them.
+    hr = ScriptItemize(input_, input_length_,
+                       static_cast<int>(runs_->size()) - 1, &input_control, &input_state,
+                       &runs_[0], &num_items);
+    if (SUCCEEDED(hr)) {
+      runs_->resize(num_items);
+      break;
+    }
+    if (hr != E_OUTOFMEMORY) {
+      // Some kind of unexpected error.
+      runs_->resize(0);
+      break;
+    }
+    // There was not enough items for it to write into, expand.
+    runs_->resize(runs_->size() * 2);
+  }
+
+  // Fix up the directions of the items so they're what WebKit thinks
+  // they are. WebKit (and we assume any other caller) always knows what
+  // direction it wants things to be in, and will only give us runs that are in
+  // the same direction. Sometimes, Uniscibe disagrees, for example, if you
+  // have embedded ASCII punctuation in an Arabic string, WebKit will
+  // (correctly) know that is should still be rendered RTL, but Uniscibe might
+  // think LTR is better.
+  //
+  // TODO(brettw) bug 747235:
+  // This workaround fixes the bug but causes spacing problems in other cases.
+  // WebKit sometimes gives us a big run that includes ASCII and Arabic, and
+  // this forcing direction makes those cases incorrect. This seems to happen
+  // during layout only, so it ends up that spacing is incorrect (because being
+  // the wrong direction changes ligatures and stuff).
+  //
+  //for (size_t i = 0; i < runs_->size(); i++)
+  //  runs_[i].a.fRTL = is_rtl_;
+}
+
+
+bool UniscribeState::Shape(const wchar_t* input,
+                           int item_length,
+                           int num_glyphs,
+                           SCRIPT_ITEM& run,
+                           Shaping& shaping) {
+  HFONT hfont = hfont_;
+  SCRIPT_CACHE* script_cache = script_cache_;
+  SCRIPT_FONTPROPERTIES* font_properties = font_properties_;
+  int ascent = ascent_;
+  HDC temp_dc = NULL;
+  HGDIOBJ old_font = 0;
+  HRESULT hr;
+  bool lastFallbackTried = false;
+  bool result;
+
+  int generated_glyphs = 0;
+
+  // In case HFONT passed in ctor cannot render this run, we have to scan
+  // other fonts from the beginning of the font list.
+  ResetFontIndex();
+
+  // Compute shapes.
+  while (true) {
+    shaping.logs->resize(item_length);
+    shaping.glyphs->resize(num_glyphs);
+    shaping.visattr->resize(num_glyphs);
+
+    // Firefox sets SCRIPT_ANALYSIS.SCRIPT_STATE.fDisplayZWG to true
+    // here. Is that what we want? It will display control characters.
+    hr = ScriptShape(temp_dc, script_cache, input, item_length,
+                     num_glyphs, &run.a,
+                     &shaping.glyphs[0], &shaping.logs[0],
+                     &shaping.visattr[0], &generated_glyphs);
+    if (hr == E_PENDING) {
+      // Allocate the DC.
+      temp_dc = GetDC(NULL);
+      old_font = SelectObject(temp_dc, hfont);
+      continue;
+    } else if (hr == E_OUTOFMEMORY) {
+      num_glyphs *= 2;
+      continue;
+    } else if (SUCCEEDED(hr) &&
+               (lastFallbackTried || !ContainsMissingGlyphs(&shaping.glyphs[0],
+                generated_glyphs, font_properties))) {
+      break;
+    }
+
+    // The current font can't render this run. clear DC and try
+    // next font.
+    if (temp_dc) {
+      SelectObject(temp_dc, old_font);
+      ReleaseDC(NULL, temp_dc);
+      temp_dc = NULL;
+    }
+
+    if (NextWinFontData(&hfont, &script_cache, &font_properties, &ascent)) {
+      // The primary font does not support this run. Try next font.
+      // In case of web page rendering, they come from fonts specified in
+      // CSS stylesheets.
+      continue;
+    } else if (!lastFallbackTried) {
+      lastFallbackTried = true;
+
+      // Generate a last fallback font based on the script of
+      // a character to draw while inheriting size and styles
+      // from the primary font
+      if (!logfont_.lfFaceName[0])
+        SetLogFontAndStyle(hfont_, &logfont_, &style_);
+
+      // TODO(jungshik): generic type should come from webkit for
+      // UniscribeStateTextRun (a derived class used in webkit).
+      const wchar_t *family = GetFallbackFamily(input, item_length,
+          GENERIC_FAMILY_STANDARD);
+      bool font_ok = GetDerivedFontData(family, style_, &logfont_, &ascent, &hfont, &script_cache);
+
+      if (!font_ok) {
+        // If this GetDerivedFontData is called from the renderer it might fail
+        // because the sandbox is preventing it from opening the font files.
+        // If we are running in the renderer, TryToPreloadFont is overridden to
+        // ask the browser to preload the font for us so we can access it.
+        TryToPreloadFont(hfont);
+
+        // Try again.
+        font_ok = GetDerivedFontData(family, style_, &logfont_, &ascent, &hfont, &script_cache);
+        DCHECK(font_ok);
+      }
+
+      // TODO(jungshik) : Currently GetDerivedHFont always returns a
+      // a valid HFONT, but in the future, I may change it to return 0.
+      DCHECK(hfont);
+
+      // We don't need a font_properties for the last resort fallback font
+      // because we don't have anything more to try and are forced to
+      // accept empty glyph boxes. If we tried a series of fonts as
+      // 'last-resort fallback', we'd need it, but currently, we don't.
+      continue;
+    } else if (hr == USP_E_SCRIPT_NOT_IN_FONT) {
+      run.a.eScript = SCRIPT_UNDEFINED;
+      continue;
+    } else if (FAILED(hr)) {
+      // Error shaping.
+      generated_glyphs = 0;
+      result = false;
+      goto cleanup;
+    }
+  }
+
+  // Sets Windows font data for this run to those corresponding to
+  // a font supporting this run. we don't need to store font_properties
+  // because it's not used elsewhere.
+  shaping.hfont_ = hfont;
+  shaping.script_cache_ = script_cache;
+
+  // The ascent of a font for this run can be different from
+  // that of the primary font so that we need to keep track of
+  // the difference per run and take that into account when calling
+  // ScriptTextOut in |Draw|. Otherwise, different runs rendered by
+  // different fonts would not be aligned vertically.
+  shaping.ascent_offset_ = ascent_ ? ascent - ascent_ : 0;
+  result = true;
+
+cleanup:
+  shaping.glyphs->resize(generated_glyphs);
+  shaping.visattr->resize(generated_glyphs);
+  shaping.advance->resize(generated_glyphs);
+  shaping.offsets->resize(generated_glyphs);
+  if (temp_dc) {
+    SelectObject(temp_dc, old_font);
+    ReleaseDC(NULL, temp_dc);
+  }
+  // On failure, our logs don't mean anything, so zero those out.
+  if (!result)
+    shaping.logs->clear();
+
+  return result;
+}
+
+void UniscribeState::FillShapes() {
+  shapes_->resize(runs_->size());
+  for (size_t i = 0; i < runs_->size(); i++) {
+    int start_item = runs_[i].iCharPos;
+    int item_length = input_length_ - start_item;
+    if (i < runs_->size() - 1)
+      item_length = runs_[i + 1].iCharPos - start_item;
+
+    int num_glyphs;
+    if (item_length < UNISCRIBE_STATE_STACK_CHARS) {
+      // We'll start our buffer sizes with the current stack space available
+      // in our buffers if the current input fits. As long as it
+      // doesn't expand past that we'll save a lot of time mallocing.
+      num_glyphs = UNISCRIBE_STATE_STACK_CHARS;
+    } else {
+      // When the input doesn't fit, give up with the stack since it will
+      // almost surely not be enough room (unless the input actually shrinks,
+      // which is unlikely) and just start with the length recommended by
+      // the Uniscribe documentation as a "usually fits" size.
+      num_glyphs = item_length * 3 / 2 + 16;
+    }
+
+    // Convert a string to a glyph string trying the primary font,
+    // fonts in the fallback list and then script-specific last resort font.
+    Shaping& shaping = shapes_[i];
+    if (!Shape(&input_[start_item], item_length, num_glyphs, runs_[i], shaping))
+      continue;
+
+    // Compute placements. Note that offsets is documented incorrectly
+    // and is actually an array.
+
+    // DC that we lazily create if Uniscribe commands us to.
+    // (this does not happen often because script_cache is already
+    //  updated when calling ScriptShape).
+    HDC temp_dc = NULL;
+    HGDIOBJ old_font = NULL;
+    HRESULT hr;
+    while (true) {
+      shaping.pre_padding = 0;
+      hr = ScriptPlace(temp_dc, shaping.script_cache_, &shaping.glyphs[0],
+                       static_cast<int>(shaping.glyphs->size()),
+                       &shaping.visattr[0], &runs_[i].a,
+                       &shaping.advance[0], &shaping.offsets[0],
+                       &shaping.abc);
+      if (hr != E_PENDING)
+        break;
+
+      // Allocate the DC and run the loop again.
+      temp_dc = GetDC(NULL);
+      old_font = SelectObject(temp_dc, shaping.hfont_);
+    }
+
+    if (FAILED(hr)) {
+      // Some error we don't know how to handle. Nuke all of our data
+      // since we can't deal with partially valid data later.
+      runs_->clear();
+      shapes_->clear();
+      screen_order_->clear();
+    }
+
+    if (temp_dc) {
+      SelectObject(temp_dc, old_font);
+      ReleaseDC(NULL, temp_dc);
+    }
+  }
+
+  AdjustSpaceAdvances();
+
+  if (letter_spacing_ != 0 || word_spacing_ != 0)
+    ApplySpacing();
+}
+
+void UniscribeState::FillScreenOrder() {
+  screen_order_->resize(runs_->size());
+
+  // We assume that the input has only one text direction in it.
+  // TODO(brettw) are we sure we want to keep this restriction?
+  if (is_rtl_) {
+    for (int i = 0; i < static_cast<int>(screen_order_->size()); i++)
+      screen_order_[static_cast<int>(screen_order_->size()) - i - 1] = i;
+  } else {
+    for (int i = 0; i < static_cast<int>(screen_order_->size()); i++)
+      screen_order_[i] = i;
+  }
+}
+
+void UniscribeState::AdjustSpaceAdvances() {
+  if (space_width_ == 0)
+    return;
+
+  int space_width_without_letter_spacing = space_width_ - letter_spacing_;
+
+  // This mostly matches what WebKit's UniscribeController::shapeAndPlaceItem.
+  for (size_t run = 0; run < runs_->size(); run++) {
+    Shaping& shaping = shapes_[run];
+
+    for (int i = 0; i < shaping.char_length(); i++) {
+      if (!TreatAsSpace(input_[runs_[run].iCharPos + i]))
+        continue;
+
+      int glyph_index = shaping.logs[i];
+      int current_advance = shaping.advance[glyph_index];
+      // Don't give zero-width spaces a width.
+      if (!current_advance)
+        continue;
+
+      // current_advance does not include additional letter-spacing, but
+      // space_width does. Here we find out how off we are from the correct
+      // width for the space not including letter-spacing, then just subtract
+      // that diff.
+      int diff = current_advance - space_width_without_letter_spacing;
+      // The shaping can consist of a run of text, so only subtract the
+      // difference in the width of the glyph.
+      shaping.advance[glyph_index] -= diff;
+      shaping.abc.abcB -= diff;
+    }
+  }
+}
+
+void UniscribeState::ApplySpacing() {
+  for (size_t run = 0; run < runs_->size(); run++) {
+    Shaping& shaping = shapes_[run];
+    bool is_rtl = runs_[run].a.fRTL;
+
+    if (letter_spacing_ != 0) {
+      // RTL text gets padded to the left of each character. We increment the
+      // run's advance to make this happen. This will be balanced out by NOT
+      // adding additional advance to the last glyph in the run.
+      if (is_rtl)
+        shaping.pre_padding += letter_spacing_;
+
+      // Go through all the glyphs in this run and increase the "advance" to
+      // account for letter spacing. We adjust letter spacing only on cluster
+      // boundaries.
+      //
+      // This works for most scripts, but may have problems with some indic
+      // scripts. This behavior is better than Firefox or IE for Hebrew.
+      for (int i = 0; i < shaping.glyph_length(); i++) {
+        if (shaping.visattr[i].fClusterStart) {
+          // Ick, we need to assign the extra space so that the glyph comes
+          // first, then is followed by the space. This is opposite for RTL.
+          if (is_rtl) {
+            if (i != shaping.glyph_length() - 1) {
+              // All but the last character just get the spacing applied to
+              // their advance. The last character doesn't get anything,
+              shaping.advance[i] += letter_spacing_;
+              shaping.abc.abcB += letter_spacing_;
+            }
+          } else {
+            // LTR case is easier, we just add to the advance.
+            shaping.advance[i] += letter_spacing_;
+            shaping.abc.abcB += letter_spacing_;
+          }
+        }
+      }
+    }
+
+    // Go through all the characters to find whitespace and insert the extra
+    // wordspacing amount for the glyphs they correspond to.
+    if (word_spacing_ != 0) {
+      for (int i = 0; i < shaping.char_length(); i++) {
+        if (!TreatAsSpace(input_[runs_[run].iCharPos + i]))
+          continue;
+
+        // The char in question is a word separator...
+        int glyph_index = shaping.logs[i];
+
+        // Spaces will not have a glyph in Uniscribe, it will just add
+        // additional advance to the character to the left of the space. The
+        // space's corresponding glyph will be the character following it in
+        // reading order.
+        if (is_rtl) {
+          // In RTL, the glyph to the left of the space is the same as the
+          // first glyph of the following character, so we can just increment
+          // it.
+          shaping.advance[glyph_index] += word_spacing_;
+          shaping.abc.abcB += word_spacing_;
+        } else {
+          // LTR is actually more complex here, we apply it to the previous
+          // character if there is one, otherwise we have to apply it to the
+          // leading space of the run.
+          if (glyph_index == 0) {
+            shaping.pre_padding += word_spacing_;
+          } else {
+            shaping.advance[glyph_index - 1] += word_spacing_;
+            shaping.abc.abcB += word_spacing_;
+          }
+        }
+      }
+    }  // word_spacing_ != 0
+
+    // Loop for next run...
+  }
+}
+
+// The advance is the ABC width of the run
+int UniscribeState::AdvanceForItem(int item_index) const {
+  int accum = 0;
+  const Shaping& shaping = shapes_[item_index];
+
+  if (shaping.justify->empty()) {
+    // Easy case with no justification, the width is just the ABC width of	t		
+    // the run. (The ABC width is the sum of the advances).
+    return shaping.abc.abcA + shaping.abc.abcB + shaping.abc.abcC +
+        shaping.pre_padding;
+  }
+
+  // With justification, we use the justified amounts instead. The
+  // justification array contains both the advance and the extra space
+  // added for justification, so is the width we want.
+  int justification = 0;
+  for (size_t i = 0; i < shaping.justify->size(); i++)
+    justification += shaping.justify[i];
+
+  return shaping.pre_padding + justification;
+}
+
+}  // namespace gfx
diff --git a/base/gfx/uniscribe.h b/base/gfx/uniscribe.h
new file mode 100644
index 0000000..29f0d81
--- /dev/null
+++ b/base/gfx/uniscribe.h
@@ -0,0 +1,390 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// A wrapper around Uniscribe that provides a reasonable API.
+
+#ifndef BASE_GFX_UNISCRIBE_H__
+#define BASE_GFX_UNISCRIBE_H__
+
+#include <windows.h>
+#include <usp10.h>
+#include <wchar.h>
+#include <map>
+#include <vector>
+
+#include "base/stack_container.h"
+#include "testing/gtest/include/gtest/gtest_prod.h"
+
+namespace gfx {
+
+#define UNISCRIBE_STATE_STACK_RUNS 8
+#define UNISCRIBE_STATE_STACK_CHARS 32
+
+// This object should be safe to create & destroy frequently, as long as the
+// caller preserves the script_cache when possible (this data may be slow to
+// compute).
+//
+// This object is "kind of large" (~1K) because it reserves a lot of space for
+// working with to avoid expensive heap operations. Therefore, not only should
+// you not worry about creating and destroying it, you should try to not keep
+// them around.
+class UniscribeState {
+ public:
+  // Initializes this Uniscribe run with the text pointed to by |run| with
+  // |length|. The input is NOT null terminated.
+  //
+  // The is_rtl flag should be set if the input script is RTL. It is assumed
+  // that the caller has already divided up the input text (using ICU, for
+  // example) into runs of the same direction of script. This avoids
+  // disagreements between the caller and Uniscribe later (see FillItems).
+  //
+  // A script cache should be provided by the caller that is initialized to
+  // NULL. When the caller is done with the cache (it may be stored between
+  // runs as long as it is used consistently with the same HFONT), it should
+  // call ScriptFreeCache().
+  UniscribeState(const wchar_t* input,
+                 int input_length,
+                 bool is_rtl,
+                 HFONT hfont,
+                 SCRIPT_CACHE* script_cache,
+                 SCRIPT_FONTPROPERTIES* font_properties);
+
+  virtual ~UniscribeState();
+
+  // Sets Uniscribe's directional override flag. False by default.
+  bool directional_override() const {
+    return directional_override_;
+  }
+  void set_directional_override(bool override) {
+    directional_override_ = override;
+  }
+
+  // Set's Uniscribe's no-ligate override flag. False by default.
+  bool inhibit_ligate() const {
+    return inhibit_ligate_;
+  }
+  void set_inhibit_ligate(bool inhibit) {
+    inhibit_ligate_ = inhibit;
+  }
+
+  // Set letter spacing. We will try to insert this much space between
+  // graphemes (one or more glyphs perceived as a single unit by ordinary users
+  // of a script). Positive values increase letter spacing, negative values
+  // decrease it. 0 by default.
+  int letter_spacing() const {
+    return letter_spacing_;
+  }
+  void set_letter_spacing(int letter_spacing) {
+    letter_spacing_ = letter_spacing;
+  }
+
+  // Set the width of a standard space character. We use this to normalize
+  // space widths. Windows will make spaces after Hindi characters larger than
+  // other spaces. A space_width of 0 means to use the default space width.
+  //
+  // Must be set before Init() is called.
+  int space_width() const {
+    return space_width_;
+  }
+  void set_space_width(int space_width) {
+    space_width_ = space_width;
+  }
+
+  // Set word spacing. We will try to insert this much extra space between
+  // each word in the input (beyond whatever whitespace character separates
+  // words). Positive values lead to increased letter spacing, negative values
+  // decrease it. 0 by default.
+  //
+  // Must be set before Init() is called.
+  int word_spacing() const {
+    return word_spacing_;
+  }
+  void set_word_spacing(int word_spacing) {
+    word_spacing_ = word_spacing;
+  }
+  void set_ascent(int ascent) {
+    ascent_ = ascent;
+  }
+
+  // You must call this after setting any options but before doing any
+  // other calls like asking for widths or drawing.
+  void Init() { InitWithOptionalLengthProtection(true); }
+
+  // Returns the total width in pixels of the text run.
+  int Width() const;
+
+  // Call to justify the text, with the amount of space that should be ADDED to
+  // get the desired width that the column should be justified to. Normally,
+  // spaces are inserted, but for Arabic there will be kashidas (extra strokes)
+  // inserted instead.
+  //
+  // This function MUST be called AFTER Init().
+  void Justify(int additional_space);
+
+  // Computes the given character offset into a pixel offset of the beginning
+  // of that character.
+  int CharacterToX(int offset) const;
+
+  // Converts the given pixel X position into a logical character offset into
+  // the run. For positions appearing before the first character, this will
+  // return -1.
+  int XToCharacter(int x) const;
+
+  // Draws the given characters to (x, y) in the given DC. The font will be
+  // handled by this function, but the font color and other attributes should
+  // be pre-set.
+  //
+  // The y position is the upper left corner, NOT the baseline.
+  void Draw(HDC dc, int x, int y, int from, int to);
+
+  // Returns the first glyph assigned to the character at the given offset.
+  // This function is used to retrieve glyph information when Uniscribe is
+  // being used to generate glyphs for non-complex, non-BMP (above U+FFFF)
+  // characters. These characters are not otherwise special and have no
+  // complex shaping rules, so we don't otherwise need Uniscribe, except
+  // Uniscribe is the only way to get glyphs for non-BMP characters.
+  //
+  // Returns 0 if there is no glyph for the given character.
+  WORD FirstGlyphForCharacter(int char_offset) const;
+
+ protected:
+  // Backend for init. The flag allows the unit test to specify whether we
+  // should fail early for very long strings like normal, or try to pass the
+  // long string to Uniscribe. The latter provides a way to force failure of
+  // shaping.
+  void InitWithOptionalLengthProtection(bool length_protection);
+
+  // Tries to preload the font when the it is not accessible.
+  // This is the default implementation and it does not do anything.
+  virtual void TryToPreloadFont(HFONT font) {}
+
+ private:
+  FRIEND_TEST(UniscribeTest, TooBig);
+
+  // An array corresponding to each item in runs_ containing information
+  // on each of the glyphs that were generated. Like runs_, this is in
+  // reading order. However, for rtl text, the characters within each
+  // item will be reversed.
+  struct Shaping {
+    Shaping()
+        : pre_padding(0),
+          hfont_(NULL),
+          script_cache_(NULL),
+          ascent_offset_(0) {
+      abc.abcA = 0;
+      abc.abcB = 0;
+      abc.abcC = 0;
+    }
+
+    // Returns the number of glyphs (which will be drawn to the screen)
+    // in this run.
+    int glyph_length() const {
+      return static_cast<int>(glyphs->size());
+    }
+
+    // Returns the number of characters (that we started with) in this run.
+    int char_length() const {
+      return static_cast<int>(logs->size());
+    }
+
+    // Returns the advance array that should be used when measuring glyphs.
+    // The returned pointer will indicate an array with glyph_length() elements
+    // and the advance that should be used for each one. This is either the
+    // real advance, or the justified advances if there is one, and is the
+    // array we want to use for measurement.
+    const int* effective_advances() const {
+      if (advance->empty())
+        return 0;
+      if (justify->empty())
+        return &advance[0];
+      return &justify[0];
+    }
+
+    // This is the advance amount of space that we have added to the beginning
+    // of the run. It is like the ABC's |A| advance but one that we create and
+    // must handle internally whenever computing with pixel offsets.
+    int pre_padding;
+
+    // Glyph indices in the font used to display this item. These indices
+    // are in screen order.
+    StackVector<WORD, UNISCRIBE_STATE_STACK_CHARS> glyphs;
+
+    // For each input character, this tells us the first glyph index it
+    // generated. This is the only array with size of the input chars.
+    //
+    // All offsets are from the beginning of this run. Multiple characters can
+    // generate one glyph, in which case there will be adjacent duplicates in
+    // this list. One character can also generate multiple glyphs, in which
+    // case there will be skipped indices in this list.
+    StackVector<WORD, UNISCRIBE_STATE_STACK_CHARS> logs;
+
+    // Flags and such for each glyph.
+    StackVector<SCRIPT_VISATTR, UNISCRIBE_STATE_STACK_CHARS> visattr;
+
+    // Horizontal advances for each glyph listed above, this is basically
+    // how wide each glyph is.
+    StackVector<int, UNISCRIBE_STATE_STACK_CHARS> advance;
+
+    // This contains glyph offsets, from the nominal position of a glyph. It
+    // is used to adjust the positions of multiple combining characters
+    // around/above/below base characters in a context-sensitive manner so
+    // that they don't bump against each other and the base character.
+    StackVector<GOFFSET, UNISCRIBE_STATE_STACK_CHARS> offsets;
+
+    // Filled by a call to Justify, this is empty for nonjustified text.
+    // If nonempty, this contains the array of justify characters for each
+    // character as returned by ScriptJustify.
+    //
+    // This is the same as the advance array, but with extra space added for
+    // some characters. The difference between a glyph's |justify| width and
+    // it's |advance| width is the extra space added.
+    StackVector<int, UNISCRIBE_STATE_STACK_CHARS> justify;
+
+    // Sizing information for this run. This treats the entire run as a
+    // character with a preceeding advance, width, and ending advance.
+    // The B width is the sum of the |advance| array, and the A and C widths
+    // are any extra spacing applied to each end.
+    //
+    // It is unclear from the documentation what this actually means. From
+    // experimentation, it seems that the sum of the character advances is
+    // always the sum of the ABC values, and I'm not sure what you're supposed
+    // to do with the ABC values.
+    ABC abc;
+
+    // Pointers to windows font data used to render this run.
+    HFONT hfont_;
+    SCRIPT_CACHE* script_cache_;
+
+    // Ascent offset between the ascent of the primary font
+    // and that of the fallback font. The offset needs to be applied,
+    // when drawing a string, to align multiple runs rendered with
+    // different fonts.
+    int ascent_offset_;
+  };
+
+  // Computes the runs_ array from the text run.
+  void FillRuns();
+
+  // Computes the shapes_ array given an runs_ array already filled in.
+  void FillShapes();
+
+  // Fills in the screen_order_ array (see below).
+  void FillScreenOrder();
+
+  // Called to update the glyph positions based on the current spacing options
+  // that are set.
+  void ApplySpacing();
+
+  // Normalizes all advances for spaces to the same width. This keeps windows
+  // from making spaces after Hindi characters larger, which is then
+  // inconsistent with our meaure of the width since WebKit doesn't include
+  // spaces in text-runs sent to uniscribe unless white-space:pre.
+  void AdjustSpaceAdvances();
+
+  // Returns the total width of a single item.
+  int AdvanceForItem(int item_index) const;
+
+  // Shapes a run (pointed to by |input|) using |hfont| first.
+  // Tries a series of fonts specified retrieved with NextWinFontData
+  // and finally a font covering characters in |*input|. A string pointed
+  // by |input| comes from ScriptItemize and is supposed to contain
+  // characters belonging to a single script aside from characters
+  // common to all scripts (e.g. space).
+  bool Shape(const wchar_t* input,
+             int item_length,
+             int num_glyphs,
+             SCRIPT_ITEM& run,
+             Shaping& shaping);
+
+  // Gets Windows font data for the next best font to try in the list
+  // of fonts. When there's no more font available, returns false
+  // without touching any of out params. Need to call ResetFontIndex
+  // to start scanning of the font list from the beginning.
+  virtual bool NextWinFontData(HFONT* hfont,
+                               SCRIPT_CACHE** script_cache,
+                               SCRIPT_FONTPROPERTIES** font_properties,
+                               int* ascent) {
+    return false;
+  }
+
+  // Resets the font index to the first in the list of fonts
+  // to try after the primaryFont turns out not to work. With font_index
+  // reset, NextWinFontData scans fallback fonts from the beginning.
+  virtual void ResetFontIndex() {}
+
+  // The input data for this run of Uniscribe. See the constructor.
+  const wchar_t* input_;
+  const int input_length_;
+  const bool is_rtl_;
+
+  // Windows font data for the primary font :
+  // In a sense, logfont_ and style_ are redundant because
+  // hfont_ contains all the information. However, invoking GetObject,
+  // everytime we need the height and the style, is rather expensive so
+  // that we cache them. Would it be better to add getter and (virtual)
+  // setter for the height and the style of the primary font, instead of
+  // logfont_? Then, a derived class ctor can set ascent_, height_ and style_
+  // if they're known. Getters for them would have to 'infer' their values from
+  // hfont_ ONLY when they're not set.
+  HFONT hfont_;
+  SCRIPT_CACHE* script_cache_;
+  SCRIPT_FONTPROPERTIES* font_properties_;
+  int ascent_;
+  LOGFONT logfont_;
+  int style_;
+
+  // Options, see the getters/setters above.
+  bool directional_override_;
+  bool inhibit_ligate_;
+  int letter_spacing_;
+  int space_width_;
+  int word_spacing_;
+  int justification_width_;
+
+  // Uniscribe breaks the text into Runs. These are one length of text that is
+  // in one script and one direction. This array is in reading order.
+  StackVector<SCRIPT_ITEM, UNISCRIBE_STATE_STACK_RUNS> runs_;
+
+  StackVector<Shaping, UNISCRIBE_STATE_STACK_RUNS> shapes_;
+
+  // This is a mapping between reading order and screen order for the items.
+  // Uniscribe's items array are in reading order. For right-to-left text,
+  // or mixed (although WebKit's |TextRun| should really be only one
+  // direction), this makes it very difficult to compute character offsets
+  // and positions. This list is in screen order from left to right, and
+  // gives the index into the |runs_| and |shapes_| arrays of each
+  // subsequent item.
+  StackVector<int, UNISCRIBE_STATE_STACK_RUNS> screen_order_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(UniscribeState);
+};
+
+}  // namespace gfx
+
+#endif  // BASE_GFX_UNISCRIBE_H__
diff --git a/base/gfx/uniscribe_unittest.cc b/base/gfx/uniscribe_unittest.cc
new file mode 100644
index 0000000..8b2419c
--- /dev/null
+++ b/base/gfx/uniscribe_unittest.cc
@@ -0,0 +1,164 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/gfx/uniscribe.h"
+#include "base/win_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// This must be in the gfx namespace for the friend statements in uniscribe.h
+// to work.
+namespace gfx {
+
+namespace {
+
+class UniscribeTest : public testing::Test {
+ public:
+  UniscribeTest() {
+  }
+
+  // Returns an HFONT with the given name. The caller does not have to free
+  // this, it will be automatically freed at the end of the test. Returns NULL
+  // on failure. On success, the
+  HFONT MakeFont(const wchar_t* font_name, SCRIPT_CACHE** cache) {
+    LOGFONT lf;
+    memset(&lf, 0, sizeof(LOGFONT));
+    lf.lfHeight = 20;
+    wcscpy_s(lf.lfFaceName, font_name);
+
+    HFONT hfont = CreateFontIndirect(&lf);
+    if (!hfont)
+      return NULL;
+
+    *cache = new SCRIPT_CACHE;
+    **cache = NULL;
+    created_fonts_.push_back(std::make_pair(hfont, *cache));
+    return hfont;
+  }
+
+ protected:
+  // Default font properties structure for tests to use.
+  SCRIPT_FONTPROPERTIES properties_;
+
+ private:
+  virtual void SetUp() {
+    memset(&properties_, 0, sizeof(SCRIPT_FONTPROPERTIES));
+    properties_.cBytes = sizeof(SCRIPT_FONTPROPERTIES);
+    properties_.wgBlank = ' ';
+    properties_.wgDefault = '?';  // Used when the character is not in the font.
+    properties_.wgInvalid = '#';  // Used for invalid characters.
+  }
+
+  virtual void TearDown() {
+    // Free any allocated fonts.
+    for (size_t i = 0; i < created_fonts_.size(); i++) {
+      DeleteObject(created_fonts_[i].first);
+      ScriptFreeCache(created_fonts_[i].second);
+      delete created_fonts_[i].second;
+    }
+    created_fonts_.clear();
+  }
+
+  // Tracks allocated fonts so we can delete them at the end of the test.
+  // The script cache pointer is heap allocated and must be freed.
+  std::vector< std::pair<HFONT, SCRIPT_CACHE*> > created_fonts_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(UniscribeTest);
+};
+
+}  // namespace
+
+// This test tests giving Uniscribe a very large buffer, which will cause a
+// failure.
+TEST_F(UniscribeTest, TooBig) {
+  // This test will only run on Windows XP. It seems Uniscribe does not have the
+  // internal limit on Windows 2000 that we rely on to cause this failure.
+  if (win_util::GetWinVersion() <= win_util::WINVERSION_2000)
+    return;
+
+  // Make a large string with an e with a zillion combining accents.
+  std::wstring input(L"e");
+  for (int i = 0; i < 100000; i++)
+    input.push_back(0x301);  // Combining acute accent.
+
+  SCRIPT_CACHE* script_cache;
+  HFONT hfont = MakeFont(L"Times New Roman", &script_cache);
+  ASSERT_TRUE(hfont);
+
+  // Test a long string without the normal length protection we have. This will
+  // cause shaping to fail.
+  {
+    gfx::UniscribeState uniscribe(input.data(), static_cast<int>(input.size()),
+                                  false, hfont, script_cache, &properties_);
+    uniscribe.InitWithOptionalLengthProtection(false);
+
+    // There should be one shaping entry, with nothing in it.
+    ASSERT_EQ(1, uniscribe.shapes_->size());
+    EXPECT_EQ(0, uniscribe.shapes_[0].glyphs->size());
+    EXPECT_EQ(0, uniscribe.shapes_[0].logs->size());
+    EXPECT_EQ(0, uniscribe.shapes_[0].visattr->size());
+    EXPECT_EQ(0, uniscribe.shapes_[0].advance->size());
+    EXPECT_EQ(0, uniscribe.shapes_[0].offsets->size());
+    EXPECT_EQ(0, uniscribe.shapes_[0].justify->size());
+    EXPECT_EQ(0, uniscribe.shapes_[0].abc.abcA);
+    EXPECT_EQ(0, uniscribe.shapes_[0].abc.abcB);
+    EXPECT_EQ(0, uniscribe.shapes_[0].abc.abcC);
+
+    // The sizes of the other stuff should match the shaping entry.
+    EXPECT_EQ(1, uniscribe.runs_->size());
+    EXPECT_EQ(1, uniscribe.screen_order_->size());
+
+    // Check that the various querying functions handle the empty case properly.
+    EXPECT_EQ(0, uniscribe.Width());
+    EXPECT_EQ(0, uniscribe.FirstGlyphForCharacter(0));
+    EXPECT_EQ(0, uniscribe.FirstGlyphForCharacter(1000));
+    EXPECT_EQ(0, uniscribe.XToCharacter(0));
+    EXPECT_EQ(0, uniscribe.XToCharacter(1000));
+  }
+
+  // Now test the very large string and make sure it is handled properly by the
+  // length protection.
+  {
+    gfx::UniscribeState uniscribe(input.data(), static_cast<int>(input.size()),
+                                  false, hfont, script_cache, &properties_);
+    uniscribe.InitWithOptionalLengthProtection(true);
+
+    // There should be 0 runs and shapes.
+    EXPECT_EQ(0, uniscribe.runs_->size());
+    EXPECT_EQ(0, uniscribe.shapes_->size());
+    EXPECT_EQ(0, uniscribe.screen_order_->size());
+
+    EXPECT_EQ(0, uniscribe.Width());
+    EXPECT_EQ(0, uniscribe.FirstGlyphForCharacter(0));
+    EXPECT_EQ(0, uniscribe.FirstGlyphForCharacter(1000));
+    EXPECT_EQ(0, uniscribe.XToCharacter(0));
+    EXPECT_EQ(0, uniscribe.XToCharacter(1000));
+  }
+}
+
+}  // namespace gfx
\ No newline at end of file
diff --git a/base/gfx/vector_canvas.cc b/base/gfx/vector_canvas.cc
new file mode 100644
index 0000000..6f02f11
--- /dev/null
+++ b/base/gfx/vector_canvas.cc
@@ -0,0 +1,109 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/gfx/vector_canvas.h"
+
+#include "base/gfx/vector_device.h"
+#include "base/logging.h"
+
+namespace gfx {
+
+VectorCanvas::VectorCanvas() {
+}
+
+VectorCanvas::VectorCanvas(HDC dc, int width, int height) {
+  initialize(dc, width, height);
+}
+
+VectorCanvas::~VectorCanvas() {
+}
+
+void VectorCanvas::initialize(HDC context, int width, int height) {
+  SkDevice* device = createPlatformDevice(width, height, true, context);
+  setDevice(device);
+  device->unref();  // was created with refcount 1, and setDevice also refs
+}
+
+SkBounder* VectorCanvas::setBounder(SkBounder* bounder) {
+  if (!IsTopDeviceVectorial())
+    return PlatformCanvas::setBounder(bounder);
+
+  // This function isn't used in the code. Verify this assumption.
+  NOTREACHED();
+  return NULL;
+}
+
+SkDevice* VectorCanvas::createDevice(SkBitmap::Config config,
+                                     int width, int height,
+                                     bool is_opaque, bool isForLayer) {
+  DCHECK(config == SkBitmap::kARGB_8888_Config);
+  return createPlatformDevice(width, height, is_opaque, NULL);
+}
+
+SkDrawFilter* VectorCanvas::setDrawFilter(SkDrawFilter* filter) {
+  // This function isn't used in the code. Verify this assumption.
+  NOTREACHED();
+  return NULL;
+}
+
+SkDevice* VectorCanvas::createPlatformDevice(int width,
+                                             int height, bool is_opaque,
+                                             HANDLE shared_section) {
+  if (!is_opaque) {
+    // TODO(maruel):  http://b/1184002 1184002 When restoring a semi-transparent
+    // layer, i.e. merging it, we need to rasterize it because GDI doesn't
+    // support transparency except for AlphaBlend(). Right now, a
+    // BitmapPlatformDevice is created when VectorCanvas think a saveLayers()
+    // call is being done. The way to save a layer would be to create an
+    // EMF-based VectorDevice and have this device registers the drawing. When
+    // playing back the device into a bitmap, do it at the printer's dpi instead
+    // of the layout's dpi (which is much lower).
+    return PlatformCanvas::createPlatformDevice(width, height, is_opaque,
+                                                shared_section);
+  }
+
+  // TODO(maruel):  http://b/1183870 Look if it would be worth to increase the
+  // resolution by ~10x (any worthy factor) to increase the rendering precision
+  // (think about printing) while using a relatively low dpi. This happens
+  // because we receive float as input but the GDI functions works with
+  // integers. The idea is to premultiply the matrix with this factor and
+  // multiply each SkScalar that are passed to SkScalarRound(value) as
+  // SkScalarRound(value * 10). Safari is already doing the same for text
+  // rendering.
+  DCHECK(shared_section);
+  PlatformDevice* device = VectorDevice::create(
+      reinterpret_cast<HDC>(shared_section), width, height);
+  return device;
+}
+
+bool VectorCanvas::IsTopDeviceVectorial() const {
+  return getTopPlatformDevice().IsVectorial();
+}
+
+}  // namespace gfx
diff --git a/base/gfx/vector_canvas.h b/base/gfx/vector_canvas.h
new file mode 100644
index 0000000..d6a5703
--- /dev/null
+++ b/base/gfx/vector_canvas.h
@@ -0,0 +1,70 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_GFX_VECTOR_CANVAS_H__
+#define BASE_GFX_VECTOR_CANVAS_H__
+
+#include "base/gfx/platform_canvas.h"
+#include "base/gfx/vector_device.h"
+
+namespace gfx {
+
+// This class is a specialization of the regular PlatformCanvas. It is designed
+// to work with a VectorDevice to manage platform-specific drawing. It allows
+// using both Skia operations and platform-specific operations. It *doesn't*
+// support reading back from the bitmap backstore since it is not used.
+class VectorCanvas : public PlatformCanvas {
+ public:
+  VectorCanvas();
+  VectorCanvas(HDC dc, int width, int height);
+  virtual ~VectorCanvas();
+
+  // For two-part init, call if you use the no-argument constructor above
+  void initialize(HDC context, int width, int height);
+
+  virtual SkBounder* setBounder(SkBounder*);
+  virtual SkDevice* createDevice(SkBitmap::Config config,
+                                 int width, int height,
+                                 bool is_opaque, bool isForLayer);
+  virtual SkDrawFilter* setDrawFilter(SkDrawFilter* filter);
+
+ private:
+  // |is_opaque| is unused. |shared_section| is in fact the HDC used for output.
+  virtual SkDevice* createPlatformDevice(int width, int height, bool is_opaque,
+                                         HANDLE shared_section);
+
+  // Returns true if the top device is vector based and not bitmap based.
+  bool IsTopDeviceVectorial() const;
+
+  DISALLOW_EVIL_CONSTRUCTORS(VectorCanvas);
+};
+
+}  // namespace gfx
+
+#endif  // BASE_GFX_VECTOR_CANVAS_H__
diff --git a/base/gfx/vector_canvas_unittest.cc b/base/gfx/vector_canvas_unittest.cc
new file mode 100644
index 0000000..57ff34d
--- /dev/null
+++ b/base/gfx/vector_canvas_unittest.cc
@@ -0,0 +1,1032 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/gfx/vector_canvas.h"
+
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/gfx/bitmap_header.h"
+#include "base/gfx/png_decoder.h"
+#include "base/gfx/png_encoder.h"
+#include "base/gfx/size.h"
+#include "base/path_service.h"
+#include "base/string_util.h"
+#include "base/win_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "SkDashPathEffect.h"
+
+namespace {
+
+const wchar_t* const kGenerateSwitch = L"vector-canvas-generate";
+
+// Base class for unit test that uses data. It initializes a directory path
+// based on the test's name.
+class DataUnitTest : public testing::Test {
+ public:
+  DataUnitTest(const std::wstring& base_path) : base_path_(base_path) { }
+
+ protected:
+  // Load the test's data path.
+  virtual void SetUp() {
+    const testing::TestInfo& test_info =
+        *testing::UnitTest::GetInstance()->current_test_info();
+    PathService::Get(base::DIR_SOURCE_ROOT, &test_dir_);
+    file_util::AppendToPath(&test_dir_, base_path_);
+    file_util::AppendToPath(&test_dir_, L"data");
+    file_util::AppendToPath(&test_dir_,
+        ASCIIToWide(test_info.test_case_name()));
+    file_util::AppendToPath(&test_dir_, ASCIIToWide(test_info.name()));
+
+    // Hack for a quick lowercase. We assume all the tests names are ASCII.
+    std::string tmp(WideToASCII(test_dir_));
+    for (size_t i = 0; i < tmp.size(); ++i)
+      tmp[i] = ToLowerASCII(tmp[i]);
+    test_dir_ = ASCIIToWide(tmp);
+  }
+
+  // Returns the fully qualified path of directory containing test data files.
+  const std::wstring& test_dir() const {
+    return test_dir_;
+  }
+
+  // Returns the fully qualified path of a data file.
+  std::wstring test_file(const std::wstring& filename) const {
+    // Hack for a quick lowercase. We assume all the test data file names are
+    // ASCII.
+    std::string tmp(WideToASCII(filename));
+    for (size_t i = 0; i < tmp.size(); ++i)
+      tmp[i] = ToLowerASCII(tmp[i]);
+
+    std::wstring path(test_dir());
+    file_util::AppendToPath(&path, ASCIIToWide(tmp));
+    return path;
+  }
+
+ private:
+  // Path where the unit test is coming from: base, net, chrome, etc.
+  std::wstring base_path_;
+
+  // Path to directory used to contain the test data.
+  std::wstring test_dir_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(DataUnitTest);
+};
+
+// Lightweight HDC management.
+class Context {
+ public:
+  Context() : context_(CreateCompatibleDC(NULL)) {
+    EXPECT_TRUE(context_);
+  }
+  ~Context() {
+    DeleteDC(context_);
+  }
+
+  HDC context() const { return context_; }
+
+ private:
+  HDC context_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(Context);
+};
+
+// Lightweight HBITMAP management.
+class Bitmap {
+ public:
+  Bitmap(const Context& context, int x, int y) {
+    BITMAPINFOHEADER hdr;
+    gfx::CreateBitmapHeader(x, y, &hdr);
+    bitmap_ = CreateDIBSection(context.context(),
+                               reinterpret_cast<BITMAPINFO*>(&hdr), 0,
+                               &data_, NULL, 0);
+    EXPECT_TRUE(bitmap_);
+    EXPECT_TRUE(SelectObject(context.context(), bitmap_));
+  }
+  ~Bitmap() {
+    EXPECT_TRUE(DeleteObject(bitmap_));
+  }
+
+ private:
+  HBITMAP bitmap_;
+
+  void* data_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(Bitmap);
+};
+
+// Lightweight raw-bitmap management. The image, once initialized, is immuable.
+// It is mainly used for comparison.
+class Image {
+ public:
+  // Creates the image from the given filename on disk.
+  Image(const std::wstring& filename) : ignore_alpha_(true) {
+    std::string compressed;
+    file_util::ReadFileToString(filename, &compressed);
+    EXPECT_TRUE(compressed.size());
+
+    int w;
+    int h;
+    EXPECT_TRUE(PNGDecoder::Decode(
+        reinterpret_cast<const unsigned char*>(compressed.c_str()),
+        compressed.size(), PNGDecoder::FORMAT_BGRA, &data_, &w, &h));
+    size_.SetSize(w, h);
+    row_length_ = w * sizeof(uint32);
+  }
+
+  // Loads the image from a canvas.
+  Image(const gfx::PlatformCanvas& canvas) : ignore_alpha_(true) {
+    // Use a different way to access the bitmap. The normal way would be to
+    // query the SkBitmap.
+    HDC context = canvas.getTopPlatformDevice().getBitmapDC();
+    HGDIOBJ bitmap = GetCurrentObject(context, OBJ_BITMAP);
+    EXPECT_TRUE(bitmap != NULL);
+    // Initialize the clip region to the entire bitmap.
+    BITMAP bitmap_data;
+    EXPECT_EQ(GetObject(bitmap, sizeof(BITMAP), &bitmap_data),
+              sizeof(BITMAP));
+    size_.SetSize(bitmap_data.bmWidth, bitmap_data.bmHeight);
+    row_length_ = bitmap_data.bmWidthBytes;
+    size_t size = row_length_ * size_.height();
+    data_.resize(size);
+    memcpy(&*data_.begin(), bitmap_data.bmBits, size);
+  }
+
+  // Loads the image from a canvas.
+  Image(const SkBitmap& bitmap) : ignore_alpha_(true) {
+    SkAutoLockPixels lock(bitmap);
+    size_.SetSize(bitmap.width(), bitmap.height());
+    row_length_ = static_cast<int>(bitmap.rowBytes());
+    size_t size = row_length_ * size_.height();
+    data_.resize(size);
+    memcpy(&*data_.begin(), bitmap.getAddr(0, 0), size);
+  }
+
+  const gfx::Size& size() const {
+    return size_;
+  }
+
+  int row_length() const {
+    return row_length_;
+  }
+
+  // Save the image to a png file. Used to create the initial test files.
+  void SaveToFile(const std::wstring& filename) {
+    std::vector<unsigned char> compressed;
+    ASSERT_TRUE(PNGEncoder::Encode(&*data_.begin(),
+                                   PNGEncoder::FORMAT_BGRA,
+                                   size_.width(),
+                                   size_.height(),
+                                   row_length_,
+                                   true,
+                                   &compressed));
+    ASSERT_TRUE(compressed.size());
+    FILE* f;
+    ASSERT_EQ(_wfopen_s(&f, filename.c_str(), L"wbS"), 0);
+    ASSERT_EQ(fwrite(&*compressed.begin(), 1, compressed.size(), f),
+              compressed.size());
+    fclose(f);
+  }
+
+  // Returns the percentage of the image that is different from the other,
+  // between 0 and 100.
+  double PercentageDifferent(const Image& rhs) const {
+    if (size_ != rhs.size_ || row_length_ != rhs.row_length_ ||
+        size_.width() == 0 || size_.height() == 0)
+      return 100.;  // When of different size or empty, they are 100% different.
+
+    // Compute pixels different in the overlap
+    int pixels_different = 0;
+    for (int y = 0; y < size_.height(); ++y) {
+      for (int x = 0; x < size_.width(); ++x) {
+        uint32_t lhs_pixel = pixel_at(x, y);
+        uint32_t rhs_pixel = rhs.pixel_at(x, y);
+        if (lhs_pixel != rhs_pixel)
+          ++pixels_different;
+      }
+    }
+
+    // Like the WebKit ImageDiff tool, we define percentage different in terms
+    // of the size of the 'actual' bitmap.
+    double total_pixels = static_cast<double>(size_.width()) *
+                          static_cast<double>(size_.height());
+    return static_cast<double>(pixels_different) / total_pixels * 100.;
+  }
+
+  // Returns the 0x0RGB or 0xARGB value of the pixel at the given location,
+  // depending on ignore_alpha_.
+  uint32 pixel_at(int x, int y) const {
+    EXPECT_TRUE(x >= 0 && x < size_.width());
+    EXPECT_TRUE(y >= 0 && y < size_.height());
+    const uint32* data = reinterpret_cast<const uint32*>(&*data_.begin());
+    const uint32* data_row = data + y * row_length_ / sizeof(uint32);
+    if (ignore_alpha_)
+      return data_row[x] & 0xFFFFFF;  // Strip out A.
+    else
+      return data_row[x];
+  }
+
+ private:
+  // Pixel dimensions of the image.
+  gfx::Size size_;
+
+  // Length of a line in bytes.
+  int row_length_;
+
+  // Actual bitmap data in arrays of RGBAs (so when loaded as uint32, it's
+  // 0xABGR).
+  std::vector<unsigned char> data_;
+
+  // Flag to signal if the comparison functions should ignore the alpha channel.
+  const bool ignore_alpha_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(Image);
+};
+
+// Base for tests. Capability to process an image.
+class ImageTest : public DataUnitTest {
+ public:
+  typedef DataUnitTest parent;
+
+  // In what state is the test running.
+  enum ProcessAction {
+    GENERATE,
+    COMPARE,
+    NOOP,
+  };
+
+  ImageTest(const std::wstring& base_path, ProcessAction default_action)
+      : parent(base_path),
+        action_(default_action) {
+  }
+
+ protected:
+  virtual void SetUp() {
+    parent::SetUp();
+
+    if (action_ == GENERATE) {
+      // Make sure the directory exist.
+      file_util::CreateDirectory(test_dir());
+    }
+  }
+
+  // Compares or saves the bitmap currently loaded in the context, depending on
+  // kGenerating value. Returns 0 on success or any positive value between ]0,
+  // 100] on failure. The return value is the percentage of difference between
+  // the image in the file and the image in the canvas.
+  double ProcessCanvas(const gfx::PlatformCanvas& canvas,
+                       std::wstring filename) const {
+    filename +=  L".png";
+    switch (action_) {
+      case GENERATE:
+        SaveImage(canvas, filename);
+        return 0.;
+      case COMPARE:
+        return CompareImage(canvas, filename);
+      case NOOP:
+        return 0;
+      default:
+        // Invalid state, returns that the image is 100 different.
+        return 100.;
+    }
+  }
+
+  // Compares the bitmap currently loaded in the context with the file. Returns
+  // the percentage of pixel difference between both images, between 0 and 100.
+  double CompareImage(const gfx::PlatformCanvas& canvas,
+                    const std::wstring& filename) const {
+    Image image1(canvas);
+    Image image2(test_file(filename));
+    double diff = image1.PercentageDifferent(image2);
+    return diff;
+  }
+
+  // Saves the bitmap currently loaded in the context into the file.
+  void SaveImage(const gfx::PlatformCanvas& canvas,
+                 const std::wstring& filename) const {
+    Image(canvas).SaveToFile(test_file(filename));
+  }
+
+  ProcessAction action_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(ImageTest);
+};
+
+// Premultiply the Alpha channel on the R, B and G channels.
+void Premultiply(SkBitmap bitmap) {
+  SkAutoLockPixels lock(bitmap);
+  for (int x = 0; x < bitmap.width(); ++x) {
+    for (int y = 0; y < bitmap.height(); ++y) {
+      uint32_t* pixel_addr = bitmap.getAddr32(x, y);
+      uint32_t color = *pixel_addr;
+      BYTE alpha = SkColorGetA(color);
+      if (!alpha) {
+        *pixel_addr = 0;
+      } else {
+        BYTE alpha_offset = alpha / 2;
+        *pixel_addr = SkColorSetARGB(
+            SkColorGetA(color),
+            (SkColorGetR(color) * 255 + alpha_offset) / alpha,
+            (SkColorGetG(color) * 255 + alpha_offset) / alpha,
+            (SkColorGetB(color) * 255 + alpha_offset) / alpha);
+      }
+    }
+  }
+}
+
+void LoadPngFileToSkBitmap(const std::wstring& file, SkBitmap* bitmap) {
+  std::string compressed;
+  file_util::ReadFileToString(file, &compressed);
+  EXPECT_TRUE(compressed.size());
+  // Extra-lame. If you care, fix it.
+  std::vector<unsigned char> data;
+  data.assign(reinterpret_cast<const unsigned char*>(compressed.c_str()),
+              reinterpret_cast<const unsigned char*>(compressed.c_str() +
+                  compressed.size()));
+  EXPECT_TRUE(PNGDecoder::Decode(&data, bitmap));
+  EXPECT_FALSE(bitmap->isOpaque());
+  Premultiply(*bitmap);
+}
+
+}  // namespace
+
+// Streams an image.
+inline std::ostream& operator<<(std::ostream& out, const Image& image) {
+  return out << "Image(" << image.size() << ", " << image.row_length() << ")";
+}
+
+// Runs simultaneously the same drawing commands on VectorCanvas and
+// PlatformCanvas and compare the results.
+class VectorCanvasTest : public ImageTest {
+ public:
+  typedef ImageTest parent;
+
+  VectorCanvasTest() : parent(L"base", CurrentMode()), compare_canvas_(true) {
+  }
+
+ protected:
+  virtual void SetUp() {
+    parent::SetUp();
+    Init(100);
+    number_ = 0;
+  }
+
+  virtual void TearDown() {
+    delete pcanvas_;
+    pcanvas_ = NULL;
+
+    delete vcanvas_;
+    vcanvas_ = NULL;
+
+    delete bitmap_;
+    bitmap_ = NULL;
+
+    delete context_;
+    context_ = NULL;
+
+    parent::TearDown();
+  }
+
+  void Init(int size) {
+    size_ = size;
+    context_ = new Context();
+    bitmap_ = new Bitmap(*context_, size_, size_);
+    vcanvas_ = new gfx::VectorCanvas(context_->context(), size_, size_);
+    pcanvas_ = new gfx::PlatformCanvas(size_, size_, false);
+
+    // Clear white.
+    vcanvas_->drawARGB(255, 255, 255, 255, SkPorterDuff::kSrc_Mode);
+    pcanvas_->drawARGB(255, 255, 255, 255, SkPorterDuff::kSrc_Mode);
+  }
+
+  // Compares both canvas and returns the pixel difference in percentage between
+  // both images. 0 on success and ]0, 100] on failure.
+  double ProcessImage(const std::wstring& filename) {
+    std::wstring number(StringPrintf(L"%02d_", number_++));
+    double diff1 = parent::ProcessCanvas(*vcanvas_, number + L"vc_" + filename);
+    double diff2 = parent::ProcessCanvas(*pcanvas_, number + L"pc_" + filename);
+    if (!compare_canvas_)
+      return std::max(diff1, diff2);
+
+    Image image1(*vcanvas_);
+    Image image2(*pcanvas_);
+    double diff = image1.PercentageDifferent(image2);
+    return std::max(std::max(diff1, diff2), diff);
+  }
+
+  // Returns COMPARE, which is the default. If kGenerateSwitch command
+  // line argument is used to start this process, GENERATE is returned instead.
+  static ProcessAction CurrentMode() {
+    return CommandLine().HasSwitch(kGenerateSwitch) ? GENERATE : COMPARE;
+  }
+
+  // Length in x and y of the square canvas.
+  int size_;
+
+  // Current image number in the current test. Used to number of test files.
+  int number_;
+
+  // A temporary HDC to draw into.
+  Context* context_;
+
+  // Bitmap created inside context_.
+  Bitmap* bitmap_;
+
+  // Vector based canvas.
+  gfx::VectorCanvas* vcanvas_;
+
+  // Pixel based canvas.
+  gfx::PlatformCanvas* pcanvas_;
+
+  // When true (default), vcanvas_ and pcanvas_ contents are compared and
+  // verified to be identical.
+  bool compare_canvas_;
+};
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Actual tests
+
+TEST_F(VectorCanvasTest, Uninitialized) {
+  // Do a little mubadumba do get uninitialized stuff.
+  VectorCanvasTest::TearDown();
+
+  // The goal is not to verify that have the same uninitialized data.
+  compare_canvas_ = false;
+
+  context_ = new Context();
+  bitmap_ = new Bitmap(*context_, size_, size_);
+  vcanvas_ = new gfx::VectorCanvas(context_->context(), size_, size_);
+  pcanvas_ = new gfx::PlatformCanvas(size_, size_, false);
+
+  // VectorCanvas default initialization is black.
+  // PlatformCanvas default initialization is almost white 0x01FFFEFD (invalid
+  // Skia color) in both Debug and Release. See magicTransparencyColor in
+  // platform_device.cc
+  EXPECT_EQ(0., ProcessImage(L"empty"));
+}
+
+TEST_F(VectorCanvasTest, BasicDrawing) {
+  EXPECT_EQ(Image(*vcanvas_).PercentageDifferent(Image(*pcanvas_)), 0.)
+      << L"clean";
+  EXPECT_EQ(0., ProcessImage(L"clean"));
+
+  // Clear white.
+  {
+    vcanvas_->drawARGB(255, 255, 255, 255, SkPorterDuff::kSrc_Mode);
+    pcanvas_->drawARGB(255, 255, 255, 255, SkPorterDuff::kSrc_Mode);
+  }
+  EXPECT_EQ(0., ProcessImage(L"drawARGB"));
+
+  // Diagonal line top-left to bottom-right.
+  {
+    SkPaint paint;
+    // Default color is black.
+    vcanvas_->drawLine(10, 10, 90, 90, paint);
+    pcanvas_->drawLine(10, 10, 90, 90, paint);
+  }
+  EXPECT_EQ(0., ProcessImage(L"drawLine_black"));
+
+  // Rect.
+  {
+    SkPaint paint;
+    paint.setColor(SK_ColorGREEN);
+    vcanvas_->drawRectCoords(25, 25, 75, 75, paint);
+    pcanvas_->drawRectCoords(25, 25, 75, 75, paint);
+  }
+  EXPECT_EQ(0., ProcessImage(L"drawRect_green"));
+
+  // A single-point rect doesn't leave any mark.
+  {
+    SkPaint paint;
+    paint.setColor(SK_ColorBLUE);
+    vcanvas_->drawRectCoords(5, 5, 5, 5, paint);
+    pcanvas_->drawRectCoords(5, 5, 5, 5, paint);
+  }
+  EXPECT_EQ(0., ProcessImage(L"drawRect_noop"));
+
+  // Rect.
+  {
+    SkPaint paint;
+    paint.setColor(SK_ColorBLUE);
+    vcanvas_->drawRectCoords(75, 50, 80, 55, paint);
+    pcanvas_->drawRectCoords(75, 50, 80, 55, paint);
+  }
+  EXPECT_EQ(0., ProcessImage(L"drawRect_noop"));
+
+  // Empty again
+  {
+    vcanvas_->drawPaint(SkPaint());
+    pcanvas_->drawPaint(SkPaint());
+  }
+  EXPECT_EQ(0., ProcessImage(L"drawPaint_black"));
+
+  // Horizontal line left to right.
+  {
+    SkPaint paint;
+    paint.setColor(SK_ColorRED);
+    vcanvas_->drawLine(10, 20, 90, 20, paint);
+    pcanvas_->drawLine(10, 20, 90, 20, paint);
+  }
+  EXPECT_EQ(0., ProcessImage(L"drawLine_left_to_right"));
+
+  // Vertical line downward.
+  {
+    SkPaint paint;
+    paint.setColor(SK_ColorRED);
+    vcanvas_->drawLine(30, 10, 30, 90, paint);
+    pcanvas_->drawLine(30, 10, 30, 90, paint);
+  }
+  EXPECT_EQ(0., ProcessImage(L"drawLine_red"));
+}
+
+TEST_F(VectorCanvasTest, Circles) {
+  // There is NO WAY to make them agree. At least verify that the output doesn't
+  // change across versions. This test is disabled. See bug 1060231.
+  compare_canvas_ = false;
+
+  // Stroked Circle.
+  {
+    SkPaint paint;
+    SkPath path;
+    path.addCircle(50, 75, 10);
+    paint.setStyle(SkPaint::kStroke_Style);
+    paint.setColor(SK_ColorMAGENTA);
+    vcanvas_->drawPath(path, paint);
+    pcanvas_->drawPath(path, paint);
+  }
+  EXPECT_EQ(0., ProcessImage(L"circle_stroke"));
+
+  // Filled Circle.
+  {
+    SkPaint paint;
+    SkPath path;
+    path.addCircle(50, 25, 10);
+    paint.setStyle(SkPaint::kFill_Style);
+    vcanvas_->drawPath(path, paint);
+    pcanvas_->drawPath(path, paint);
+  }
+  EXPECT_EQ(0., ProcessImage(L"circle_fill"));
+
+  // Stroked Circle over.
+  {
+    SkPaint paint;
+    SkPath path;
+    path.addCircle(50, 25, 10);
+    paint.setStyle(SkPaint::kStroke_Style);
+    paint.setColor(SK_ColorBLUE);
+    vcanvas_->drawPath(path, paint);
+    pcanvas_->drawPath(path, paint);
+  }
+  EXPECT_EQ(0., ProcessImage(L"circle_over_strike"));
+
+  // Stroke and Fill Circle.
+  {
+    SkPaint paint;
+    SkPath path;
+    path.addCircle(12, 50, 10);
+    paint.setStyle(SkPaint::kStrokeAndFill_Style);
+    paint.setColor(SK_ColorRED);
+    vcanvas_->drawPath(path, paint);
+    pcanvas_->drawPath(path, paint);
+  }
+  EXPECT_EQ(0., ProcessImage(L"circle_stroke_and_fill"));
+
+  // Line + Quad + Cubic.
+  {
+    SkPaint paint;
+    SkPath path;
+    paint.setStyle(SkPaint::kStroke_Style);
+    paint.setColor(SK_ColorGREEN);
+    path.moveTo(1, 1);
+    path.lineTo(60, 40);
+    path.lineTo(80, 80);
+    path.quadTo(20, 50, 10, 90);
+    path.quadTo(50, 20, 90, 10);
+    path.cubicTo(20, 40, 50, 50, 10, 10);
+    path.cubicTo(30, 20, 50, 50, 90, 10);
+    path.addRect(90, 90, 95, 96);
+    vcanvas_->drawPath(path, paint);
+    pcanvas_->drawPath(path, paint);
+  }
+  EXPECT_EQ(0., ProcessImage(L"mixed_stroke"));
+}
+
+TEST_F(VectorCanvasTest, LineOrientation) {
+  // There is NO WAY to make them agree. At least verify that the output doesn't
+  // change across versions. This test is disabled. See bug 1060231.
+  compare_canvas_ = false;
+
+  // Horizontal lines.
+  {
+    SkPaint paint;
+    paint.setColor(SK_ColorRED);
+    // Left to right.
+    vcanvas_->drawLine(10, 20, 90, 20, paint);
+    pcanvas_->drawLine(10, 20, 90, 20, paint);
+    // Right to left.
+    vcanvas_->drawLine(90, 30, 10, 30, paint);
+    pcanvas_->drawLine(90, 30, 10, 30, paint);
+  }
+  EXPECT_EQ(0., ProcessImage(L"horizontal"));
+
+  // Vertical lines.
+  {
+    SkPaint paint;
+    paint.setColor(SK_ColorRED);
+    // Top down.
+    vcanvas_->drawLine(20, 10, 20, 90, paint);
+    pcanvas_->drawLine(20, 10, 20, 90, paint);
+    // Bottom up.
+    vcanvas_->drawLine(30, 90, 30, 10, paint);
+    pcanvas_->drawLine(30, 90, 30, 10, paint);
+  }
+  EXPECT_EQ(0., ProcessImage(L"vertical"));
+
+  // Try again with a 180 degres rotation.
+  vcanvas_->rotate(180);
+  pcanvas_->rotate(180);
+
+  // Horizontal lines (rotated).
+  {
+    SkPaint paint;
+    paint.setColor(SK_ColorRED);
+    vcanvas_->drawLine(-10, -25, -90, -25, paint);
+    pcanvas_->drawLine(-10, -25, -90, -25, paint);
+    vcanvas_->drawLine(-90, -35, -10, -35, paint);
+    pcanvas_->drawLine(-90, -35, -10, -35, paint);
+  }
+  EXPECT_EQ(0., ProcessImage(L"horizontal_180"));
+
+  // Vertical lines (rotated).
+  {
+    SkPaint paint;
+    paint.setColor(SK_ColorRED);
+    vcanvas_->drawLine(-25, -10, -25, -90, paint);
+    pcanvas_->drawLine(-25, -10, -25, -90, paint);
+    vcanvas_->drawLine(-35, -90, -35, -10, paint);
+    pcanvas_->drawLine(-35, -90, -35, -10, paint);
+  }
+  EXPECT_EQ(0., ProcessImage(L"vertical_180"));
+}
+
+TEST_F(VectorCanvasTest, PathOrientation) {
+  // There is NO WAY to make them agree. At least verify that the output doesn't
+  // change across versions. This test is disabled. See bug 1060231.
+  compare_canvas_ = false;
+
+  // Horizontal lines.
+  {
+    SkPaint paint;
+    paint.setStyle(SkPaint::kStroke_Style);
+    paint.setColor(SK_ColorRED);
+    SkPath path;
+    SkPoint start;
+    start.set(10, 20);
+    SkPoint end;
+    end.set(90, 20);
+    path.moveTo(start);
+    path.lineTo(end);
+    vcanvas_->drawPath(path, paint);
+    pcanvas_->drawPath(path, paint);
+  }
+  EXPECT_EQ(0., ProcessImage(L"drawPath_ltr"));
+
+  // Horizontal lines.
+  {
+    SkPaint paint;
+    paint.setStyle(SkPaint::kStroke_Style);
+    paint.setColor(SK_ColorRED);
+    SkPath path;
+    SkPoint start;
+    start.set(90, 30);
+    SkPoint end;
+    end.set(10, 30);
+    path.moveTo(start);
+    path.lineTo(end);
+    vcanvas_->drawPath(path, paint);
+    pcanvas_->drawPath(path, paint);
+  }
+  EXPECT_EQ(0., ProcessImage(L"drawPath_rtl"));
+}
+
+TEST_F(VectorCanvasTest, DiagonalLines) {
+  SkPaint paint;
+  paint.setColor(SK_ColorRED);
+
+  vcanvas_->drawLine(10, 10, 90, 90, paint);
+  pcanvas_->drawLine(10, 10, 90, 90, paint);
+  EXPECT_EQ(0., ProcessImage(L"nw-se"));
+
+  // Starting here, there is NO WAY to make them agree. At least verify that the
+  // output doesn't change across versions. This test is disabled. See bug
+  // 1060231.
+  compare_canvas_ = false;
+
+  vcanvas_->drawLine(10, 95, 90, 15, paint);
+  pcanvas_->drawLine(10, 95, 90, 15, paint);
+  EXPECT_EQ(0., ProcessImage(L"sw-ne"));
+
+  vcanvas_->drawLine(90, 10, 10, 90, paint);
+  pcanvas_->drawLine(90, 10, 10, 90, paint);
+  EXPECT_EQ(0., ProcessImage(L"ne-sw"));
+
+  vcanvas_->drawLine(95, 90, 15, 10, paint);
+  pcanvas_->drawLine(95, 90, 15, 10, paint);
+  EXPECT_EQ(0., ProcessImage(L"se-nw"));
+}
+
+TEST_F(VectorCanvasTest, PathEffects) {
+  {
+    SkPaint paint;
+    SkScalar intervals[] = { 1, 1 };
+    SkPathEffect* effect = new SkDashPathEffect(intervals, arraysize(intervals),
+                                                0);
+    paint.setPathEffect(effect)->unref();
+    paint.setColor(SK_ColorMAGENTA);
+    paint.setStyle(SkPaint::kStroke_Style);
+
+    vcanvas_->drawLine(10, 10, 90, 10, paint);
+    pcanvas_->drawLine(10, 10, 90, 10, paint);
+  }
+  EXPECT_EQ(0., ProcessImage(L"dash_line"));
+
+
+  // Starting here, there is NO WAY to make them agree. At least verify that the
+  // output doesn't change across versions. This test is disabled. See bug
+  // 1060231.
+  compare_canvas_ = false;
+
+  {
+    SkPaint paint;
+    SkScalar intervals[] = { 3, 5 };
+    SkPathEffect* effect = new SkDashPathEffect(intervals, arraysize(intervals),
+                                                0);
+    paint.setPathEffect(effect)->unref();
+    paint.setColor(SK_ColorMAGENTA);
+    paint.setStyle(SkPaint::kStroke_Style);
+
+    SkPath path;
+    path.moveTo(10, 15);
+    path.lineTo(90, 15);
+    path.lineTo(90, 90);
+    vcanvas_->drawPath(path, paint);
+    pcanvas_->drawPath(path, paint);
+  }
+  EXPECT_EQ(0., ProcessImage(L"dash_path"));
+
+  {
+    SkPaint paint;
+    SkScalar intervals[] = { 2, 1 };
+    SkPathEffect* effect = new SkDashPathEffect(intervals, arraysize(intervals),
+                                                0);
+    paint.setPathEffect(effect)->unref();
+    paint.setColor(SK_ColorMAGENTA);
+    paint.setStyle(SkPaint::kStroke_Style);
+
+    vcanvas_->drawRectCoords(20, 20, 30, 30, paint);
+    pcanvas_->drawRectCoords(20, 20, 30, 30, paint);
+  }
+  EXPECT_EQ(0., ProcessImage(L"dash_rect"));
+
+  // This thing looks like it has been drawn by a 3 years old kid. I haven't
+  // filed a bug on this since I guess nobody is expecting this to look nice.
+  {
+    SkPaint paint;
+    SkScalar intervals[] = { 1, 1 };
+    SkPathEffect* effect = new SkDashPathEffect(intervals, arraysize(intervals),
+                                                0);
+    paint.setPathEffect(effect)->unref();
+    paint.setColor(SK_ColorMAGENTA);
+    paint.setStyle(SkPaint::kStroke_Style);
+
+    SkPath path;
+    path.addCircle(50, 75, 10);
+    vcanvas_->drawPath(path, paint);
+    pcanvas_->drawPath(path, paint);
+    EXPECT_EQ(0., ProcessImage(L"circle"));
+  }
+}
+
+TEST_F(VectorCanvasTest, Bitmaps) {
+  // ICM is enabled on VectorCanvas only on Windows 2000 so bitmap-based tests
+  // can't compare the pixels between PlatformCanvas and VectorCanvas. We don't
+  // really care about Windows 2000 pixel colors.
+  if (win_util::GetWinVersion() <= win_util::WINVERSION_2000)
+    return;
+  {
+    SkBitmap bitmap;
+    LoadPngFileToSkBitmap(test_file(L"bitmap_opaque.png"), &bitmap);
+    vcanvas_->drawBitmap(bitmap, 13, 3, NULL);
+    pcanvas_->drawBitmap(bitmap, 13, 3, NULL);
+    EXPECT_EQ(0., ProcessImage(L"opaque"));
+  }
+
+  {
+    SkBitmap bitmap;
+    LoadPngFileToSkBitmap(test_file(L"bitmap_alpha.png"), &bitmap);
+    vcanvas_->drawBitmap(bitmap, 5, 15, NULL);
+    pcanvas_->drawBitmap(bitmap, 5, 15, NULL);
+    EXPECT_EQ(0., ProcessImage(L"alpha"));
+  }
+}
+
+TEST_F(VectorCanvasTest, ClippingRect) {
+  // ICM is enabled on VectorCanvas only on Windows 2000 so bitmap-based tests
+  // can't compare the pixels between PlatformCanvas and VectorCanvas. We don't
+  // really care about Windows 2000 pixel colors.
+  if (win_util::GetWinVersion() <= win_util::WINVERSION_2000)
+    return;
+  SkBitmap bitmap;
+  LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap);
+  SkRect rect;
+  rect.fLeft = 2;
+  rect.fTop = 2;
+  rect.fRight = 30.5f;
+  rect.fBottom = 30.5f;
+  vcanvas_->clipRect(rect);
+  pcanvas_->clipRect(rect);
+
+  vcanvas_->drawBitmap(bitmap, 13, 3, NULL);
+  pcanvas_->drawBitmap(bitmap, 13, 3, NULL);
+  EXPECT_EQ(0., ProcessImage(L"rect"));
+}
+
+TEST_F(VectorCanvasTest, ClippingPath) {
+  // ICM is enabled on VectorCanvas only on Windows 2000 so bitmap-based tests
+  // can't compare the pixels between PlatformCanvas and VectorCanvas. We don't
+  // really care about Windows 2000 pixel colors.
+  if (win_util::GetWinVersion() <= win_util::WINVERSION_2000)
+    return;
+  SkBitmap bitmap;
+  LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap);
+  SkPath path;
+  path.addCircle(20, 20, 10);
+  vcanvas_->clipPath(path);
+  pcanvas_->clipPath(path);
+
+  vcanvas_->drawBitmap(bitmap, 14, 3, NULL);
+  pcanvas_->drawBitmap(bitmap, 14, 3, NULL);
+  EXPECT_EQ(0., ProcessImage(L"path"));
+}
+
+TEST_F(VectorCanvasTest, ClippingCombined) {
+  // ICM is enabled on VectorCanvas only on Windows 2000 so bitmap-based tests
+  // can't compare the pixels between PlatformCanvas and VectorCanvas. We don't
+  // really care about Windows 2000 pixel colors.
+  if (win_util::GetWinVersion() <= win_util::WINVERSION_2000)
+    return;
+  SkBitmap bitmap;
+  LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap);
+
+  SkRect rect;
+  rect.fLeft = 2;
+  rect.fTop = 2;
+  rect.fRight = 30.5f;
+  rect.fBottom = 30.5f;
+  vcanvas_->clipRect(rect);
+  pcanvas_->clipRect(rect);
+  SkPath path;
+  path.addCircle(20, 20, 10);
+  vcanvas_->clipPath(path, SkRegion::kUnion_Op);
+  pcanvas_->clipPath(path, SkRegion::kUnion_Op);
+
+  vcanvas_->drawBitmap(bitmap, 15, 3, NULL);
+  pcanvas_->drawBitmap(bitmap, 15, 3, NULL);
+  EXPECT_EQ(0., ProcessImage(L"combined"));
+}
+
+TEST_F(VectorCanvasTest, ClippingIntersect) {
+  // ICM is enabled on VectorCanvas only on Windows 2000 so bitmap-based tests
+  // can't compare the pixels between PlatformCanvas and VectorCanvas. We don't
+  // really care about Windows 2000 pixel colors.
+  if (win_util::GetWinVersion() <= win_util::WINVERSION_2000)
+    return;
+  SkBitmap bitmap;
+  LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap);
+
+  SkRect rect;
+  rect.fLeft = 2;
+  rect.fTop = 2;
+  rect.fRight = 30.5f;
+  rect.fBottom = 30.5f;
+  vcanvas_->clipRect(rect);
+  pcanvas_->clipRect(rect);
+  SkPath path;
+  path.addCircle(23, 23, 15);
+  vcanvas_->clipPath(path);
+  pcanvas_->clipPath(path);
+
+  vcanvas_->drawBitmap(bitmap, 15, 3, NULL);
+  pcanvas_->drawBitmap(bitmap, 15, 3, NULL);
+  EXPECT_EQ(0., ProcessImage(L"intersect"));
+}
+
+TEST_F(VectorCanvasTest, ClippingClean) {
+  // ICM is enabled on VectorCanvas only on Windows 2000 so bitmap-based tests
+  // can't compare the pixels between PlatformCanvas and VectorCanvas. We don't
+  // really care about Windows 2000 pixel colors.
+  if (win_util::GetWinVersion() <= win_util::WINVERSION_2000)
+    return;
+  SkBitmap bitmap;
+  LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap);
+  {
+    SkRegion old_region(pcanvas_->getTotalClip());
+    SkRect rect;
+    rect.fLeft = 2;
+    rect.fTop = 2;
+    rect.fRight = 30.5f;
+    rect.fBottom = 30.5f;
+    vcanvas_->clipRect(rect);
+    pcanvas_->clipRect(rect);
+
+    vcanvas_->drawBitmap(bitmap, 15, 3, NULL);
+    pcanvas_->drawBitmap(bitmap, 15, 3, NULL);
+    EXPECT_EQ(0., ProcessImage(L"clipped"));
+    vcanvas_->clipRegion(old_region, SkRegion::kReplace_Op);
+    pcanvas_->clipRegion(old_region, SkRegion::kReplace_Op);
+  }
+  {
+    // Verify that the clipping region has been fixed back.
+    vcanvas_->drawBitmap(bitmap, 55, 3, NULL);
+    pcanvas_->drawBitmap(bitmap, 55, 3, NULL);
+    EXPECT_EQ(0., ProcessImage(L"unclipped"));
+  }
+}
+
+TEST_F(VectorCanvasTest, Matrix) {
+  // ICM is enabled on VectorCanvas only on Windows 2000 so bitmap-based tests
+  // can't compare the pixels between PlatformCanvas and VectorCanvas. We don't
+  // really care about Windows 2000 pixel colors.
+  if (win_util::GetWinVersion() <= win_util::WINVERSION_2000)
+    return;
+  SkBitmap bitmap;
+  LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap);
+  {
+    vcanvas_->translate(15, 3);
+    pcanvas_->translate(15, 3);
+    vcanvas_->drawBitmap(bitmap, 0, 0, NULL);
+    pcanvas_->drawBitmap(bitmap, 0, 0, NULL);
+    EXPECT_EQ(0., ProcessImage(L"translate1"));
+  }
+  {
+    vcanvas_->translate(-30, -23);
+    pcanvas_->translate(-30, -23);
+    vcanvas_->drawBitmap(bitmap, 0, 0, NULL);
+    pcanvas_->drawBitmap(bitmap, 0, 0, NULL);
+    EXPECT_EQ(0., ProcessImage(L"translate2"));
+  }
+  vcanvas_->resetMatrix();
+  pcanvas_->resetMatrix();
+
+  // For scaling and rotation, they use a different algorithm (nearest
+  // neighborhood vs smoothing). At least verify that the output doesn't change
+  // across versions.
+  compare_canvas_ = false;
+
+  {
+    vcanvas_->scale(SkDoubleToScalar(1.9), SkDoubleToScalar(1.5));
+    pcanvas_->scale(SkDoubleToScalar(1.9), SkDoubleToScalar(1.5));
+    vcanvas_->drawBitmap(bitmap, 1, 1, NULL);
+    pcanvas_->drawBitmap(bitmap, 1, 1, NULL);
+    EXPECT_EQ(0., ProcessImage(L"scale"));
+  }
+  vcanvas_->resetMatrix();
+  pcanvas_->resetMatrix();
+
+  {
+    vcanvas_->rotate(67);
+    pcanvas_->rotate(67);
+    vcanvas_->drawBitmap(bitmap, 20, -50, NULL);
+    pcanvas_->drawBitmap(bitmap, 20, -50, NULL);
+    EXPECT_EQ(0., ProcessImage(L"rotate"));
+  }
+}
diff --git a/base/gfx/vector_device.cc b/base/gfx/vector_device.cc
new file mode 100644
index 0000000..c7e20f6
--- /dev/null
+++ b/base/gfx/vector_device.cc
@@ -0,0 +1,646 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/gfx/vector_device.h"
+
+#include "base/gfx/bitmap_header.h"
+#include "base/gfx/skia_utils.h"
+#include "base/logging.h"
+#include "base/scoped_handle.h"
+
+#include "SkUtils.h"
+
+namespace gfx {
+
+VectorDevice* VectorDevice::create(HDC dc, int width, int height) {
+  InitializeDC(dc);
+
+  // Link the SkBitmap to the current selected bitmap in the device context.
+  SkBitmap bitmap;
+  HGDIOBJ selected_bitmap = GetCurrentObject(dc, OBJ_BITMAP);
+  bool succeeded = false;
+  if (selected_bitmap != NULL) {
+    BITMAP bitmap_data;
+    if (GetObject(selected_bitmap, sizeof(BITMAP), &bitmap_data) ==
+        sizeof(BITMAP)) {
+      // The context has a bitmap attached. Attach our SkBitmap to it.
+      // Warning: If the bitmap gets unselected from the HDC, VectorDevice has
+      // no way to detect this, so the HBITMAP could be released while SkBitmap
+      // still has a reference to it. Be cautious.
+      if (width == bitmap_data.bmWidth &&
+          height == bitmap_data.bmHeight) {
+        bitmap.setConfig(SkBitmap::kARGB_8888_Config,
+                         bitmap_data.bmWidth,
+                         bitmap_data.bmHeight,
+                         bitmap_data.bmWidthBytes);
+        bitmap.setPixels(bitmap_data.bmBits);
+        succeeded = true;
+      }
+    }
+  }
+
+  if (!succeeded)
+    bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
+
+  return new VectorDevice(dc, bitmap);
+}
+
+VectorDevice::VectorDevice(HDC dc, const SkBitmap& bitmap)
+    : PlatformDevice(bitmap),
+      hdc_(dc),
+      previous_brush_(NULL),
+      previous_pen_(NULL),
+      offset_x_(0),
+      offset_y_(0) {
+  transform_.reset();
+}
+
+VectorDevice::~VectorDevice() {
+  DCHECK(previous_brush_ == NULL);
+  DCHECK(previous_pen_ == NULL);
+}
+
+
+void VectorDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
+  // TODO(maruel):  Bypass the current transformation matrix.
+  SkRect rect;
+  rect.fLeft = 0;
+  rect.fTop = 0;
+  rect.fRight = SkIntToScalar(width() + 1);
+  rect.fBottom = SkIntToScalar(height() + 1);
+  drawRect(draw, rect, paint);
+}
+
+void VectorDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode,
+                              size_t count, const SkPoint pts[],
+                              const SkPaint& paint) {
+  if (!count)
+    return;
+
+  if (mode == SkCanvas::kPoints_PointMode) {
+    NOTREACHED();
+    return;
+  }
+
+  SkPaint tmp_paint(paint);
+  tmp_paint.setStyle(SkPaint::kStroke_Style);
+
+  // Draw a path instead.
+  SkPath path;
+  switch (mode) {
+    case SkCanvas::kLines_PointMode:
+      if (count % 2) {
+        NOTREACHED();
+        return;
+      }
+      for (size_t i = 0; i < count / 2; ++i) {
+        path.moveTo(pts[2 * i]);
+        path.lineTo(pts[2 * i + 1]);
+      }
+      break;
+    case SkCanvas::kPolygon_PointMode:
+      path.moveTo(pts[0]);
+      for (size_t i = 1; i < count; ++i) {
+        path.lineTo(pts[i]);
+      }
+      break;
+    default:
+      NOTREACHED();
+      return;
+  }
+  // Draw the calculated path.
+  drawPath(draw, path, tmp_paint);
+}
+
+void VectorDevice::drawRect(const SkDraw& draw, const SkRect& rect,
+                            const SkPaint& paint) {
+  if (paint.getPathEffect()) {
+    // Draw a path instead.
+    SkPath path_orginal;
+    path_orginal.addRect(rect);
+
+    // Apply the path effect to the rect.
+    SkPath path_modified;
+    paint.getFillPath(path_orginal, &path_modified);
+
+    // Removes the path effect from the temporary SkPaint object.
+    SkPaint paint_no_effet(paint);
+    paint_no_effet.setPathEffect(NULL)->safeUnref();
+
+    // Draw the calculated path.
+    drawPath(draw, path_modified, paint_no_effet);
+    return;
+  }
+
+  if (!ApplyPaint(paint)) {
+    return;
+  }
+  HDC dc = getBitmapDC();
+  if (!Rectangle(dc, SkScalarRound(rect.fLeft),
+                 SkScalarRound(rect.fTop),
+                 SkScalarRound(rect.fRight),
+                 SkScalarRound(rect.fBottom))) {
+    NOTREACHED();
+  }
+  Cleanup();
+}
+
+void VectorDevice::drawPath(const SkDraw& draw, const SkPath& path,
+                            const SkPaint& paint) {
+  if (paint.getPathEffect()) {
+    // Apply the path effect forehand.
+    SkPath path_modified;
+    paint.getFillPath(path, &path_modified);
+
+    // Removes the path effect from the temporary SkPaint object.
+    SkPaint paint_no_effet(paint);
+    paint_no_effet.setPathEffect(NULL)->safeUnref();
+
+    // Draw the calculated path.
+    drawPath(draw, path_modified, paint_no_effet);
+    return;
+  }
+
+  if (!ApplyPaint(paint)) {
+    return;
+  }
+  HDC dc = getBitmapDC();
+  PlatformDevice::LoadPathToDC(dc, path);
+  switch (paint.getStyle()) {
+    case SkPaint::kFill_Style: {
+      BOOL res = StrokeAndFillPath(dc);
+      DCHECK(res != 0);
+      break;
+    }
+    case SkPaint::kStroke_Style: {
+      BOOL res = StrokePath(dc);
+      DCHECK(res != 0);
+      break;
+    }
+    case SkPaint::kStrokeAndFill_Style: {
+      BOOL res = StrokeAndFillPath(dc);
+      DCHECK(res != 0);
+      break;
+    }
+    default:
+      NOTREACHED();
+      break;
+  }
+  Cleanup();
+}
+
+void VectorDevice::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap,
+                              const SkMatrix& matrix, const SkPaint& paint) {
+  // Load the temporary matrix. This is what will translate, rotate and resize
+  // the bitmap.
+  SkMatrix actual_transform(transform_);
+  actual_transform.preConcat(matrix);
+  LoadTransformToDC(hdc_, actual_transform);
+
+  InternalDrawBitmap(bitmap, 0, 0, paint);
+
+  // Restore the original matrix.
+  LoadTransformToDC(hdc_, transform_);
+}
+
+void VectorDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
+                              int x, int y, const SkPaint& paint) {
+  SkMatrix identity;
+  identity.reset();
+  LoadTransformToDC(hdc_, identity);
+
+  InternalDrawBitmap(bitmap, x, y, paint);
+
+  // Restore the original matrix.
+  LoadTransformToDC(hdc_, transform_);
+}
+
+void VectorDevice::drawText(const SkDraw& draw, const void* text, size_t byteLength,
+                            SkScalar x, SkScalar y, const SkPaint& paint) {
+  // This function isn't used in the code. Verify this assumption.
+  NOTREACHED();
+}
+
+void VectorDevice::drawPosText(const SkDraw& draw, const void* text, size_t len,
+                               const SkScalar pos[], SkScalar constY,
+                               int scalarsPerPos, const SkPaint& paint) {
+  // This function isn't used in the code. Verify this assumption.
+  NOTREACHED();
+}
+
+void VectorDevice::drawTextOnPath(const SkDraw& draw, const void* text,
+                                  size_t len,
+                                  const SkPath& path, const SkMatrix* matrix,
+                                  const SkPaint& paint) {
+  // This function isn't used in the code. Verify this assumption.
+  NOTREACHED();
+}
+
+void VectorDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,
+                                int vertexCount,
+                                const SkPoint vertices[], const SkPoint texs[],
+                                const SkColor colors[], SkXfermode* xmode,
+                                const uint16_t indices[], int indexCount,
+                                const SkPaint& paint) {
+  // This function isn't used in the code. Verify this assumption.
+  NOTREACHED();
+}
+
+void VectorDevice::drawDevice(const SkDraw& draw, SkDevice* device, int x,
+                              int y, const SkPaint& paint) {
+  // TODO(maruel):  http://b/1183870 Playback the EMF buffer at printer's dpi if
+  // it is a vectorial device.
+  drawSprite(draw, device->accessBitmap(false), x, y, paint);
+}
+
+bool VectorDevice::ApplyPaint(const SkPaint& paint) {
+  // Note: The goal here is to transfert the SkPaint's state to the HDC's state.
+  // This function does not execute the SkPaint drawing commands. These should
+  // be executed in drawPaint().
+
+  SkPaint::Style style = paint.getStyle();
+  if (!paint.getAlpha())
+    style = SkPaint::kStyleCount;
+
+  switch (style) {
+    case SkPaint::kFill_Style:
+      if (!CreateBrush(true, paint) ||
+          !CreatePen(false, paint))
+        return false;
+      break;
+    case SkPaint::kStroke_Style:
+      if (!CreateBrush(false, paint) ||
+          !CreatePen(true, paint))
+        return false;
+      break;
+    case SkPaint::kStrokeAndFill_Style:
+      if (!CreateBrush(true, paint) ||
+          !CreatePen(true, paint))
+        return false;
+      break;
+    default:
+      if (!CreateBrush(false, paint) ||
+          !CreatePen(false, paint))
+        return false;
+      break;
+  }
+
+  /*
+  getFlags();
+    isAntiAlias();
+    isDither()
+    isLinearText()
+    isSubpixelText()
+    isUnderlineText()
+    isStrikeThruText()
+    isFakeBoldText()
+    isDevKernText()
+    isFilterBitmap()
+
+  // Skia's text is not used. This should be fixed.
+  getTextAlign()
+  getTextScaleX()
+  getTextSkewX()
+  getTextEncoding()
+  getFontMetrics()
+  getFontSpacing()
+  */
+
+  // BUG 1094907: Implement shaders. Shaders currently in use:
+  //  SkShader::CreateBitmapShader
+  //  SkGradientShader::CreateRadial
+  //  SkGradientShader::CreateLinear
+  // DCHECK(!paint.getShader());
+
+  // http://b/1106647 Implement loopers and mask filter. Looper currently in
+  // use:
+  //   SkBlurDrawLooper is used for shadows.
+  // DCHECK(!paint.getLooper());
+  // DCHECK(!paint.getMaskFilter());
+
+  // http://b/1165900 Implement xfermode.
+  // DCHECK(!paint.getXfermode());
+
+  // The path effect should be processed before arriving here.
+  DCHECK(!paint.getPathEffect());
+
+  // These aren't used in the code. Verify this assumption.
+  DCHECK(!paint.getColorFilter());
+  DCHECK(!paint.getRasterizer());
+  // Reuse code to load Win32 Fonts.
+  DCHECK(!paint.getTypeface());
+  return true;
+}
+
+void VectorDevice::setMatrixClip(const SkMatrix& transform,
+                                 const SkRegion& region) {
+  transform_ = transform;
+  LoadTransformToDC(hdc_, transform_);
+  clip_region_ = region;
+  if (!clip_region_.isEmpty())
+    LoadClipRegion();
+}
+
+void VectorDevice::setDeviceOffset(int x, int y) {
+  offset_x_ = x;
+  offset_y_ = y;
+}
+
+void VectorDevice::drawToHDC(HDC dc, int x, int y, const RECT* src_rect) {
+  NOTREACHED();
+}
+
+void VectorDevice::LoadClipRegion() {
+  // We don't use transform_ for the clipping region since the translation is
+  // already applied to offset_x_ and offset_y_.
+  SkMatrix t;
+  t.reset();
+  t.postTranslate(SkIntToScalar(-offset_x_), SkIntToScalar(-offset_y_));
+  LoadClippingRegionToDC(hdc_, clip_region_, t);
+}
+
+bool VectorDevice::CreateBrush(bool use_brush, COLORREF color) {
+  DCHECK(previous_brush_ == NULL);
+  // We can't use SetDCBrushColor() or DC_BRUSH when drawing to a EMF buffer.
+  // SetDCBrushColor() calls are not recorded at all and DC_BRUSH will use
+  // WHITE_BRUSH instead.
+
+  if (!use_brush) {
+    // Set the transparency.
+    if (0 == SetBkMode(hdc_, TRANSPARENT)) {
+      NOTREACHED();
+      return false;
+    }
+
+    // Select the NULL brush.
+    previous_brush_ = SelectObject(GetStockObject(NULL_BRUSH));
+    return previous_brush_ != NULL;
+  }
+
+  // Set the opacity.
+  if (0 == SetBkMode(hdc_, OPAQUE)) {
+    NOTREACHED();
+    return false;
+  }
+
+  // Create and select the brush.
+  previous_brush_ = SelectObject(CreateSolidBrush(color));
+  return previous_brush_ != NULL;
+}
+
+bool VectorDevice::CreatePen(bool use_pen, COLORREF color, int stroke_width,
+                             float stroke_miter, DWORD pen_style) {
+  DCHECK(previous_pen_ == NULL);
+  // We can't use SetDCPenColor() or DC_PEN when drawing to a EMF buffer.
+  // SetDCPenColor() calls are not recorded at all and DC_PEN will use BLACK_PEN
+  // instead.
+
+  // No pen case
+  if (!use_pen) {
+    previous_pen_ = SelectObject(GetStockObject(NULL_PEN));
+    return previous_pen_ != NULL;
+  }
+
+  // Use the stock pen if the stroke width is 0.
+  if (stroke_width == 0) {
+    // Create a pen with the right color.
+    previous_pen_ = SelectObject(::CreatePen(PS_SOLID, 0, color));
+    return previous_pen_ != NULL;
+  }
+
+  // Load a custom pen.
+  LOGBRUSH brush;
+  brush.lbStyle = BS_SOLID;
+  brush.lbColor = color;
+  brush.lbHatch = 0;
+  HPEN pen = ExtCreatePen(pen_style, stroke_width, &brush, 0, NULL);
+  DCHECK(pen != NULL);
+  previous_pen_ = SelectObject(pen);
+  if (previous_pen_ == NULL)
+    return false;
+
+  if (!SetMiterLimit(hdc_, stroke_miter, NULL)) {
+    NOTREACHED();
+    return false;
+  }
+  return true;
+}
+
+void VectorDevice::Cleanup() {
+  if (previous_brush_) {
+    HGDIOBJ result = SelectObject(previous_brush_);
+    previous_brush_ = NULL;
+    if (result) {
+      BOOL res = DeleteObject(result);
+      DCHECK(res != 0);
+    }
+  }
+  if (previous_pen_) {
+    HGDIOBJ result = SelectObject(previous_pen_);
+    previous_pen_ = NULL;
+    if (result) {
+      BOOL res = DeleteObject(result);
+      DCHECK(res != 0);
+    }
+  }
+  // Remove any loaded path from the context.
+  AbortPath(hdc_);
+}
+
+HGDIOBJ VectorDevice::SelectObject(HGDIOBJ object) {
+  HGDIOBJ result = ::SelectObject(hdc_, object);
+  DCHECK(result != HGDI_ERROR);
+  if (result == HGDI_ERROR)
+    return NULL;
+  return result;
+}
+
+bool VectorDevice::CreateBrush(bool use_brush, const SkPaint& paint) {
+  // Make sure that for transparent color, no brush is used.
+  if (paint.getAlpha() == 0) {
+    // Test if it ever happen.
+    NOTREACHED();
+    use_brush = false;
+  }
+
+  return CreateBrush(use_brush, SkColorToCOLORREF(paint.getColor()));
+}
+
+bool VectorDevice::CreatePen(bool use_pen, const SkPaint& paint) {
+  // Make sure that for transparent color, no pen is used.
+  if (paint.getAlpha() == 0) {
+    // Test if it ever happen.
+    NOTREACHED();
+    use_pen = false;
+  }
+
+  DWORD pen_style = PS_GEOMETRIC | PS_SOLID;
+  switch (paint.getStrokeJoin()) {
+    case SkPaint::kMiter_Join:
+      // Connects path segments with a sharp join.
+      pen_style |= PS_JOIN_MITER;
+      break;
+    case SkPaint::kRound_Join:
+      // Connects path segments with a round join.
+      pen_style |= PS_JOIN_ROUND;
+      break;
+    case SkPaint::kBevel_Join:
+      // Connects path segments with a flat bevel join.
+      pen_style |= PS_JOIN_BEVEL;
+      break;
+    default:
+      NOTREACHED();
+      break;
+  }
+  switch (paint.getStrokeCap()) {
+    case SkPaint::kButt_Cap:
+      // Begin/end contours with no extension.
+      pen_style |= PS_ENDCAP_FLAT;
+      break;
+    case SkPaint::kRound_Cap:
+      // Begin/end contours with a semi-circle extension.
+      pen_style |= PS_ENDCAP_ROUND;
+      break;
+    case SkPaint::kSquare_Cap:
+      // Begin/end contours with a half square extension.
+      pen_style |= PS_ENDCAP_SQUARE;
+      break;
+    default:
+      NOTREACHED();
+      break;
+  }
+
+  return CreatePen(use_pen,
+                   SkColorToCOLORREF(paint.getColor()),
+                   SkScalarRound(paint.getStrokeWidth()),
+                   paint.getStrokeMiter(),
+                   pen_style);
+}
+
+void VectorDevice::InternalDrawBitmap(const SkBitmap& bitmap, int x, int y,
+                                      const SkPaint& paint) {
+  uint8 alpha = paint.getAlpha();
+  if (alpha == 0)
+    return;
+
+  bool is_translucent;
+  if (alpha != 255) {
+    // ApplyPaint expect an opaque color.
+    SkPaint tmp_paint(paint);
+    tmp_paint.setAlpha(255);
+    if (!ApplyPaint(tmp_paint))
+      return;
+    is_translucent = true;
+  } else {
+    if (!ApplyPaint(paint))
+      return;
+    is_translucent = false;
+  }
+  int src_size_x = bitmap.width();
+  int src_size_y = bitmap.height();
+  if (!src_size_x || !src_size_y)
+    return;
+
+  // Create a BMP v4 header that we can serialize.
+  BITMAPV4HEADER bitmap_header;
+  gfx::CreateBitmapV4Header(src_size_x, src_size_y, &bitmap_header);
+  HDC dc = getBitmapDC();
+  SkAutoLockPixels lock(bitmap);
+  DCHECK_EQ(bitmap.getConfig(), SkBitmap::kARGB_8888_Config);
+  const uint32_t* pixels = static_cast<const uint32_t*>(bitmap.getPixels());
+  if (pixels == NULL) {
+    NOTREACHED();
+    return;
+  }
+
+  if (!is_translucent) {
+    int row_length = bitmap.rowBytesAsPixels();
+    // There is no quick way to determine if an image is opaque.
+    for (int y2 = 0; y2 < src_size_y; ++y2) {
+      for (int x2 = 0; x2 < src_size_x; ++x2) {
+        if (SkColorGetA(pixels[(y2 * row_length) + x2]) != 255) {
+          is_translucent = true;
+          y2 = src_size_y;
+          break;
+        }
+      }
+    }
+  }
+
+  BITMAPINFOHEADER hdr;
+  gfx::CreateBitmapHeader(src_size_x, src_size_y, &hdr);
+  if (is_translucent) {
+    // The image must be loaded as a bitmap inside a device context.
+    ScopedHDC bitmap_dc(::CreateCompatibleDC(dc));
+    void* bits = NULL;
+    ScopedBitmap hbitmap(::CreateDIBSection(
+        bitmap_dc, reinterpret_cast<const BITMAPINFO*>(&hdr),
+        DIB_RGB_COLORS, &bits, NULL, 0));
+    memcpy(bits, pixels, bitmap.getSize());
+    DCHECK(hbitmap);
+    HGDIOBJ old_bitmap = ::SelectObject(bitmap_dc, hbitmap);
+    DeleteObject(old_bitmap);
+
+    // After some analysis of IE7's behavior, this is the thing to do. I was
+    // sure IE7 was doing so kind of bitmasking due to the way translucent image
+    // where renderered but after some windbg tracing, it is being done by the
+    // printer driver after all (mostly HP printers). IE7 always use AlphaBlend
+    // for bitmasked images. The trick seems to switch the stretching mode in
+    // what the driver expects.
+    DWORD previous_mode = GetStretchBltMode(dc);
+    BOOL result = SetStretchBltMode(dc, COLORONCOLOR);
+    DCHECK(result);
+    // Note that this function expect premultiplied colors (!)
+    BLENDFUNCTION blend_function = {AC_SRC_OVER, 0, alpha, AC_SRC_ALPHA};
+    result = AlphaBlend(dc,
+                        x, y,  // Destination origin.
+                        src_size_x, src_size_y,  // Destination size.
+                        bitmap_dc,
+                        0, 0,  // Source origin.
+                        src_size_x, src_size_y,  // Source size.
+                        blend_function);
+    DCHECK(result);
+    result = SetStretchBltMode(dc, previous_mode);
+    DCHECK(result);
+  } else {
+    BOOL result = StretchDIBits(dc,
+                                x, y,  // Destination origin.
+                                src_size_x, src_size_y,
+                                0, 0,  // Source origin.
+                                src_size_x, src_size_y,  // Source size.
+                                pixels,
+                                reinterpret_cast<const BITMAPINFO*>(&hdr),
+                                DIB_RGB_COLORS,
+                                SRCCOPY);
+    DCHECK(result);
+  }
+  Cleanup();
+}
+
+}  // namespace gfx
diff --git a/base/gfx/vector_device.h b/base/gfx/vector_device.h
new file mode 100644
index 0000000..002ce17
--- /dev/null
+++ b/base/gfx/vector_device.h
@@ -0,0 +1,146 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_GFX_VECTOR_DEVICE_H__
+#define BASE_GFX_VECTOR_DEVICE_H__
+
+#include "base/basictypes.h"
+#include "base/gfx/platform_device.h"
+#include "SkMatrix.h"
+#include "SkRegion.h"
+
+namespace gfx {
+
+// A device is basically a wrapper around SkBitmap that provides a surface for
+// SkCanvas to draw into. This specific device is not not backed by a surface
+// and is thus unreadable. This is because the backend is completely vectorial.
+// This device is a simple wrapper over a Windows device context (HDC) handle.
+class VectorDevice : public PlatformDevice {
+ public:
+  // Factory function. The DC is kept as the output context.
+  static VectorDevice* create(HDC dc, int width, int height);
+
+  VectorDevice(HDC dc, const SkBitmap& bitmap);
+  virtual ~VectorDevice();
+
+  virtual HDC getBitmapDC() {
+    return hdc_;
+  }
+
+  virtual void drawPaint(const SkDraw& draw, const SkPaint& paint);
+  virtual void drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, size_t count,
+                          const SkPoint[], const SkPaint& paint);
+  virtual void drawRect(const SkDraw& draw, const SkRect& r,
+                        const SkPaint& paint);
+  virtual void drawPath(const SkDraw& draw, const SkPath& path,
+                        const SkPaint& paint);
+  virtual void drawBitmap(const SkDraw& draw, const SkBitmap& bitmap,
+                          const SkMatrix& matrix, const SkPaint& paint);
+  virtual void drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
+                          int x, int y, const SkPaint& paint);
+  virtual void drawText(const SkDraw& draw, const void* text, size_t len,
+                        SkScalar x, SkScalar y, const SkPaint& paint);
+  virtual void drawPosText(const SkDraw& draw, const void* text, size_t len,
+                           const SkScalar pos[], SkScalar constY,
+                           int scalarsPerPos, const SkPaint& paint);
+  virtual void drawTextOnPath(const SkDraw& draw, const void* text, size_t len,
+                              const SkPath& path, const SkMatrix* matrix,
+                              const SkPaint& paint);
+  virtual void drawVertices(const SkDraw& draw, SkCanvas::VertexMode, int vertexCount,
+                            const SkPoint verts[], const SkPoint texs[],
+                            const SkColor colors[], SkXfermode* xmode,
+                            const uint16_t indices[], int indexCount,
+                            const SkPaint& paint);
+  virtual void drawDevice(const SkDraw& draw, SkDevice*, int x, int y,
+                          const SkPaint&);
+
+
+  virtual void setMatrixClip(const SkMatrix& transform, const SkRegion& region);
+  virtual void setDeviceOffset(int x, int y);
+  virtual void drawToHDC(HDC dc, int x, int y, const RECT* src_rect);
+  virtual bool IsVectorial() { return true; }
+
+  void LoadClipRegion();
+
+ private:
+  // Applies the SkPaint's painting properties in the current GDI context, if
+  // possible. If GDI can't support all paint's properties, returns false. It
+  // doesn't execute the "commands" in SkPaint.
+  bool ApplyPaint(const SkPaint& paint);
+
+  // Selects a new object in the device context. It can be a pen, a brush, a
+  // clipping region, a bitmap or a font. Returns the old selected object.
+  HGDIOBJ SelectObject(HGDIOBJ object);
+
+  // Creates a brush according to SkPaint's properties.
+  bool CreateBrush(bool use_brush, const SkPaint& paint);
+
+  // Creates a pen according to SkPaint's properties.
+  bool CreatePen(bool use_pen, const SkPaint& paint);
+
+  // Restores back the previous objects (pen, brush, etc) after a paint command.
+  void Cleanup();
+
+  // Creates a brush according to SkPaint's properties.
+  bool CreateBrush(bool use_brush, COLORREF color);
+
+  // Creates a pen according to SkPaint's properties.
+  bool CreatePen(bool use_pen, COLORREF color, int stroke_width,
+                 float stroke_miter, DWORD pen_style);
+
+  // Draws a bitmap in the the device, using the currently loaded matrix.
+  void InternalDrawBitmap(const SkBitmap& bitmap, int x, int y,
+                          const SkPaint& paint);
+
+  // The Windows Device Context handle. It is the backend used with GDI drawing.
+  // This backend is write-only and vectorial.
+  HDC hdc_;
+
+  // Translation assigned to the DC: we need to keep track of this separately
+  // so it can be updated even if the DC isn't created yet.
+  SkMatrix transform_;
+
+  // The current clipping
+  SkRegion clip_region_;
+
+  // Previously selected brush before the current drawing.
+  HGDIOBJ previous_brush_;
+
+  // Previously selected pen before the current drawing.
+  HGDIOBJ previous_pen_;
+
+  int offset_x_;
+  int offset_y_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(VectorDevice);
+};
+
+}  // namespace gfx
+
+#endif  // BASE_GFX_VECTOR_DEVICE_H__
diff --git a/base/hash_tables.h b/base/hash_tables.h
new file mode 100644
index 0000000..13c201b
--- /dev/null
+++ b/base/hash_tables.h
@@ -0,0 +1,54 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Deal with the differences between Microsoft and GNU implemenations
+// of hash_map
+//
+
+#ifndef BASE_HASH_TABLES_H__
+#define BASE_HASH_TABLES_H__
+
+#ifdef WIN32
+#include <hash_map>
+#include <hash_set>
+namespace base {
+  using stdext::hash_map;
+  using stdext::hash_set;
+}
+#else
+#include <ext/hash_map>
+#include <ext/hash_set>
+namespace base {
+  using __gnu_cxx::hash_map;
+  using __gnu_cxx::hash_set;
+}
+
+#endif
+
+#endif  // BASE_HASH_TABLES_H__
diff --git a/base/histogram.cc b/base/histogram.cc
new file mode 100644
index 0000000..fc72f8f
--- /dev/null
+++ b/base/histogram.cc
@@ -0,0 +1,668 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Histogram is an object that aggregates statistics, and can summarize them in
+// various forms, including ASCII graphical, HTML, and numerically (as a
+// vector of numbers corresponding to each of the aggregating buckets).
+// See header file for details and examples.
+
+#include "base/histogram.h"
+
+#include <math.h>
+#include <string>
+
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+
+typedef Histogram::Count Count;
+
+Histogram::Histogram(const wchar_t* name, Sample minimum,
+                     Sample maximum, size_t bucket_count)
+  : StatsRate(name),
+    histogram_name_(WideToASCII(name)),
+    declared_min_(minimum),
+    declared_max_(maximum),
+    bucket_count_(bucket_count),
+    flags_(0),
+    ranges_(bucket_count + 1, 0),
+    sample_(),
+    registered_(false) {
+  Initialize();
+}
+
+Histogram::Histogram(const wchar_t* name, TimeDelta minimum,
+                     TimeDelta maximum, size_t bucket_count)
+  : StatsRate(name),
+    histogram_name_(WideToASCII(name)),
+    declared_min_(static_cast<int> (minimum.InMilliseconds())),
+    declared_max_(static_cast<int> (maximum.InMilliseconds())),
+    bucket_count_(bucket_count),
+    flags_(0),
+    ranges_(bucket_count + 1, 0),
+    sample_(),
+    registered_(false) {
+  Initialize();
+}
+
+Histogram::~Histogram() {
+  if (registered_)
+    StatisticsRecorder::UnRegister(*this);
+  // Just to make sure most derived class did this properly...
+  DCHECK(ValidateBucketRanges());
+}
+
+
+// Hooks to override stats counter methods.  This ensures that we gather all
+// input the stats counter sees.
+void Histogram::Add(int value) {
+  if (!registered_)
+    registered_ = StatisticsRecorder::Register(*this);
+  if (value >= kSampleType_MAX)
+    value = kSampleType_MAX - 1;
+  StatsRate::Add(value);
+  if (value < 0)
+    value = 0;
+  size_t index = BucketIndex(value);
+  DCHECK(value >= ranges(index));
+  DCHECK(value < ranges(index + 1));
+  Accumulate(value, 1, index);
+}
+
+// The following methods provide a graphical histogram display.
+void Histogram::WriteHTMLGraph(std::string* output) const {
+  // TBD(jar) Write a nice HTML bar chart, with divs an mouse-overs etc.
+  output->append("<PRE>");
+  WriteAscii(true, "<br>", output);
+  output->append("</PRE>");
+}
+
+void Histogram::WriteAscii(bool graph_it, const std::string& newline,
+                           std::string* output) const {
+  // Get local (stack) copies of all effectively volatile class data so that we
+  // are consistent across our output activities.
+  SampleSet snapshot;
+  SnapshotSample(&snapshot);
+  Count sample_count = snapshot.TotalCount();
+
+  WriteAsciiHeader(snapshot, sample_count, output);
+  output->append(newline);
+
+  // Prepare to normalize graphical rendering of bucket contents.
+  double max_size = 0;
+  if (graph_it)
+    max_size = GetPeakBucketSize(snapshot);
+
+  // Calculate space needed to print bucket range numbers.  Leave room to print
+  // nearly the largest bucket range without sliding over the histogram.
+  size_t largest_non_empty_bucket = bucket_count_ - 1;
+  while (0 == sample_.counts(largest_non_empty_bucket)) {
+    if (0 == largest_non_empty_bucket)
+      break;  // All buckets are empty.
+    largest_non_empty_bucket--;
+  }
+
+  // Calculate largest print width needed for any of our bucket range displays.
+  size_t print_width = 1;
+  for (size_t i = 0; i < bucket_count_; ++i) {
+    if (snapshot.counts(i)) {
+      size_t width = GetAsciiBucketRange(i).size() + 1;
+      if (width > print_width)
+        print_width = width;
+    }
+  }
+
+  int64 remaining = sample_count;
+  int64 past = 0;
+  // Output the actual histogram graph.
+  for (size_t i = 0; i < bucket_count_; i++) {
+    Count current = snapshot.counts(i);
+    if (!current && !PrintEmptyBucket(i))
+      continue;
+    remaining -= current;
+    StringAppendF(output, "%#*s ", print_width, GetAsciiBucketRange(i).c_str());
+    if (0 == current && i < bucket_count_ - 1 && 0 == snapshot.counts(i + 1)) {
+      while (i < bucket_count_ - 1 && 0 == snapshot.counts(i + 1))
+        i++;
+      output->append("... ");
+      output->append(newline);
+      continue;  // No reason to plot emptiness.
+    }
+    double current_size = GetBucketSize(current, i);
+    if (graph_it)
+      WriteAsciiBucketGraph(current_size, max_size, output);
+    WriteAsciiBucketContext(past, current, remaining, i, output);
+    output->append(newline);
+    past += current;
+  }
+  DCHECK(past == sample_count);
+}
+
+bool Histogram::ValidateBucketRanges() const {
+  // Standard assertions that all bucket ranges should satisfy.
+  DCHECK(ranges_.size() == bucket_count_ + 1);
+  DCHECK(0 == ranges_[0]);
+  DCHECK(declared_min() == ranges_[1]);
+  DCHECK(declared_max() == ranges_[bucket_count_ - 1]);
+  DCHECK(kSampleType_MAX == ranges_[bucket_count_]);
+  return true;
+}
+
+void Histogram::Initialize() {
+  sample_.Resize(*this);
+  if (declared_min_ <= 0)
+    declared_min_ = 1;
+  if (declared_max_ >= kSampleType_MAX)
+    declared_max_ = kSampleType_MAX - 1;
+  DCHECK(declared_min_ > 0);  // We provide underflow bucket.
+  DCHECK(declared_min_ < declared_max_);
+  DCHECK(1 < bucket_count_);
+  size_t maximal_bucket_count = declared_max_ - declared_min_ + 2;
+  DCHECK(bucket_count_ <= maximal_bucket_count);
+  DCHECK(0 == ranges_[0]);
+  ranges_[bucket_count_] = kSampleType_MAX;
+  InitializeBucketRange();
+  DCHECK(ValidateBucketRanges());
+  registered_ = StatisticsRecorder::Register(*this);
+}
+
+// Calculate what range of values are held in each bucket.
+// We have to be careful that we don't pick a ratio between starting points in
+// consecutive buckets that is sooo small, that the integer bounds are the same
+// (effectively making one bucket get no values).  We need to avoid:
+// (ranges_[i] == ranges_[i + 1]
+// To avoid that, we just do a fine-grained bucket width as far as we need to
+// until we get a ratio that moves us along at least 2 units at a time.  From
+// that bucket onward we do use the exponential growth of buckets.
+void Histogram::InitializeBucketRange() {
+  double log_max = log(static_cast<double>(declared_max()));
+  double log_ratio;
+  double log_next;
+  size_t bucket_index = 1;
+  Sample current = declared_min();
+  SetBucketRange(bucket_index, current);
+  while (bucket_count() > ++bucket_index) {
+    double log_current;
+    log_current = log(static_cast<double>(current));
+    // Calculate the count'th root of the range.
+    log_ratio = (log_max - log_current) / (bucket_count() - bucket_index);
+    // See where the next bucket would start.
+    log_next = log_current + log_ratio;
+    int next;
+    next = static_cast<int>(floor(exp(log_next) + 0.5));
+    if (next > current)
+      current = next;
+    else
+      current++;  // Just do a narrow bucket, and keep trying.
+    SetBucketRange(bucket_index, current);
+  }
+
+  DCHECK(bucket_count() == bucket_index);
+}
+
+size_t Histogram::BucketIndex(Sample value) const {
+  // Use simple binary search.  This is very general, but there are better
+  // approaches if we knew that the buckets were linearly distributed.
+  DCHECK(ranges(0) <= value);
+  DCHECK(ranges(bucket_count()) > value);
+  size_t under = 0;
+  size_t over = bucket_count();
+  size_t mid;
+
+  do {
+    DCHECK(over >= under);
+    mid = (over + under)/2;
+    if (mid == under)
+      break;
+    if (ranges(mid) <= value)
+      under = mid;
+    else
+      over = mid;
+  } while (true);
+
+  DCHECK(ranges(mid) <= value && ranges(mid+1) > value);
+  return mid;
+}
+
+// Use the actual bucket widths (like a linear histogram) until the widths get
+// over some transition value, and then use that transition width.  Exponentials
+// get so big so fast (and we don't expect to see a lot of entries in the large
+// buckets), so we need this to make it possible to see what is going on and
+// not have 0-graphical-height buckets.
+double Histogram::GetBucketSize(Count current, size_t i) const {
+  DCHECK(ranges(i + 1) > ranges(i));
+  static const double kTransitionWidth = 5;
+  double denominator = ranges(i + 1) - ranges(i);
+  if (denominator > kTransitionWidth)
+    denominator = kTransitionWidth;  // Stop trying to normalize.
+  return current/denominator;
+}
+
+//------------------------------------------------------------------------------
+// The following two methods can be overridden to provide a thread safe
+// version of this class.  The cost of locking is low... but an error in each
+// of these methods has minimal impact.  For now, I'll leave this unlocked,
+// and I don't believe I can loose more than a count or two.
+// The vectors are NOT reallocated, so there is no risk of them moving around.
+
+// Update histogram data with new sample.
+void Histogram::Accumulate(Sample value, Count count, size_t index) {
+  // Note locking not done in this version!!!
+  sample_.Accumulate(value, count, index);
+}
+
+// Do a safe atomic snapshot of sample data.
+// This implementation assumes we are on a safe single thread.
+void Histogram::SnapshotSample(SampleSet* sample) const {
+  // Note locking not done in this version!!!
+  *sample = sample_;
+}
+
+//------------------------------------------------------------------------------
+// Accessor methods
+
+void Histogram::SetBucketRange(size_t i, Sample value) {
+  DCHECK(bucket_count_ > i);
+  ranges_[i] = value;
+}
+
+//------------------------------------------------------------------------------
+// Private methods
+
+double Histogram::GetPeakBucketSize(const SampleSet& snapshot) const {
+  double max = 0;
+  for (size_t i = 0; i < bucket_count_ ; i++) {
+    double current_size = GetBucketSize(snapshot.counts(i), i);
+    if (current_size > max)
+      max = current_size;
+  }
+  return max;
+}
+
+void Histogram::WriteAsciiHeader(const SampleSet& snapshot,
+                                 Count sample_count,
+                                 std::string* output) const {
+  StringAppendF(output,
+                "Histogram: %s recorded %ld samples",
+                histogram_name().c_str(),
+                sample_count);
+  if (0 == sample_count) {
+    DCHECK(0 == snapshot.sum());
+  } else {
+    double average = static_cast<float>(snapshot.sum()) / sample_count;
+    double variance = static_cast<float>(snapshot.square_sum())/sample_count
+                      - average * average;
+    double standard_deviation = sqrt(variance);
+
+    StringAppendF(output,
+                  ", average = %.1f, standard deviation = %.1f",
+                  average, standard_deviation);
+  }
+  if (flags_ & ~kHexRangePrintingFlag )
+    StringAppendF(output, " (flags = 0x%x)", flags_ & ~kHexRangePrintingFlag);
+}
+
+void Histogram::WriteAsciiBucketContext(const int64 past,
+                                        const Count current,
+                                        const int64 remaining,
+                                        const size_t i,
+                                        std::string* output) const {
+  double scaled_sum = (past + current + remaining) / 100.0;
+  WriteAsciiBucketValue(current, scaled_sum, output);
+  if (0 < i) {
+    double percentage = past / scaled_sum;
+    StringAppendF(output, " {%3.1f%%}", percentage);
+  }
+}
+
+const std::string Histogram::GetAsciiBucketRange(size_t i) const {
+  std::string result;
+  if (kHexRangePrintingFlag & flags_)
+    StringAppendF(&result, "%#x", ranges_[i]);
+  else
+    StringAppendF(&result, "%d", ranges_[i]);
+  return result;
+}
+
+void Histogram::WriteAsciiBucketValue(Count current, double scaled_sum,
+                                      std::string* output) const {
+  StringAppendF(output, " (%d = %3.1f%%)", current, current/scaled_sum);
+}
+
+void Histogram::WriteAsciiBucketGraph(double current_size, double max_size,
+                                      std::string* output) const {
+  const int k_line_length = 72;  // Maximal horizontal width of graph.
+  int x_count = static_cast<int>(k_line_length * (current_size / max_size)
+                                 + 0.5);
+  int x_remainder = k_line_length - x_count;
+
+  while (0 < x_count--)
+    output->append("-");
+  output->append("O");
+  while (0 < x_remainder--)
+    output->append(" ");
+}
+
+//------------------------------------------------------------------------------
+// Methods for the Histogram::SampleSet class
+//------------------------------------------------------------------------------
+
+Histogram::SampleSet::SampleSet()
+    : counts_(),
+      sum_(0),
+      square_sum_(0) {
+}
+
+void Histogram::SampleSet::Resize(const Histogram& histogram) {
+  counts_.resize(histogram.bucket_count(), 0);
+}
+
+void Histogram::SampleSet::CheckSize(const Histogram& histogram) const {
+  DCHECK(counts_.size() == histogram.bucket_count());
+}
+
+
+void Histogram::SampleSet::Accumulate(Sample value,  Count count,
+                                      size_t index) {
+  DCHECK(count == 1 || count == -1);
+  counts_[index] += count;
+  sum_ += count * value;
+  square_sum_ += (count * value) * static_cast<int64>(value);
+  DCHECK(counts_[index] >= 0);
+  DCHECK(sum_ >= 0);
+  DCHECK(square_sum_ >= 0);
+}
+
+Count Histogram::SampleSet::TotalCount() const {
+  Count total = 0;
+  for (Counts::const_iterator it = counts_.begin();
+       it != counts_.end();
+       it++) {
+    total += *it;
+  }
+  return total;
+}
+
+void Histogram::SampleSet::Add(const SampleSet& other) {
+  DCHECK(counts_.size() == other.counts_.size());
+  sum_ += other.sum_;
+  square_sum_ += other.square_sum_;
+  for (size_t index = 0; index < counts_.size(); index++)
+    counts_[index] += other.counts_[index];
+}
+
+void Histogram::SampleSet::Subtract(const SampleSet& other) {
+  DCHECK(counts_.size() == other.counts_.size());
+  // Note: Race conditions in snapshotting a sum or square_sum may lead to
+  // (temporary) negative values when snapshots are later combined (and deltas
+  // calculated).  As a result, we don't currently CHCEK() for positive values.
+  sum_ -= other.sum_;
+  square_sum_ -= other.square_sum_;
+  for (size_t index = 0; index < counts_.size(); index++) {
+    counts_[index] -= other.counts_[index];
+    DCHECK(counts_[index] >= 0);
+  }
+}
+
+//------------------------------------------------------------------------------
+// LinearHistogram: This histogram uses a traditional set of evenly spaced
+// buckets.
+//------------------------------------------------------------------------------
+
+LinearHistogram::LinearHistogram(const wchar_t* name,
+    Sample minimum, Sample maximum, size_t bucket_count)
+    : Histogram(name, minimum >= 1 ? minimum : 1, maximum, bucket_count) {
+  InitializeBucketRange();
+  DCHECK(ValidateBucketRanges());
+}
+
+LinearHistogram::LinearHistogram(const wchar_t* name,
+    TimeDelta minimum, TimeDelta maximum, size_t bucket_count)
+    : Histogram(name, minimum >= TimeDelta::FromMilliseconds(1) ?
+                                 minimum : TimeDelta::FromMilliseconds(1),
+                maximum, bucket_count) {
+  // Do a "better" (different) job at init than a base classes did...
+  InitializeBucketRange();
+  DCHECK(ValidateBucketRanges());
+}
+
+void LinearHistogram::SetRangeDescriptions(const DescriptionPair descriptions[]) {
+  for (int i =0; descriptions[i].description; ++i) {
+    bucket_description_[descriptions[i].sample] = descriptions[i].description;
+  }
+}
+
+const std::string LinearHistogram::GetAsciiBucketRange(size_t i) const {
+  int range = ranges(i);
+  BucketDescriptionMap::const_iterator it = bucket_description_.find(range);
+  if (it == bucket_description_.end())
+    return Histogram::GetAsciiBucketRange(i);
+  return it->second;
+}
+
+bool LinearHistogram::PrintEmptyBucket(size_t index) const {
+  return bucket_description_.find(ranges(index)) == bucket_description_.end();
+}
+
+
+void LinearHistogram::InitializeBucketRange() {
+  DCHECK(0 < declared_min());  // 0 is the underflow bucket here.
+  double min = declared_min();
+  double max = declared_max();
+  size_t i;
+  for (i = 1; i < bucket_count(); i++) {
+    double linear_range = (min * (bucket_count() -1 - i) + max * (i - 1)) /
+                          (bucket_count() - 2);
+    SetBucketRange(i, static_cast<int> (linear_range + 0.5));
+  }
+}
+
+// Find bucket to increment for sample value.
+size_t LinearHistogram::BucketIndex(Sample value) const {
+  if (value < declared_min()) return 0;
+  if (value >= declared_max()) return bucket_count() - 1;
+  size_t index;
+  index = static_cast<size_t>(((value - declared_min()) * (bucket_count() - 2))
+                              / (declared_max() - declared_min()) + 1);
+  DCHECK(1 <= index && bucket_count() > index);
+  return index;
+}
+
+double LinearHistogram::GetBucketSize(Count current, size_t i) const {
+  DCHECK(ranges(i + 1) > ranges(i));
+  // Adjacent buckets with different widths would have "surprisingly" many (few)
+  // samples in a histogram if we didn't normalize this way.
+  double denominator = ranges(i + 1) - ranges(i);
+  return current/denominator;
+}
+
+//------------------------------------------------------------------------------
+// This section provides implementation for ThreadSafeHistogram.
+//------------------------------------------------------------------------------
+
+ThreadSafeHistogram::ThreadSafeHistogram(const wchar_t* name, Sample minimum,
+                                         Sample maximum, size_t bucket_count)
+    : Histogram(name, minimum, maximum, bucket_count),
+      lock_() {
+  }
+
+void ThreadSafeHistogram::Remove(int value) {
+  if (value >= kSampleType_MAX)
+    value = kSampleType_MAX - 1;
+  StatsRate::Add(-value);
+  size_t index = BucketIndex(value);
+  Accumulate(value, -1, index);
+}
+
+void ThreadSafeHistogram::Accumulate(Sample value, Count count, size_t index) {
+  AutoLock lock(lock_);
+  Histogram::Accumulate(value, count, index);
+}
+
+void ThreadSafeHistogram::SnapshotSample(SampleSet* sample) {
+  AutoLock lock(lock_);
+  Histogram::SnapshotSample(sample);
+};
+
+
+//------------------------------------------------------------------------------
+// The next section handles global (central) support for all histograms, as well
+// as startup/teardown of this service.
+//------------------------------------------------------------------------------
+
+// This singleton instance should be started during the single threaded portion
+// of main(), and hence it is not thread safe.  It initializes globals to
+// provide support for all future calls.
+StatisticsRecorder::StatisticsRecorder() {
+  DCHECK(!histograms_);
+  lock_ = new Lock;
+  histograms_ = new HistogramMap;
+}
+
+StatisticsRecorder::~StatisticsRecorder() {
+  DCHECK(histograms_);
+
+  if (dump_on_exit_) {
+    std::string output;
+    WriteGraph("", &output);
+    LOG(INFO) << output;
+  }
+
+  // Clean up.
+  delete histograms_;
+  histograms_ = NULL;
+  delete lock_;
+  lock_ = NULL;
+}
+
+// static
+bool StatisticsRecorder::WasStarted() {
+  return NULL != histograms_;
+}
+
+// static
+bool StatisticsRecorder::Register(const Histogram& histogram) {
+  if (!histograms_)
+    return false;
+  const std::string name = histogram.histogram_name();
+  AutoLock auto_lock(*lock_);
+  DCHECK(histograms_->end() == histograms_->find(name));  // Only register once.
+  (*histograms_)[name] = &histogram;
+  return true;
+}
+
+// static
+void StatisticsRecorder::UnRegister(const Histogram& histogram) {
+  if (!histograms_)
+    return;
+  const std::string name = histogram.histogram_name();
+  AutoLock auto_lock(*lock_);
+  DCHECK(histograms_->end() != histograms_->find(name));
+  histograms_->erase(name);
+  if (dump_on_exit_) {
+    std::string output;
+    histogram.WriteAscii(true, "\n", &output);
+    LOG(INFO) << output;
+  }
+}
+
+// static
+void StatisticsRecorder::WriteHTMLGraph(const std::string& query,
+                                        std::string* output) {
+  if (!histograms_)
+    return;
+  output->append("<html><head><title>About Histograms");
+  if (!query.empty())
+    output->append(" - " + query);
+  output->append("</title>"
+                 // We'd like the following no-cache... but it doesn't work.
+                 // "<META HTTP-EQUIV=\"Pragma\" CONTENT=\"no-cache\">"
+                 "</head><body>");
+
+  Histograms snapshot;
+  GetSnapshot(query, &snapshot);
+  for (Histograms::iterator it = snapshot.begin();
+       it != snapshot.end();
+       it++) {
+    (*it)->WriteHTMLGraph(output);
+    output->append("<br><hr><br>");
+  }
+  output->append("</body></html>");
+}
+
+// static
+void StatisticsRecorder::WriteGraph(const std::string& query,
+                                        std::string* output) {
+  if (!histograms_)
+    return;
+  if (query.length())
+    StringAppendF(output, "Collections of histograms for %s\n", query.c_str());
+  else
+    output->append("Collections of all histograms\n");
+
+  Histograms snapshot;
+  GetSnapshot(query, &snapshot);
+  for (Histograms::iterator it = snapshot.begin();
+       it != snapshot.end();
+       it++) {
+    (*it)->WriteAscii(true, "\n", output);
+    output->append("\n");
+  }
+}
+
+// static
+void StatisticsRecorder::GetHistograms(Histograms* output) {
+  if (!histograms_)
+    return;
+  AutoLock auto_lock(*lock_);
+  for (HistogramMap::iterator it = histograms_->begin();
+       histograms_->end() != it;
+       it++) {
+    output->push_back(it->second);
+  }
+}
+
+// private static
+void StatisticsRecorder::GetSnapshot(const std::string& query,
+                                     Histograms* snapshot) {
+  AutoLock auto_lock(*lock_);
+  for (HistogramMap::iterator it = histograms_->begin();
+       histograms_->end() != it;
+       it++) {
+    if (it->first.find(query) != std::string::npos)
+      snapshot->push_back(it->second);
+  }
+}
+
+// static
+StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = NULL;
+// static
+Lock* StatisticsRecorder::lock_ = NULL;
+// static
+bool StatisticsRecorder::dump_on_exit_ = false;
diff --git a/base/histogram.h b/base/histogram.h
new file mode 100644
index 0000000..295f610
--- /dev/null
+++ b/base/histogram.h
@@ -0,0 +1,469 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Histogram is an object that aggregates statistics, and can summarize them in
+// various forms, including ASCII graphical, HTML, and numerically (as a
+// vector of numbers corresponding to each of the aggregating buckets).
+
+// It supports calls to accumulate either time intervals (which are processed
+// as integral number of milliseconds), or arbitrary integral units.
+
+// The default layout of buckets is exponential.  For example, buckets might
+// contain (sequentially) the count of values in the following intervals:
+// [0,1), [1,2), [2,4), [4,8), [8,16), [16,32), [32,64), [64,infinity)
+// That bucket allocation would actually result from construction of a histogram
+// for values between 1 and 64, with 8 buckets, such as:
+// Histogram count(L"some name", 1, 64, 8);
+// Note that the underflow bucket [0,1) and the overflow bucket [64,infinity)
+// are not counted by the constructor in the user supplied "bucket_count"
+// argument.
+// The above example has an exponential ratio of 2 (doubling the bucket width
+// in each consecutive bucket.  The Histogram class automatically calculates
+// the smallest ratio that it can use to construct the number of buckets
+// selected in the constructor.  An another example, if you had 50 buckets,
+// and millisecond time values from 1 to 10000, then the ratio between
+// consecutive bucket widths will be approximately somewhere around the 50th
+// root of 10000.  This approach provides very fine grain (narrow) buckets
+// at the low end of the histogram scale, but allows the histogram to cover a
+// gigantic range with the addition of very few buckets.
+
+#ifndef BASE_HISTOGRAM_H__
+#define BASE_HISTOGRAM_H__
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/lock.h"
+#include "base/scoped_ptr.h"
+#include "base/stats_counters.h"
+
+//------------------------------------------------------------------------------
+// Provide easy general purpose histogram in a macro, just like stats counters.
+// These macros all use 50 buckets.
+
+#define HISTOGRAM_TIMES(name, sample) do { \
+    static Histogram counter((name), TimeDelta::FromMilliseconds(1), \
+                             TimeDelta::FromSeconds(10), 50); \
+    counter.AddTime(sample); \
+  } while (0)
+
+#define HISTOGRAM_COUNTS(name, sample) do { \
+    static Histogram counter((name), 1, 1000000, 50); \
+    counter.Add(sample); \
+  } while (0)
+
+//------------------------------------------------------------------------------
+// This macro set is for a histogram that can support both addition and removal
+// of samples. It should be used to render the accumulated asset allocation
+// of some samples.  For example, it can sample memory allocation sizes, and
+// memory releases (as negative samples).
+// To simplify the interface, only non-zero values can be sampled, with positive
+// numbers indicating addition, and negative numbers implying dimunition
+// (removal).
+// Note that the underlying ThreadSafeHistogram() uses locking to ensure that
+// counts are precise (no chance of losing an addition or removal event, due to
+// multithread racing). This precision is required to prevent missed-counts from
+// resulting in drift, as the calls to Remove() for a given value should always
+// be equal in number or fewer than the corresponding calls to Add().
+
+#define ASSET_HISTOGRAM_COUNTS(name, sample) do { \
+    static ThreadSafeHistogram counter((name), 1, 1000000, 50); \
+    if (0 == sample) break; \
+    if (sample >= 0) \
+      counter.Add(sample); \
+    else\
+      counter.Remove(-sample); \
+  } while (0)
+
+//------------------------------------------------------------------------------
+// Define Debug vs non-debug flavors of macros.
+#ifndef NDEBUG
+
+#define DHISTOGRAM_TIMES(name, sample) HISTOGRAM_TIMES(name, sample)
+#define DHISTOGRAM_COUNTS(name, sample) HISTOGRAM_COUNTS(name, sample)
+#define DASSET_HISTOGRAM_COUNTS(name, sample) ASSET_HISTOGRAM_COUNTS(name, \
+                                                                     sample)
+
+#else  // NDEBUG
+
+#define DHISTOGRAM_TIMES(name, sample) do {} while (0)
+#define DHISTOGRAM_COUNTS(name, sample) do {} while (0)
+#define DASSET_HISTOGRAM_COUNTS(name, sample) do {} while (0)
+
+#endif  // NDEBUG
+
+//------------------------------------------------------------------------------
+// The following macros provide typical usage scenarios for callers that wish
+// to record histogram data, and have the data submitted/uploaded via UMA.
+// Not all systems support such UMA, but if they do, the following macros
+// should work with the service.
+
+static const int kUmaTargetedHistogramFlag = 0x1;
+
+#define UMA_HISTOGRAM_TIMES(name, sample) do { \
+    static Histogram counter((name), TimeDelta::FromMilliseconds(1), \
+                             TimeDelta::FromSeconds(10), 50); \
+    counter.SetFlags(kUmaTargetedHistogramFlag); \
+    counter.AddTime(sample); \
+  } while (0)
+
+// Use this macro when times can routinely be much longer than 10 seconds.
+#define UMA_HISTOGRAM_LONG_TIMES(name, sample) do { \
+    static Histogram counter((name), TimeDelta::FromMilliseconds(1), \
+                             TimeDelta::FromHours(1), 50); \
+    counter.SetFlags(kUmaTargetedHistogramFlag); \
+    counter.AddTime(sample); \
+  } while (0)
+
+#define UMA_HISTOGRAM_COUNTS(name, sample) do { \
+    static Histogram counter((name), 1, 1000000, 50); \
+    counter.SetFlags(kUmaTargetedHistogramFlag); \
+    counter.Add(sample); \
+  } while (0)
+
+#define UMA_HISTOGRAM_COUNTS_100(name, sample) do { \
+    static Histogram counter((name), 1, 100, 50); \
+    counter.SetFlags(kUmaTargetedHistogramFlag); \
+    counter.Add(sample); \
+  } while (0)
+
+#define UMA_HISTOGRAM_MEMORY_KB(name, sample) do { \
+    static Histogram counter((name), 1000, 500000, 50); \
+    counter.SetFlags(kUmaTargetedHistogramFlag); \
+    counter.Add(sample); \
+  } while (0)
+
+#define UMA_HISTOGRAM_MEMORY_MB(name, sample) do { \
+    static Histogram counter((name), 1, 1000, 50); \
+    counter.SetFlags(kUmaTargetedHistogramFlag); \
+    counter.Add(sample); \
+  } while (0)
+
+//------------------------------------------------------------------------------
+
+class Histogram : public StatsRate {
+ public:
+  typedef int Sample;  // Used for samples (and ranges of samples).
+  typedef int Count;  // Used to count samples in a bucket.
+  static const Sample kSampleType_MAX = INT_MAX;
+
+  typedef std::vector<Count> Counts;
+  typedef std::vector<const Sample> Ranges;
+
+  static const int kHexRangePrintingFlag = 0x8000;
+  //----------------------------------------------------------------------------
+  // Statistic values, developed over the life of the histogram.
+
+  class SampleSet {
+   public:
+    explicit SampleSet();
+    // Adjust size of counts_ for use with given histogram.
+    void Resize(const Histogram& histogram);
+    void CheckSize(const Histogram& histogram) const;
+
+    // Accessor for histogram to make routine additions.
+    void Accumulate(Sample value, Count count, size_t index);
+
+    // Accessor methods.
+    Count counts(size_t i) const { return counts_[i]; }
+    Count TotalCount() const ;
+    int64 sum() const { return sum_; }
+    int64 square_sum() const { return square_sum_; }
+
+    // Arithmetic manipulation of corresponding elements of the set.
+    void Add(const SampleSet& other);
+    void Subtract(const SampleSet& other);
+
+   private:
+    // Actual histogram data is stored in buckets, showing the count of values
+    // that fit into each bucket.
+    Counts counts_;
+
+    // Save simple stats locally.  Note that this MIGHT get done in base class
+    // without shared memory at some point.
+    int64 sum_;         // sum of samples.
+    int64 square_sum_;  // sum of squares of samples.
+  };
+  //----------------------------------------------------------------------------
+
+  Histogram(const wchar_t* name, Sample minimum,
+            Sample maximum, size_t bucket_count);
+  Histogram(const wchar_t* name, TimeDelta minimum,
+            TimeDelta maximum, size_t bucket_count);
+  ~Histogram();
+
+  // Hooks to override stats counter methods.  This ensures that we gather all
+  // input the stats counter sees.
+  virtual void Add(int value);
+
+  // The following methods provide a graphical histogram displays.
+  void WriteHTMLGraph(std::string* output) const;
+  void WriteAscii(bool graph_it, const std::string& newline,
+                  std::string* output) const;
+
+  // Support generic flagging of Histograms.
+  // 0x1 Currently used to mark this histogram to be recorded by UMA..
+  // 0x8000 means print ranges in hex.
+  void SetFlags(int flags) { flags_ |= flags; }
+  int flags() const { return flags_; }
+
+  //----------------------------------------------------------------------------
+  // Accessors for serialization and testing.
+  //----------------------------------------------------------------------------
+  const std::string histogram_name() const { return histogram_name_; }
+  Sample declared_min() const { return declared_min_; }
+  Sample declared_max() const { return declared_max_; }
+  Sample ranges(size_t i) const { return ranges_[i];}
+  size_t bucket_count() const { return bucket_count_; }
+  // Snapshot the current complete set of sample data.
+  // Override with atomic/locked snapshot if needed.
+  virtual void SnapshotSample(SampleSet* sample) const;
+
+ protected:
+  // Method to override to skip the display of the i'th bucket if it's empty.
+  virtual bool PrintEmptyBucket(size_t index) const { return true; }
+
+  //----------------------------------------------------------------------------
+  // Methods to override to create histogram with different bucket widths.
+  //----------------------------------------------------------------------------
+  // Initialize ranges_ mapping.
+  virtual void InitializeBucketRange();
+  // Find bucket to increment for sample value.
+  virtual size_t BucketIndex(Sample value) const;
+  // Get normalized size, relative to the ranges_[i].
+  virtual double GetBucketSize(Count current, size_t i) const;
+
+  // Return a string description of what goes in a given bucket.
+  // Most commonly this is the numeric value, but in derived classes it may
+  // be a name (or string description) given to the bucket.
+  virtual const std::string GetAsciiBucketRange(size_t it) const;
+
+  //----------------------------------------------------------------------------
+  // Methods to override to create thread safe histogram.
+  //----------------------------------------------------------------------------
+  // Update all our internal data, including histogram
+  virtual void Accumulate(Sample value, Count count, size_t index);
+
+  //----------------------------------------------------------------------------
+  // Accessors for derived classes.
+  //----------------------------------------------------------------------------
+  void SetBucketRange(size_t i, Sample value);
+
+  // Validate that ranges_ was created sensibly (top and bottom range
+  // values relate properly to the declared_min_ and declared_max_)..
+  bool ValidateBucketRanges() const;
+
+ private:
+  // Post constructor initialization.
+  void Initialize();
+
+  //----------------------------------------------------------------------------
+  // Helpers for emitting Ascii graphic.  Each method appends data to output.
+
+  // Find out how large the (graphically) the largest bucket will appear to be.
+  double GetPeakBucketSize(const SampleSet& snapshot) const;
+
+  // Write a common header message describing this histogram.
+  void WriteAsciiHeader(const SampleSet& snapshot,
+                        Count sample_count, std::string* output) const ;
+
+  // Write information about previous, current, and next buckets.
+  // Information such as cumulative percentage, etc.
+  void WriteAsciiBucketContext(const int64 past, const Count current,
+                               const int64 remaining, const size_t i,
+                               std::string* output) const;
+
+  // Write textual description of the bucket contents (relative to histogram).
+  // Output is the count in the buckets, as well as the percentage.
+  void WriteAsciiBucketValue(Count current, double scaled_sum,
+                             std::string* output) const;
+
+  // Produce actual graph (set of blank vs non blank char's) for a bucket.
+  void WriteAsciiBucketGraph(double current_size, double max_size,
+                             std::string* output) const;
+
+  //----------------------------------------------------------------------------
+  // Invariant values set at/near construction time
+
+  // ASCII version of original name given to the constructor.  All identically
+  // named instances will  be coalesced cross-project TODO(jar).
+  // If a user needs one histogram name to be called by several places in a
+  // single process, a central function should be defined by teh user, which
+  // defins the single declared instance of the named histogram.
+  const std::string histogram_name_;
+  Sample declared_min_;  // Less than this goes into counts_[0]
+  Sample declared_max_;  // Over this goes into counts_[bucket_count_ - 1].
+  size_t bucket_count_;  // Dimension of counts_[].
+
+  // Flag the histogram for recording by UMA via metric_services.h.
+  int flags_;
+
+  // For each index, show the least value that can be stored in the
+  // corresponding bucket. We also append one extra element in this array,
+  // containing kSampleType_MAX, to make calculations easy.
+  // The dimension of ranges_ is bucket_count + 1.
+  Ranges ranges_;
+
+  // Finally, provide the state that changes with the addition of each new
+  // sample.
+  SampleSet sample_;
+
+  // Indicate if successfully registered.
+  bool registered_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(Histogram);
+};
+
+//------------------------------------------------------------------------------
+
+// LinearHistogram is a more traditional histogram, with evenly spaced
+// buckets.
+class LinearHistogram : public Histogram {
+ public:
+  struct DescriptionPair {
+    Sample sample;
+    char* description;  // Null means end of a list of pairs.
+  };
+  LinearHistogram(const wchar_t* name, Sample minimum,
+            Sample maximum, size_t bucket_count);
+  LinearHistogram(const wchar_t* name, TimeDelta minimum,
+            TimeDelta maximum, size_t bucket_count);
+  ~LinearHistogram() {}
+
+  // Store a list of number/text values for use in rendering the histogram.
+  // The last element in the array has a null in its "description" slot.
+  void SetRangeDescriptions(const DescriptionPair descriptions[]);
+
+ protected:
+  // Initialize ranges_ mapping.
+  virtual void InitializeBucketRange();
+  // Find bucket to increment for sample value.
+  virtual size_t BucketIndex(Sample value) const;
+  virtual double LinearHistogram::GetBucketSize(Count current,
+                                                size_t i) const;
+
+  // If we have a description for a bucket, then return that.  Otherwise
+  // let parent class provide a (numeric) description.
+  virtual const std::string GetAsciiBucketRange(size_t i) const;
+
+  // Skip printing of name for numeric range if we have a name (and if this is
+  // an empty bucket).
+  virtual bool PrintEmptyBucket(size_t index) const;
+
+ private:
+  // For some ranges, we store a printable description of a bucket range.
+  // If there is no desciption, then GetAsciiBucketRange() uses parent class
+  // to provide a description.
+  typedef std::map<Sample, std::string> BucketDescriptionMap;
+  BucketDescriptionMap bucket_description_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(LinearHistogram);
+};
+
+
+//------------------------------------------------------------------------------
+// This section provides implementation for ThreadSafeHistogram.
+//------------------------------------------------------------------------------
+
+class ThreadSafeHistogram : public Histogram {
+ public:
+  ThreadSafeHistogram(const wchar_t* name, Sample minimum,
+                      Sample maximum, size_t bucket_count);
+
+  // Provide the analog to Add()
+  void Remove(int value);
+
+ protected:
+  // Provide locked versions to get precise counts.
+  virtual void Accumulate(Sample value, Count count, size_t index);
+
+  virtual void SnapshotSample(SampleSet* sample);
+
+ private:
+  Lock lock_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(ThreadSafeHistogram);
+};
+
+//------------------------------------------------------------------------------
+// StatisticsRecorder handles all histograms in the system.  It provides a
+// general place for histograms to register, and supports a global API for
+// accessing (i.e., dumping, or graphing) the data in all the histograms.
+
+class StatisticsRecorder {
+ public:
+  typedef std::vector<const Histogram*> Histograms;
+
+  StatisticsRecorder();
+
+  ~StatisticsRecorder();
+
+  // Find out if histograms can now be registered into our list.
+  static bool WasStarted();
+
+  // Register, or add a new histogram to the collection of statistics.
+  // Return true if registered.
+  static bool Register(const Histogram& histogram);
+  // Unregister, or remove, a histogram from the collection of statistics.
+  static void UnRegister(const Histogram& histogram);
+
+  // Methods for printing histograms.  Only histograms which have query as
+  // a substring are written to output (an empty string will process all
+  // registered histograms).
+  static void WriteHTMLGraph(const std::string& query, std::string* output);
+  static void WriteGraph(const std::string& query, std::string* output);
+
+  // Method for extracting histograms which were marked for use by UMA.
+  static void GetHistograms(Histograms* output);
+
+  static void set_dump_on_exit(bool enable) { dump_on_exit_ = enable; }
+
+ private:
+  typedef std::map<std::string, const Histogram*> HistogramMap;
+  // We keep all registered histograms in a map, from name to histogram.
+
+  // GetSnapshot copies some of the pointers to registered histograms into the
+  // caller supplied vector (Histograms).  Only histograms with names matching
+  // query are returned. The query must be a substring of histogram name for its
+  // pointer to be copied.
+  static void GetSnapshot(const std::string& query, Histograms* snapshot);
+
+  static HistogramMap* histograms_;
+  // lock protects access to the above map.
+  static Lock* lock_;
+
+  // Dump all known histograms to log.
+  static bool dump_on_exit_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(StatisticsRecorder);
+};
+
+#endif  // BASE_HISTOGRAM_H__
+
diff --git a/base/histogram_test.cc b/base/histogram_test.cc
new file mode 100644
index 0000000..9830392
--- /dev/null
+++ b/base/histogram_test.cc
@@ -0,0 +1,319 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Test of Histogram class
+
+#include "base/histogram.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class HistogramTest : public testing::Test {
+};
+
+// Check for basic syntax and use.
+TEST(HistogramTest, StartupShutdownTest) {
+  // Try basic construction
+  Histogram histogram(L"TestHistogram", 1, 1000, 10);
+  Histogram histogram1(L"Test1Histogram", 1, 1000, 10);
+
+  LinearHistogram linear_histogram(L"TestLinearHistogram", 1, 1000, 10);
+  LinearHistogram linear_histogram1(L"Test1LinearHistogram", 1, 1000, 10);
+
+  // Use standard macros (but with fixed samples)
+  HISTOGRAM_TIMES(L"Test2Histogram", TimeDelta::FromDays(1));
+  HISTOGRAM_COUNTS(L"Test3Histogram", 30);
+
+  DHISTOGRAM_TIMES(L"Test4Histogram", TimeDelta::FromDays(1));
+  DHISTOGRAM_COUNTS(L"Test5Histogram", 30);
+
+  ASSET_HISTOGRAM_COUNTS(L"Test6Histogram", 129);
+
+  // Try to construct samples.
+  Histogram::SampleSet sample1;
+  Histogram::SampleSet sample2;
+
+  // Use copy constructor of SampleSet
+  sample1 = sample2;
+  Histogram::SampleSet sample3(sample1);
+
+  // Finally test a statistics recorder, without really using it.
+  StatisticsRecorder recorder;
+}
+
+// Repeat with a recorder present to register with.
+TEST(HistogramTest, RecordedStartupTest) {
+  // Test a statistics recorder, by letting histograms register.
+  StatisticsRecorder recorder;  // This initializes the global state.
+
+  StatisticsRecorder::Histograms histograms;
+  EXPECT_EQ(0, histograms.size());
+  StatisticsRecorder::GetHistograms(&histograms);  // Load up lists
+  EXPECT_EQ(0, histograms.size());
+
+  // Try basic construction
+  Histogram histogram(L"TestHistogram", 1, 1000, 10);
+  histograms.clear();
+  StatisticsRecorder::GetHistograms(&histograms);  // Load up lists
+  EXPECT_EQ(1, histograms.size());
+  Histogram histogram1(L"Test1Histogram", 1, 1000, 10);
+  histograms.clear();
+  StatisticsRecorder::GetHistograms(&histograms);  // Load up lists
+  EXPECT_EQ(2, histograms.size());
+
+  LinearHistogram linear_histogram(L"TestLinearHistogram", 1, 1000, 10);
+  LinearHistogram linear_histogram1(L"Test1LinearHistogram", 1, 1000, 10);
+  histograms.clear();
+  StatisticsRecorder::GetHistograms(&histograms);  // Load up lists
+  EXPECT_EQ(4, histograms.size());
+
+  // Use standard macros (but with fixed samples)
+  HISTOGRAM_TIMES(L"Test2Histogram", TimeDelta::FromDays(1));
+  HISTOGRAM_COUNTS(L"Test3Histogram", 30);
+  histograms.clear();
+  StatisticsRecorder::GetHistograms(&histograms);  // Load up lists
+  EXPECT_EQ(6, histograms.size());
+
+  ASSET_HISTOGRAM_COUNTS(L"TestAssetHistogram", 1000);
+  histograms.clear();
+  StatisticsRecorder::GetHistograms(&histograms);  // Load up lists
+  EXPECT_EQ(7, histograms.size());
+
+  DHISTOGRAM_TIMES(L"Test4Histogram", TimeDelta::FromDays(1));
+  DHISTOGRAM_COUNTS(L"Test5Histogram", 30);
+  histograms.clear();
+  StatisticsRecorder::GetHistograms(&histograms);  // Load up lists
+#ifndef NDEBUG
+  EXPECT_EQ(9, histograms.size());
+#else
+  EXPECT_EQ(7, histograms.size());
+#endif
+}
+
+TEST(HistogramTest, RangeTest) {
+  StatisticsRecorder recorder;
+  StatisticsRecorder::Histograms histograms;
+
+  recorder.GetHistograms(&histograms);
+  EXPECT_EQ(0, histograms.size());
+
+  Histogram histogram(L"Histogram", 1, 64, 8);  // As mentioned in header file.
+  // Check that we got a nice exponential when there was enough rooom.
+  EXPECT_EQ(0, histogram.ranges(0));
+  int power_of_2 = 1;
+  for (int i = 1; i < 8; i++) {
+    EXPECT_EQ(power_of_2, histogram.ranges(i));
+    power_of_2 *= 2;
+  }
+  EXPECT_EQ(INT_MAX, histogram.ranges(8));
+
+  Histogram short_histogram(L"Histogram Shortened", 1, 7, 8);
+  // Check that when the number of buckets is short, we get a linear histogram
+  // for lack of space to do otherwise.
+  for (int i = 0; i < 8; i++)
+    EXPECT_EQ(i, short_histogram.ranges(i));
+  EXPECT_EQ(INT_MAX, short_histogram.ranges(8));
+
+  LinearHistogram linear_histogram(L"Linear", 1, 7, 8);
+  // We also get a nice linear set of bucket ranges when we ask for it
+  for (int i = 0; i < 8; i++)
+    EXPECT_EQ(i, linear_histogram.ranges(i));
+  EXPECT_EQ(INT_MAX, linear_histogram.ranges(8));
+
+  LinearHistogram linear_broad_histogram(L"Linear widened", 2, 14, 8);
+  // ...but when the list has more space, then the ranges naturally spread out.
+  for (int i = 0; i < 8; i++)
+    EXPECT_EQ(2 * i, linear_broad_histogram.ranges(i));
+  EXPECT_EQ(INT_MAX, linear_broad_histogram.ranges(8));
+
+  ThreadSafeHistogram threadsafe_histogram(L"ThreadSafe", 1, 32, 15);
+  // When space is a little tight, we transition from linear to exponential.
+  // This is what happens in both the basic histogram, and the threadsafe
+  // variant (which is derived).
+  EXPECT_EQ(0, threadsafe_histogram.ranges(0));
+  EXPECT_EQ(1, threadsafe_histogram.ranges(1));
+  EXPECT_EQ(2, threadsafe_histogram.ranges(2));
+  EXPECT_EQ(3, threadsafe_histogram.ranges(3));
+  EXPECT_EQ(4, threadsafe_histogram.ranges(4));
+  EXPECT_EQ(5, threadsafe_histogram.ranges(5));
+  EXPECT_EQ(6, threadsafe_histogram.ranges(6));
+  EXPECT_EQ(7, threadsafe_histogram.ranges(7));
+  EXPECT_EQ(9, threadsafe_histogram.ranges(8));
+  EXPECT_EQ(11, threadsafe_histogram.ranges(9));
+  EXPECT_EQ(14, threadsafe_histogram.ranges(10));
+  EXPECT_EQ(17, threadsafe_histogram.ranges(11));
+  EXPECT_EQ(21, threadsafe_histogram.ranges(12));
+  EXPECT_EQ(26, threadsafe_histogram.ranges(13));
+  EXPECT_EQ(32, threadsafe_histogram.ranges(14));
+  EXPECT_EQ(INT_MAX, threadsafe_histogram.ranges(15));
+
+  recorder.GetHistograms(&histograms);
+  EXPECT_EQ(5, histograms.size());
+}
+
+// Make sure histogram handles out-of-bounds data gracefully.
+TEST(HistogramTest, BoundsTest) {
+  const int kBucketCount = 50;
+  Histogram histogram(L"Bounded", 10, 100, kBucketCount);
+
+  // Put two samples "out of bounds" above and below.
+  histogram.Add(5);
+  histogram.Add(-50);
+
+  histogram.Add(100);
+  histogram.Add(10000);
+
+  // Verify they landed in the underflow, and overflow buckets.
+  Histogram::SampleSet sample;
+  histogram.SnapshotSample(&sample);
+  EXPECT_EQ(2, sample.counts(0));
+  EXPECT_EQ(0, sample.counts(1));
+  size_t array_size = histogram.bucket_count();
+  EXPECT_EQ(kBucketCount, array_size);
+  EXPECT_EQ(0, sample.counts(array_size - 2));
+  EXPECT_EQ(2, sample.counts(array_size - 1));
+}
+
+// Check to be sure samples land as expected is "correct" buckets.
+TEST(HistogramTest, BucketPlacementTest) {
+  Histogram histogram(L"Histogram", 1, 64, 8);  // As mentioned in header file.
+
+  // Check that we got a nice exponential since there was enough rooom.
+  EXPECT_EQ(0, histogram.ranges(0));
+  int power_of_2 = 1;
+  for (int i = 1; i < 8; i++) {
+    EXPECT_EQ(power_of_2, histogram.ranges(i));
+    power_of_2 *= 2;
+  }
+  EXPECT_EQ(INT_MAX, histogram.ranges(8));
+
+  // Add i+1 samples to the i'th bucket.
+  histogram.Add(0);
+  power_of_2 = 1;
+  for (int i = 1; i < 8; i++) {
+    for (int j = 0; j <= i; j++)
+      histogram.Add(power_of_2);
+    power_of_2 *= 2;
+  }
+  // Leave overflow bucket empty.
+
+  // Check to see that the bucket counts reflect our additions.
+  Histogram::SampleSet sample;
+  histogram.SnapshotSample(&sample);
+  EXPECT_EQ(INT_MAX, histogram.ranges(8));
+  for (int i = 0; i < 8; i++)
+    EXPECT_EQ(i + 1, sample.counts(i));
+}
+
+static const wchar_t* kAssetTestHistogramName = L"AssetCountTest";
+static const wchar_t* kAssetTestDebugHistogramName = L"DAssetCountTest";
+void AssetCountFunction(int sample) {
+  ASSET_HISTOGRAM_COUNTS(kAssetTestHistogramName, sample);
+  DASSET_HISTOGRAM_COUNTS(kAssetTestDebugHistogramName, sample);
+}
+// Check that asset can be added and removed from buckets.
+TEST(HistogramTest, AssetCountTest) {
+  // Start up a recorder system to identify all histograms.
+  StatisticsRecorder recorder;
+
+  // Call through the macro to instantiate the static variables.
+  AssetCountFunction(100);  // Put a sample in the bucket for 100.
+
+  // Find the histogram.
+  StatisticsRecorder::Histograms histogram_list;
+  StatisticsRecorder::GetHistograms(&histogram_list);
+  ASSERT_GT(histogram_list.size(), 0u);
+  std::string ascii_name = WideToASCII(kAssetTestHistogramName);
+  std::string debug_ascii_name = WideToASCII(kAssetTestDebugHistogramName);
+  const Histogram* our_histogram = NULL;
+  const Histogram* our_debug_histogram = NULL;
+  for (StatisticsRecorder::Histograms::iterator it = histogram_list.begin();
+       it != histogram_list.end();
+       ++it) {
+    if (!(*it)->histogram_name().compare(ascii_name))
+      our_histogram = *it;
+    else if (!(*it)->histogram_name().compare(debug_ascii_name)) {
+      our_debug_histogram = *it;
+    }
+  }
+  ASSERT_TRUE(our_histogram);
+#ifndef NDEBUG
+  EXPECT_TRUE(our_debug_histogram);
+#else
+  EXPECT_FALSE(our_debug_histogram);
+#endif
+  // Verify it has a 1 in exactly one bucket (where we put the sample).
+  Histogram::SampleSet sample;
+  our_histogram->SnapshotSample(&sample);
+  int match_count = 0;
+  for (size_t i = 0; i < our_histogram->bucket_count(); ++i) {
+    if (sample.counts(i) > 0) {
+      EXPECT_LT(++match_count, 2) << "extra count in bucket " << i;
+    }
+  }
+  EXPECT_EQ(1u, match_count);
+
+  // Remove our sample.
+  AssetCountFunction(-100);  // Remove a sample from the bucket for 100.
+  our_histogram->SnapshotSample(&sample);  // Extract data set.
+
+  // Verify that the bucket is now empty, as are all the other buckets.
+  for (size_t i = 0; i < our_histogram->bucket_count(); ++i) {
+    EXPECT_EQ(0, sample.counts(i)) << "extra count in bucket " << i;
+  }
+
+  if (!our_debug_histogram)
+    return;  // This is a production build.
+
+  // Repeat test with debug histogram.  Note that insertion and deletion above
+  // should have cancelled each other out.
+  AssetCountFunction(100);  // Add a sample into the bucket for 100.
+  our_debug_histogram->SnapshotSample(&sample);
+  match_count = 0;
+  for (size_t i = 0; i < our_debug_histogram->bucket_count(); ++i) {
+    if (sample.counts(i) > 0) {
+      EXPECT_LT(++match_count, 2) << "extra count in bucket " << i;
+    }
+  }
+  EXPECT_EQ(1u, match_count);
+
+  // Remove our sample.
+  AssetCountFunction(-100);  // Remove a sample from the bucket for 100.
+  our_debug_histogram->SnapshotSample(&sample);  // Extract data set.
+
+  // Verify that the bucket is now empty, as are all the other buckets.
+  for (size_t i = 0; i < our_debug_histogram->bucket_count(); ++i) {
+    EXPECT_EQ(0, sample.counts(i)) << "extra count in bucket " << i;
+  }
+}
+
+}  // namespace
diff --git a/base/hmac.cc b/base/hmac.cc
new file mode 100644
index 0000000..ee86ebd
--- /dev/null
+++ b/base/hmac.cc
@@ -0,0 +1,121 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/hmac.h"
+#include "base/logging.h"
+
+HMAC::HMAC(HashAlgorithm hash_alg, const unsigned char* key, int key_length)
+    : hash_alg_(hash_alg),
+      provider_(NULL),
+      hash_(NULL),
+      hkey_(NULL) {
+  if (!CryptAcquireContext(&provider_, NULL, NULL,
+                           PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
+    provider_ = NULL;
+  ImportKey(key, key_length);
+}
+
+HMAC::~HMAC() {
+  if (hkey_)
+    CryptDestroyKey(hkey_);
+  if (hash_)
+    CryptDestroyHash(hash_);
+  if (provider_)
+    CryptReleaseContext(provider_, 0);
+}
+
+bool HMAC::Sign(const std::string& data,
+                unsigned char* digest,
+                int digest_length) {
+  if (!provider_ || !hkey_)
+    return false;
+
+  switch (hash_alg_) {
+    case SHA1:
+      return SignWithSHA1(data, digest, digest_length);
+    default:
+      NOTREACHED();
+      return false;
+  }
+}
+
+void HMAC::ImportKey(const unsigned char* key, int key_length) {
+  if (key_length > kMaxKeySize) {
+    NOTREACHED();
+    return;
+  }
+
+  struct {
+    BLOBHEADER header;
+    DWORD key_size;
+    BYTE key_data[kMaxKeySize];
+  } key_blob;
+  key_blob.header.bType = PLAINTEXTKEYBLOB;
+  key_blob.header.bVersion = CUR_BLOB_VERSION;
+  key_blob.header.reserved = 0;
+  key_blob.header.aiKeyAlg = CALG_RC2;
+  key_blob.key_size = key_length;
+  memcpy(key_blob.key_data, key, key_length);
+
+  if (!CryptImportKey(provider_,
+                      reinterpret_cast<const BYTE *>(&key_blob),
+                      sizeof(key_blob), 0, 0, &hkey_))
+    hkey_ = NULL;
+
+  // Destroy the copy of the key.
+  SecureZeroMemory(key_blob.key_data, key_length);
+}
+
+bool HMAC::SignWithSHA1(const std::string& data,
+                        unsigned char* digest,
+                        int digest_length) {
+  DCHECK(provider_);
+  DCHECK(hkey_);
+
+  if (!CryptCreateHash(provider_, CALG_HMAC, hkey_, 0, &hash_))
+    return false;
+
+  HMAC_INFO hmac_info;
+  memset(&hmac_info, 0, sizeof(hmac_info));
+  hmac_info.HashAlgid = CALG_SHA1;
+  if (!CryptSetHashParam(hash_, HP_HMAC_INFO,
+                         reinterpret_cast<BYTE*>(&hmac_info), 0))
+    return false;
+
+  if (!CryptHashData(hash_,
+                     reinterpret_cast<const BYTE*>(data.data()),
+                     static_cast<DWORD>(data.size()), 0))
+    return false;
+
+  DWORD sha1_size = digest_length;
+  if (!CryptGetHashParam(hash_, HP_HASHVAL, digest, &sha1_size, 0))
+    return false;
+
+  return true;
+}
diff --git a/base/hmac.h b/base/hmac.h
new file mode 100644
index 0000000..37c8fc9
--- /dev/null
+++ b/base/hmac.h
@@ -0,0 +1,87 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Utility class for calculating the HMAC for a given message. We currently
+// only support SHA1 for the hash algorithm, but this can be extended easily.
+
+#ifndef BASE_HMAC_H__
+#define BASE_HMAC_H__
+
+#include <windows.h>
+#include <wincrypt.h>
+
+#include <string>
+
+#include "base/basictypes.h"
+
+class HMAC {
+ public:
+  // The set of supported hash functions. Extend as required.
+  enum HashAlgorithm {
+    SHA1
+  };
+
+  HMAC(HashAlgorithm hash_alg, const unsigned char* key, int key_length);
+  ~HMAC();
+
+  // Returns the HMAC in 'digest' for the message in 'data' and the key
+  // specified in the contructor.
+  bool Sign(const std::string& data, unsigned char* digest, int digest_length);
+
+ private:
+  // Import the key so that we don't have to store it ourself.
+  // TODO(paulg): Bug: http://b/1084719, 'ImportKey' will not currently work on
+  //              Windows 2000 since it requires special handling for importing
+  //              keys. See this link for details:
+  // http://www.derkeiler.com/Newsgroups/microsoft.public.platformsdk.security/2004-06/0270.html
+  void ImportKey(const unsigned char* key, int key_length);
+
+  // Returns the SHA1 hash of 'data' and 'key' in 'digest'. If there was any
+  // error in the calculation, this method returns false, otherwise true.
+  bool SignWithSHA1(const std::string& data,
+                    unsigned char* digest,
+                    int digest_length);
+
+  // Required for the SHA1 key_blob struct. We limit this to 16 bytes since
+  // Windows 2000 doesn't support keys larger than that.
+  static const int kMaxKeySize = 16;
+
+  // The hash algorithm to use.
+  HashAlgorithm hash_alg_;
+
+  // Windows Crypt API resources.
+  HCRYPTPROV provider_;
+  HCRYPTHASH hash_;
+  HCRYPTKEY hkey_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(HMAC);
+};
+
+
+#endif  // BASE_HMAC_H__
\ No newline at end of file
diff --git a/base/hmac_unittest.cc b/base/hmac_unittest.cc
new file mode 100644
index 0000000..1df5137
--- /dev/null
+++ b/base/hmac_unittest.cc
@@ -0,0 +1,94 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Program to generate an HMAC-SHA1 digest for a given string, for use in
+// calculating and verifying SafeBrowsing MACs.
+
+#include <string>
+
+#include "base/hmac.h"
+#include "base/no_windows2000_unittest.h"
+
+static const int kKeySize = 16;
+static const int kDigestSize = 20;
+
+// Client key.
+const unsigned char kClientKey[kKeySize] =
+    { 0xbf, 0xf6, 0x83, 0x4b, 0x3e, 0xa3, 0x23, 0xdd,
+      0x96, 0x78, 0x70, 0x8e, 0xa1, 0x9d, 0x3b, 0x40 };
+
+// Expected HMAC result using kMessage and kClientKey.
+const unsigned char kReceivedHmac[kDigestSize] =
+    { 0xb9, 0x3c, 0xd6, 0xf0, 0x49, 0x47, 0xe2, 0x52,
+      0x59, 0x7a, 0xbd, 0x1f, 0x2b, 0x4c, 0x83, 0xad,
+      0x86, 0xd2, 0x48, 0x85 };
+
+const char kMessage[] =
+"n:1896\ni:goog-malware-shavar\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shav"
+"ar_s_445-450\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_439-444\nu:s"
+".ytimg.com/safebrowsing/rd/goog-malware-shavar_s_437\nu:s.ytimg.com/safebrowsi"
+"ng/rd/goog-malware-shavar_s_436\nu:s.ytimg.com/safebrowsing/rd/goog-malware-sh"
+"avar_s_433-435\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_431\nu:s.y"
+"timg.com/safebrowsing/rd/goog-malware-shavar_s_430\nu:s.ytimg.com/safebrowsing"
+"/rd/goog-malware-shavar_s_429\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shav"
+"ar_s_428\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_426\nu:s.ytimg.c"
+"om/safebrowsing/rd/goog-malware-shavar_s_424\nu:s.ytimg.com/safebrowsing/rd/go"
+"og-malware-shavar_s_423\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_4"
+"22\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_420\nu:s.ytimg.com/saf"
+"ebrowsing/rd/goog-malware-shavar_s_419\nu:s.ytimg.com/safebrowsing/rd/goog-mal"
+"ware-shavar_s_414\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_409-411"
+"\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_405\nu:s.ytimg.com/safeb"
+"rowsing/rd/goog-malware-shavar_s_404\nu:s.ytimg.com/safebrowsing/rd/goog-malwa"
+"re-shavar_s_402\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_401\nu:s."
+"ytimg.com/safebrowsing/rd/goog-malware-shavar_a_973-978\nu:s.ytimg.com/safebro"
+"wsing/rd/goog-malware-shavar_a_937-972\nu:s.ytimg.com/safebrowsing/rd/goog-mal"
+"ware-shavar_a_931-936\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_a_925"
+"-930\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_a_919-924\ni:goog-phis"
+"h-shavar\nu:s.ytimg.com/safebrowsing/rd/goog-phish-shavar_a_2633\nu:s.ytimg.co"
+"m/safebrowsing/rd/goog-phish-shavar_a_2632\nu:s.ytimg.com/safebrowsing/rd/goog"
+"-phish-shavar_a_2629-2631\nu:s.ytimg.com/safebrowsing/rd/goog-phish-shavar_a_2"
+"626-2628\nu:s.ytimg.com/safebrowsing/rd/goog-phish-shavar_a_2625\n";
+
+// TODO(paulg): Bug: http://b/1084719, skip this test on Windows 2000 until
+//              this bug is fixed.
+class HMACTest : public NoWindows2000Test<testing::Test> {
+};
+
+TEST_F(HMACTest, HmacSafeBrowsingResponseTest) {
+  if (IsTestCaseDisabled())
+    return;
+
+  std::string message_data(kMessage);
+
+  HMAC hmac(HMAC::SHA1, kClientKey, kKeySize);
+  unsigned char calculated_hmac[kDigestSize];
+
+  EXPECT_TRUE(hmac.Sign(message_data, calculated_hmac, kDigestSize));
+  EXPECT_EQ(memcmp(kReceivedHmac, calculated_hmac, kDigestSize), 0);
+}
diff --git a/base/iat_patch.cc b/base/iat_patch.cc
new file mode 100644
index 0000000..6c4fe1e
--- /dev/null
+++ b/base/iat_patch.cc
@@ -0,0 +1,246 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/iat_patch.h"
+#include "base/logging.h"
+
+namespace iat_patch {
+
+struct InterceptFunctionInformation {
+  bool finished_operation;
+  const char* imported_from_module;
+  const char* function_name;
+  void* new_function;
+  void** old_function;
+  IMAGE_THUNK_DATA** iat_thunk;
+  DWORD return_code;
+};
+
+static void* GetIATFunction(IMAGE_THUNK_DATA* iat_thunk) {
+  if (NULL == iat_thunk) {
+    NOTREACHED();
+    return NULL;
+  }
+
+  // Works around the 64 bit portability warning:
+  // The Function member inside IMAGE_THUNK_DATA is really a pointer
+  // to the IAT function. IMAGE_THUNK_DATA correctly maps to IMAGE_THUNK_DATA32
+  // or IMAGE_THUNK_DATA64 for correct pointer size.
+  union FunctionThunk {
+    IMAGE_THUNK_DATA thunk;
+    void* pointer;
+  } iat_function;
+
+  iat_function.thunk = *iat_thunk;
+  return iat_function.pointer;
+}
+
+static bool InterceptEnumCallback(const PEImage &image, const char* module,
+                                  DWORD ordinal, const char* name, DWORD hint,
+                                  IMAGE_THUNK_DATA* iat, void* cookie) {
+  InterceptFunctionInformation* intercept_information =
+    reinterpret_cast<InterceptFunctionInformation*>(cookie);
+
+  if (NULL == intercept_information) {
+    NOTREACHED();
+    return false;
+  }
+
+  DCHECK(module);
+
+  if ((0 == lstrcmpiA(module, intercept_information->imported_from_module)) &&
+     (NULL != name) &&
+     (0 == lstrcmpiA(name, intercept_information->function_name))) {
+    // Save the old pointer.
+    if (NULL != intercept_information->old_function) {
+      *(intercept_information->old_function) = GetIATFunction(iat);
+    }
+
+    if (NULL != intercept_information->iat_thunk) {
+      *(intercept_information->iat_thunk) = iat;
+    }
+
+    // portability check
+    COMPILE_ASSERT(sizeof(iat->u1.Function) ==
+      sizeof(intercept_information->new_function), unknown_IAT_thunk_format);
+
+    // Patch the function.
+    intercept_information->return_code =
+      ModifyCode(&(iat->u1.Function),
+                 &(intercept_information->new_function),
+                 sizeof(intercept_information->new_function));
+
+    // Terminate further enumeration.
+    intercept_information->finished_operation = true;
+    return false;
+  }
+
+  return true;
+}
+
+DWORD InterceptImportedFunction(HMODULE module_handle,
+                                const char* imported_from_module,
+                                const char* function_name, void* new_function,
+                                void** old_function,
+                                IMAGE_THUNK_DATA** iat_thunk) {
+  if ((NULL == module_handle) || (NULL == imported_from_module) ||
+     (NULL == function_name) || (NULL == new_function)) {
+    NOTREACHED();
+    return ERROR_INVALID_PARAMETER;
+  }
+
+  PEImage target_image(module_handle);
+  if (!target_image.VerifyMagic()) {
+    NOTREACHED();
+    return ERROR_INVALID_PARAMETER;
+  }
+
+  InterceptFunctionInformation intercept_information = {
+    false,
+    imported_from_module,
+    function_name,
+    new_function,
+    old_function,
+    iat_thunk,
+    ERROR_GEN_FAILURE};
+
+  // First go through the IAT. If we don't find the import we are looking
+  // for in IAT, search delay import table.
+  target_image.EnumAllImports(InterceptEnumCallback, &intercept_information);
+  if (!intercept_information.finished_operation) {
+    target_image.EnumAllDelayImports(InterceptEnumCallback,
+                                     &intercept_information);
+  }
+
+  return intercept_information.return_code;
+}
+
+DWORD RestoreImportedFunction(void* intercept_function,
+                              void* original_function,
+                              IMAGE_THUNK_DATA* iat_thunk) {
+  if ((NULL == intercept_function) || (NULL == original_function) ||
+      (NULL == iat_thunk)) {
+    NOTREACHED();
+    return ERROR_INVALID_PARAMETER;
+  }
+
+  if (GetIATFunction(iat_thunk) != intercept_function) {
+    // Check if someone else has intercepted on top of us.
+    // We cannot unpatch in this case, just raise a red flag.
+    NOTREACHED();
+    return ERROR_INVALID_FUNCTION;
+  }
+
+  return ModifyCode(&(iat_thunk->u1.Function),
+                    &original_function,
+                    sizeof(original_function));
+}
+
+DWORD ModifyCode(void* old_code, void* new_code, int length) {
+  if ((NULL == old_code) || (NULL == new_code) || (0 == length)) {
+    NOTREACHED();
+    return ERROR_INVALID_PARAMETER;
+  }
+
+  // Change the page protection so that we can write.
+  DWORD error = NO_ERROR;
+  DWORD old_page_protection = 0;
+  if (VirtualProtect(old_code,
+                     length,
+                     PAGE_READWRITE,
+                     &old_page_protection)) {
+
+    // Write the data.
+    CopyMemory(old_code, new_code, length);
+
+    // Restore the old page protection.
+    error = ERROR_SUCCESS;
+    VirtualProtect(old_code,
+                  length,
+                  old_page_protection,
+                  &old_page_protection);
+  } else {
+    error = GetLastError();
+    NOTREACHED();
+  }
+
+  return error;
+}
+
+IATPatchFunction::IATPatchFunction()
+    : original_function_(NULL),
+      iat_thunk_(NULL),
+      intercept_function_(NULL) {
+}
+
+IATPatchFunction::~IATPatchFunction() {
+  if (NULL != intercept_function_) {
+    DWORD error = Unpatch();
+    DCHECK_EQ(NO_ERROR, error);
+  }
+}
+
+DWORD IATPatchFunction::Patch(HMODULE module_handle,
+                              const char* imported_from_module,
+                              const char* function_name,
+                              void* new_function) {
+  DCHECK_EQ(static_cast<void*>(NULL), original_function_);
+  DCHECK_EQ(static_cast<IMAGE_THUNK_DATA*>(NULL), iat_thunk_);
+  DCHECK_EQ(static_cast<void*>(NULL), intercept_function_);
+
+  DWORD error = InterceptImportedFunction(module_handle,
+                                          imported_from_module,
+                                          function_name,
+                                          new_function,
+                                          &original_function_,
+                                          &iat_thunk_);
+
+  if (NO_ERROR == error) {
+    DCHECK_NE(original_function_, intercept_function_);
+    intercept_function_ = new_function;
+  }
+
+  return error;
+}
+
+DWORD IATPatchFunction::Unpatch() {
+  DWORD error = RestoreImportedFunction(intercept_function_,
+                                        original_function_,
+                                        iat_thunk_);
+
+  if (NO_ERROR == error) {
+    intercept_function_ = NULL;
+    original_function_ = NULL;
+    iat_thunk_ = NULL;
+  }
+
+  return error;
+}
+
+}  // namespace iat_patch
diff --git a/base/iat_patch.h b/base/iat_patch.h
new file mode 100644
index 0000000..91e6f99
--- /dev/null
+++ b/base/iat_patch.h
@@ -0,0 +1,140 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file declares a helpers to intercept functions from a DLL.
+//
+// This set of functions are designed to intercept functions for a
+// specific DLL imported from another DLL. This is the case when,
+// for example, we want to intercept CertDuplicateCertificateContext
+// function (exported from crypt32.dll) called by wininet.dll.
+
+#ifndef BASE_IAT_PATCH_H__
+#define BASE_IAT_PATCH_H__
+
+#include <windows.h>
+#include "base/basictypes.h"
+#include "base/pe_image.h"
+
+namespace iat_patch {
+
+// Helper to intercept a function in an import table of a specific
+// module.
+//
+// Arguments:
+// module_handle          Module to be intercepted
+// imported_from_module   Module that exports the symbol
+// function_name          Name of the API to be intercepted
+// new_function           Interceptor function
+// old_function           Receives the original function pointer
+// iat_thunk              Receives pointer to IAT_THUNK_DATA
+//                        for the API from the import table.
+//
+// Returns: Returns NO_ERROR on success or Windows error code
+//          as defined in winerror.h
+//
+DWORD InterceptImportedFunction(HMODULE module_handle,
+                                const char* imported_from_module,
+                                const char* function_name,
+                                void* new_function,
+                                void** old_function,
+                                IMAGE_THUNK_DATA** iat_thunk);
+
+// Restore intercepted IAT entry with the original function.
+//
+// Arguments:
+// intercept_function     Interceptor function
+// original_function      Receives the original function pointer
+//
+// Returns: Returns NO_ERROR on success or Windows error code
+//          as defined in winerror.h
+//
+DWORD RestoreImportedFunction(void* intercept_function,
+                              void* original_function,
+                              IMAGE_THUNK_DATA* iat_thunk);
+
+// Change the page protection (of code pages) to writable and copy
+// the data at the specified location
+//
+// Arguments:
+// old_code               Target location to copy
+// new_code               Source
+// length                 Number of bytes to copy
+//
+// Returns: Windows error code (winerror.h). NO_ERROR if successful
+//
+DWORD ModifyCode(void* old_code,
+                 void* new_code,
+                 int length);
+
+// A class that encapsulates IAT patching helpers and restores
+// the original function in the destructor.
+class IATPatchFunction {
+ public:
+  IATPatchFunction();
+  ~IATPatchFunction();
+
+  // Intercept a function in an import table of a specific
+  // module. Save the original function and the import
+  // table address. These values will be used later
+  // during Unpatch
+  //
+  // Arguments:
+  // module_handle          Module to be intercepted
+  // imported_from_module   Module that exports the 'function_name'
+  // function_name          Name of the API to be intercepted
+  //
+  // Returns: Windows error code (winerror.h). NO_ERROR if successful
+  //
+  DWORD Patch(HMODULE module_handle,
+              const char* imported_from_module,
+              const char* function_name,
+              void* new_function);
+
+  // Unpatch the IAT entry using internally saved original
+  // function.
+  //
+  // Returns: Windows error code (winerror.h). NO_ERROR if successful
+  //
+  DWORD Unpatch();
+
+  bool is_patched() const {
+    return (NULL != intercept_function_);
+  }
+
+ private:
+  void* intercept_function_;
+  void* original_function_;
+  IMAGE_THUNK_DATA* iat_thunk_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(IATPatchFunction);
+};
+
+}  // namespace iat_patch
+
+#endif  // BASE_IAT_PATCH_H__
diff --git a/base/icu_util.cc b/base/icu_util.cc
new file mode 100644
index 0000000..4e72632
--- /dev/null
+++ b/base/icu_util.cc
@@ -0,0 +1,70 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+#include <string>
+
+#include "base/icu_util.h"
+
+#include "base/logging.h"
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "unicode/udata.h"
+
+namespace icu_util {
+
+bool Initialize() {
+  // Assert that we are not called more than once.  Even though calling this
+  // function isn't harmful (ICU can handle it), being called twice probably
+  // indicates a programming error.
+#ifndef DEBUG
+  static bool called_once = false;
+  DCHECK(!called_once);
+  called_once = true;
+#endif
+
+  // We expect to find the ICU data module alongside the current module.
+  std::wstring data_path;
+  PathService::Get(base::DIR_MODULE, &data_path);
+  file_util::AppendToPath(&data_path, L"icudt38.dll");
+
+  HMODULE module = LoadLibrary(data_path.c_str());
+  if (!module)
+    return false;
+
+  FARPROC addr = GetProcAddress(module, "icudt38_dat");
+  if (!addr)
+    return false;
+
+  UErrorCode err = U_ZERO_ERROR;
+  udata_setCommonData(reinterpret_cast<void*>(addr), &err);
+  return err == U_ZERO_ERROR;
+}
+
+}  // namespace icu_util
diff --git a/base/icu_util.h b/base/icu_util.h
new file mode 100644
index 0000000..d4af52c
--- /dev/null
+++ b/base/icu_util.h
@@ -0,0 +1,41 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_ICU_UTIL_H__
+#define BASE_ICU_UTIL_H__
+
+namespace icu_util {
+
+// Call this function to load ICU's data tables for the current process.  This
+// function should be called before ICU is used.
+bool Initialize();
+
+}  // namespace icu_util
+
+#endif  // BASE_ICU_UTIL_H__
diff --git a/base/id_map.h b/base/id_map.h
new file mode 100644
index 0000000..e945f6a
--- /dev/null
+++ b/base/id_map.h
@@ -0,0 +1,122 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_ID_MAP_H__
+#define BASE_ID_MAP_H__
+
+// hash map is common (GCC4 and Dinkumware also support it, for example), but
+// is not strictly part of the C++ standard. MS puts it into a funny namespace,
+// although most other vendors seem to use std. This may have to change if
+// other platforms are supported.
+#include <hash_map>
+using stdext::hash_map;
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+// This object maintains a list of IDs that can be quickly converted to
+// pointers to objects. It is implemented as a hash table, optimized for
+// relatively small data sets (in the common case, there will be exactly one
+// item in the list).
+//
+// Items can be inserted into the container with arbitrary ID, but the caller
+// must ensure they are unique. Inserting IDs and relying on automatically
+// generated ones is not allowed because they can collide.
+template<class T>
+class IDMap {
+ private:
+  typedef hash_map<int32, T*> HashTable;
+
+ public:
+  IDMap() : next_id_(1) {
+  }
+  IDMap(const IDMap& other) : next_id_(other.next_id_),
+                                        data_(other.data_) {
+  }
+
+  // support const iterators over the items
+  // Note, use iterator->first to get the ID, iterator->second to get the T*
+  typedef typename HashTable::const_iterator const_iterator;
+  const_iterator begin() const {
+    return data_.begin();
+  }
+  const_iterator end() const {
+    return data_.end();
+  }
+
+  // Adds a view with an automatically generated unique ID. See AddWithID.
+  int32 Add(T* data) {
+    int32 this_id = next_id_;
+    DCHECK(data_.find(this_id) == data_.end()) << "Inserting duplicate item";
+    data_[this_id] = data;
+    next_id_++;
+    return this_id;
+  }
+
+  // Adds a new data member with the specified ID. The ID must not be in
+  // the list. The caller either must generate all unique IDs itself and use
+  // this function, or allow this object to generate IDs and call Add. These
+  // two methods may not be mixed, or duplicate IDs may be generated
+  void AddWithID(T* data, int32 id) {
+    DCHECK(data_.find(id) == data_.end()) << "Inserting duplicate item";
+    data_[id] = data;
+  }
+
+  void Remove(int32 id) {
+    HashTable::iterator i = data_.find(id);
+    if (i == data_.end()) {
+      NOTREACHED() << "Attempting to remove an item not in the list";
+      return;
+    }
+    data_.erase(i);
+  }
+
+  bool IsEmpty() const {
+    return data_.empty();
+  }
+
+  T* Lookup(int32 id) const {
+    HashTable::const_iterator i = data_.find(id);
+    if (i == data_.end())
+      return NULL;
+    return i->second;
+  }
+
+  size_t size() const {
+    return data_.size();
+  }
+
+ protected:
+  // The next ID that we will return from Add()
+  int32 next_id_;
+
+  HashTable data_;
+};
+
+#endif  // BASE_ID_MAP_H__
diff --git a/base/idle_timer.cc b/base/idle_timer.cc
new file mode 100644
index 0000000..a69cba3
--- /dev/null
+++ b/base/idle_timer.cc
@@ -0,0 +1,103 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+
+#include "base/idle_timer.h"
+
+#include "base/message_loop.h"
+#include "base/time.h"
+
+IdleTimerTask::IdleTimerTask(TimeDelta idle_time, bool repeat)
+  : idle_interval_(idle_time),
+    repeat_(repeat),
+    get_last_input_info_fn_(GetLastInputInfo) {
+}
+
+IdleTimerTask::~IdleTimerTask() {
+  Stop();
+}
+
+void IdleTimerTask::Start() {
+  DCHECK(!timer_.get());
+  StartTimer();
+}
+
+void IdleTimerTask::Stop() {
+  timer_.reset();
+}
+
+void IdleTimerTask::Run() {
+  // Verify we can fire the idle timer.
+  if (TimeUntilIdle().InMilliseconds() <= 0) {
+    OnIdle();
+    last_time_fired_ = Time::Now();
+  }
+  Stop();
+  StartTimer();  // Restart the timer for next run.
+}
+
+void IdleTimerTask::StartTimer() {
+  DCHECK(timer_ == NULL);
+  TimeDelta delay = TimeUntilIdle();
+  if (delay.InMilliseconds() < 0)
+    delay = TimeDelta();
+  timer_.reset(new OneShotTimer(delay));
+  timer_->set_unowned_task(this);
+  timer_->Start();
+}
+
+TimeDelta IdleTimerTask::CurrentIdleTime() {
+  // TODO(mbelshe): This is windows-specific code.
+  LASTINPUTINFO info;
+  info.cbSize = sizeof(info);
+  if (get_last_input_info_fn_(&info)) {
+    // Note: GetLastInputInfo returns a 32bit value which rolls over ~49days.
+    int32 last_input_time = info.dwTime;
+    int32 current_time = GetTickCount();
+    int32 interval = current_time - last_input_time;
+    // Interval will go negative if we've been idle for 2GB of ticks.
+    if (interval < 0)
+      interval = -interval;
+    return TimeDelta::FromMilliseconds(interval);
+  }
+  NOTREACHED();
+  return TimeDelta::FromMilliseconds(0);
+}
+
+TimeDelta IdleTimerTask::TimeUntilIdle() {
+  TimeDelta time_since_last_fire = Time::Now() - last_time_fired_;
+  TimeDelta current_idle_time = CurrentIdleTime();
+  if (current_idle_time > time_since_last_fire) {
+    if (repeat_)
+      return idle_interval_ - time_since_last_fire;
+    return idle_interval_;
+  }
+  return idle_interval_ - current_idle_time;
+}
diff --git a/base/idle_timer.h b/base/idle_timer.h
new file mode 100644
index 0000000..01f2fc0
--- /dev/null
+++ b/base/idle_timer.h
@@ -0,0 +1,114 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_IDLE_TIMER_H__
+#define BASE_IDLE_TIMER_H__
+
+#include <windows.h>
+
+#include "base/basictypes.h"
+#include "base/task.h"
+#include "base/timer.h"
+
+// IdleTimer is a recurring Timer task which runs only when the system is idle.
+// System Idle time is defined as not having any user keyboard or mouse
+// activity for some period of time.  Because the timer is user dependant, it
+// is possible for the timer to never fire.
+
+//
+// Usage should be for low-priority tasks, and may look like this:
+//
+//   class MyIdleTimerTask : public IdleTimerTask {
+//    public:
+//     // This task will run after 5 seconds of idle time
+//     // and not more often than once per minute.
+//     MyIdleTimerTask() : IdleTimerTask(5, 60) {};
+//     virtual void OnIdle() { do something };
+//   }
+//
+//   MyIdleTimerTask *task = new MyIdleTimerTask();
+//   task->Start();
+//
+//   // As with all TimerTasks, the caller must dispose the object.
+//   delete task;  // Will Stop the timer and cleanup the task.
+
+class TimerManager;
+
+// Function prototype for GetLastInputInfo.
+typedef BOOL (__stdcall *GetLastInputInfoFunction)(PLASTINPUTINFO plii);
+
+class IdleTimerTask : public Task {
+ public:
+  // Create an IdleTimerTask.
+  // idle_time: idle time required before this task can run.
+  // repeat: true if the timer should fire multiple times per idle,
+  //         false to fire once per idle.
+  IdleTimerTask(TimeDelta idle_time, bool repeat);
+
+  // On destruction, the IdleTimerTask will Stop itself and delete the Task.
+  virtual ~IdleTimerTask();
+
+  // Start the IdleTimerTask.
+  void Start();
+
+  // Stop the IdleTimertask.
+  void Stop();
+
+  // The method to run when the timer elapses.
+  virtual void OnIdle() = 0;
+
+ protected:
+  // Override the GetLastInputInfo function.
+  void set_last_input_info_fn(GetLastInputInfoFunction function) {
+   get_last_input_info_fn_ = function;
+  }
+
+ private:
+  // This task's run method.
+  virtual void Run();
+
+  // Start the timer.
+  void StartTimer();
+
+  // Gets the number of milliseconds since the last input event.
+  TimeDelta CurrentIdleTime();
+
+  // Compute time until idle.  Returns 0 if we are now idle.
+  TimeDelta TimeUntilIdle();
+
+  TimeDelta idle_interval_;
+  bool repeat_;
+  Time last_time_fired_;  // The last time the idle timer fired.
+                          // will be 0 until the timer fires the first time.
+  scoped_ptr<OneShotTimer> timer_;
+
+  GetLastInputInfoFunction get_last_input_info_fn_;
+};
+
+#endif  // BASE_IDLE_TIMER_H__
diff --git a/base/idletimer_unittest.cc b/base/idletimer_unittest.cc
new file mode 100644
index 0000000..c3e8e6d
--- /dev/null
+++ b/base/idletimer_unittest.cc
@@ -0,0 +1,221 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/idle_timer.h"
+#include "base/message_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+  class IdleTimerTest : public testing::Test {
+  };
+};
+
+// We Mock the GetLastInputInfo function to return
+// the time stored here.
+static DWORD mock_idle_time = GetTickCount();
+
+BOOL __stdcall MockGetLastInputInfoFunction(PLASTINPUTINFO plii) {
+  DCHECK(plii->cbSize == sizeof(LASTINPUTINFO));
+  plii->dwTime = mock_idle_time;
+  return TRUE;
+}
+
+// TestIdle task fires after 100ms of idle time.
+class TestIdleTask : public IdleTimerTask {
+ public:
+  TestIdleTask(bool repeat)
+    : IdleTimerTask(TimeDelta::FromMilliseconds(100), repeat),
+     idle_counter_(0) {
+     set_last_input_info_fn(MockGetLastInputInfoFunction);
+  }
+
+  int get_idle_counter() { return idle_counter_; }
+
+  virtual void OnIdle() {
+    idle_counter_++;
+  }
+
+ private:
+  int idle_counter_;
+};
+
+// A task to help us quit the test.
+class TestFinishedTask : public Task {
+ public:
+  TestFinishedTask() {}
+  void Run() {
+    MessageLoop::current()->Quit();
+  }
+};
+
+// A timer which resets the idle clock.
+class ResetIdleTask : public Task {
+ public:
+  ResetIdleTask() {}
+  void Run() {
+    mock_idle_time = GetTickCount();
+  }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// NoRepeat tests:
+// A non-repeating idle timer will fire once on idle, and
+// then will not fire again unless it goes non-idle first.
+
+TEST(IdleTimerTest, NoRepeatIdle) {
+  // Create an IdleTimer, which should fire once after 100ms.
+  // Create a Quit timer which will fire after 1s.
+  // Verify that we fired exactly once.
+
+  mock_idle_time = GetTickCount();
+  TestIdleTask test_task(false);
+  TestFinishedTask finish_task;
+  MessageLoop* loop = MessageLoop::current();
+  Timer* t = loop->timer_manager()->StartTimer(1000, &finish_task, false);
+  test_task.Start();
+  loop->Run();
+
+  EXPECT_EQ(test_task.get_idle_counter(), 1);
+  delete t;
+}
+
+TEST(IdleTimerTest, NoRepeatFlipIdleOnce) {
+  // Create an IdleTimer, which should fire once after 100ms.
+  // Create a Quit timer which will fire after 1s.
+  // Create a timer to reset once, idle after 500ms.
+  // Verify that we fired exactly twice.
+
+  mock_idle_time = GetTickCount();
+  TestIdleTask test_task(false);
+  TestFinishedTask finish_task;
+  ResetIdleTask reset_task;
+  MessageLoop* loop = MessageLoop::current();
+  Timer* t1 = loop->timer_manager()->StartTimer(1000, &finish_task, false);
+  Timer* t2 = loop->timer_manager()->StartTimer(500, &reset_task, false);
+  test_task.Start();
+  loop->Run();
+
+  EXPECT_EQ(test_task.get_idle_counter(), 2);
+  delete t1;
+  delete t2;
+}
+
+TEST(IdleTimerTest, NoRepeatNotIdle) {
+  // Create an IdleTimer, which should fire once after 100ms.
+  // Create a Quit timer which will fire after 1s.
+  // Create a timer to reset idle every 50ms.
+  // Verify that we never fired.
+
+  mock_idle_time = GetTickCount();
+  TestIdleTask test_task(false);
+  TestFinishedTask finish_task;
+  ResetIdleTask reset_task;
+  MessageLoop* loop = MessageLoop::current();
+  Timer* t = loop->timer_manager()->StartTimer(1000, &finish_task, false);
+  Timer* reset_timer = loop->timer_manager()->StartTimer(50, &reset_task, true);
+  test_task.Start();
+  loop->Run();
+  loop->timer_manager()->StopTimer(reset_timer);
+
+  EXPECT_EQ(test_task.get_idle_counter(), 0);
+  delete t;
+  delete reset_timer;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Repeat tests:
+// A repeating idle timer will fire repeatedly on each interval, as long
+// as it has been idle.  So, if the machine remains idle, it will continue
+// firing over and over.
+
+TEST(IdleTimerTest, Repeat) {
+  // Create an IdleTimer, which should fire repeatedly after 100ms.
+  // Create a Quit timer which will fire after 1.05s.
+  // Verify that we fired 10 times.
+  mock_idle_time = GetTickCount();
+  TestIdleTask test_task(true);
+  TestFinishedTask finish_task;
+  MessageLoop* loop = MessageLoop::current();
+  Timer* t = loop->timer_manager()->StartTimer(1050, &finish_task, false);
+  test_task.Start();
+  loop->Run();
+
+  // In a perfect world, the idle_counter should be 10.  However,
+  // since timers aren't guaranteed to fire perfectly, this can
+  // be less.  Just expect more than 5 and no more than 10.
+  EXPECT_GT(test_task.get_idle_counter(), 5);
+  EXPECT_LE(test_task.get_idle_counter(), 10);
+  delete t;
+}
+
+TEST(IdleTimerTest, RepeatIdleReset) {
+  // Create an IdleTimer, which should fire repeatedly after 100ms.
+  // Create a Quit timer which will fire after 1s.
+  // Create a reset timer, which fires after 550ms
+  // Verify that we fired 9 times.
+  mock_idle_time = GetTickCount();
+  TestIdleTask test_task(true);
+  ResetIdleTask reset_task;
+  TestFinishedTask finish_task;
+  MessageLoop* loop = MessageLoop::current();
+  Timer* t1 = loop->timer_manager()->StartTimer(1000, &finish_task, false);
+  Timer* t2 = loop->timer_manager()->StartTimer(550, &reset_task, false);
+  test_task.Start();
+  loop->Run();
+
+  // In a perfect world, the idle_counter should be 9.  However,
+  // since timers aren't guaranteed to fire perfectly, this can
+  // be less.  Just expect more than 5 and no more than 9.
+  EXPECT_GT(test_task.get_idle_counter(), 5);
+  EXPECT_LE(test_task.get_idle_counter(), 9);
+  delete t1;
+  delete t2;
+}
+
+TEST(IdleTimerTest, RepeatNotIdle) {
+  // Create an IdleTimer, which should fire repeatedly after 100ms.
+  // Create a Quit timer which will fire after 1s.
+  // Create a timer to reset idle every 50ms.
+  // Verify that we never fired.
+
+  mock_idle_time = GetTickCount();
+  TestIdleTask test_task(true);
+  TestFinishedTask finish_task;
+  ResetIdleTask reset_task;
+  MessageLoop* loop = MessageLoop::current();
+  Timer* t1 = loop->timer_manager()->StartTimer(1000, &finish_task, false);
+  Timer* reset_timer = loop->timer_manager()->StartTimer(50, &reset_task, true);
+  test_task.Start();
+  loop->Run();
+  loop->timer_manager()->StopTimer(reset_timer);
+
+  EXPECT_EQ(test_task.get_idle_counter(), 0);
+  delete t1;
+  delete reset_timer;
+}
diff --git a/base/image_util.cc b/base/image_util.cc
new file mode 100644
index 0000000..172a595
--- /dev/null
+++ b/base/image_util.cc
@@ -0,0 +1,97 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#include <windows.h>
+#include <ImageHlp.h>
+#include <psapi.h>
+
+#include "base/image_util.h"
+#include "base/process_util.h"
+
+// imagehlp.dll appears to ship in all win versions after Win95.
+// nsylvain verified it is present in win2k.
+// Using #pragma comment for dependency, instead of LoadLibrary/GetProcAddress.
+#pragma comment(lib, "imagehlp.lib")
+
+namespace image_util {
+
+// ImageMetrics
+ImageMetrics::ImageMetrics(HANDLE process) : process_(process) {
+}
+
+ImageMetrics::~ImageMetrics() {
+}
+
+bool ImageMetrics::GetDllImageSectionData(const std::string& loaded_dll_name,
+                                          ImageSectionsData* section_sizes) {
+  // Get a handle to the loaded DLL
+  HMODULE the_dll = GetModuleHandleA(loaded_dll_name.c_str());
+  char full_filename[MAX_PATH];
+  // Get image path
+  if (GetModuleFileNameExA(process_, the_dll, full_filename, MAX_PATH)) {
+    return GetImageSectionSizes(full_filename, section_sizes);
+  }
+  return false;
+}
+
+bool ImageMetrics::GetProcessImageSectionData(ImageSectionsData*
+                                              section_sizes) {
+  char exe_path[MAX_PATH];
+  // Get image path
+  if (GetModuleFileNameExA(process_, NULL, exe_path, MAX_PATH)) {
+    return GetImageSectionSizes(exe_path, section_sizes);
+  }
+  return false;
+}
+
+// private
+bool ImageMetrics::GetImageSectionSizes(char* qualified_path,
+                                        ImageSectionsData* result) {
+  LOADED_IMAGE li;
+  // TODO (timsteele): There is no unicode version for MapAndLoad, hence
+  // why ansi functions are used in this class. Should we try and rewrite
+  // this call ourselves to be safe?
+  if (MapAndLoad(qualified_path, 0, &li, FALSE, TRUE)) {
+    IMAGE_SECTION_HEADER* section_header = li.Sections;
+    for (unsigned i = 0; i < li.NumberOfSections; i++, section_header++) {
+      std::string name(reinterpret_cast<char*>(section_header->Name));
+      ImageSectionData data(name, section_header->Misc.VirtualSize ?
+          section_header->Misc.VirtualSize :
+          section_header->SizeOfRawData);
+      // copy into result
+      result->push_back(data);
+    }
+  } else {
+    // map and load failed
+    return false;
+  }
+  UnMapAndLoad(&li);
+  return true;
+}
+
+} // namespace image_util
diff --git a/base/image_util.h b/base/image_util.h
new file mode 100644
index 0000000..094da08
--- /dev/null
+++ b/base/image_util.h
@@ -0,0 +1,88 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file/namespace contains utility functions for gathering
+// information about PE (Portable Executable) headers within
+// images (dll's / exe's )
+
+#ifndef BASE_IMAGE_UTIL_H__
+#define BASE_IMAGE_UTIL_H__
+
+#include <vector>
+#include "base/basictypes.h"
+#include <windows.h>
+
+namespace image_util {
+
+// Contains both the PE section name (.text, .reloc etc) and its size.
+struct ImageSectionData {
+  std::string name;
+  size_t size_in_bytes;
+  ImageSectionData (const std::string& section_name, size_t section_size) :
+      name (section_name), size_in_bytes(section_size) {
+  }
+};
+
+typedef std::vector<ImageSectionData> ImageSectionsData;
+
+// Provides image statistics for modules of a specified process, or for the
+// specified process' own executable file. To use, invoke CreateImageMetrics()
+// to get an instance for a specified process, then access the information via
+// methods.
+class ImageMetrics {
+  public:
+    // Creates an ImageMetrics instance for given process owned by
+    // the caller.
+    explicit ImageMetrics(HANDLE process);
+    ~ImageMetrics();
+
+    // Fills a vector of ImageSectionsData containing name/size info
+    // for every section found in the specified dll's PE section table.
+    // The DLL must be loaded by the process associated with this ImageMetrics
+    // instance.
+    bool GetDllImageSectionData(const std::string& loaded_dll_name,
+        ImageSectionsData* section_sizes);
+
+    // Fills a vector if ImageSectionsData containing name/size info
+    // for every section found in the executable file of the process
+    // associated with this ImageMetrics instance.
+    bool GetProcessImageSectionData(ImageSectionsData* section_sizes);
+
+  private:
+    // Helper for GetDllImageSectionData and GetProcessImageSectionData
+    bool GetImageSectionSizes(char* qualified_path, ImageSectionsData* result);
+
+    HANDLE process_;
+
+    DISALLOW_EVIL_CONSTRUCTORS(ImageMetrics);
+};
+
+} // namespace image_util
+
+#endif
\ No newline at end of file
diff --git a/base/json_reader.cc b/base/json_reader.cc
new file mode 100644
index 0000000..1ec5f63
--- /dev/null
+++ b/base/json_reader.cc
@@ -0,0 +1,605 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/json_reader.h"
+
+#include <float.h>
+
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/values.h"
+
+static const JSONReader::Token kInvalidToken(JSONReader::Token::INVALID_TOKEN,
+                                             0, 0);
+static const int kStackLimit = 100;
+
+namespace {
+
+inline int HexToInt(wchar_t c) {
+  if ('0' <= c && c <= '9') {
+    return c - '0';
+  } else if ('A' <= c && c <= 'F') {
+    return c - 'A' + 10;
+  } else if ('a' <= c && c <= 'f') {
+    return c - 'a' + 10;
+  }
+  NOTREACHED();
+  return 0;
+}
+
+// A helper method for ParseNumberToken.  It reads an int from the end of
+// token.  The method returns false if there is no valid integer at the end of
+// the token.
+bool ReadInt(JSONReader::Token& token, bool can_have_leading_zeros) {
+  wchar_t first = token.NextChar();
+  int len = 0;
+
+  // Read in more digits
+  wchar_t c = first;
+  while ('\0' != c && '0' <= c && c <= '9') {
+    ++token.length;
+    ++len;
+    c = token.NextChar();
+  }
+  // We need at least 1 digit.
+  if (len == 0)
+    return false;
+
+  if (!can_have_leading_zeros && len > 1 && '0' == first)
+    return false;
+
+  return true;
+}
+
+// A helper method for ParseStringToken.  It reads |digits| hex digits from the
+// token. If the sequence if digits is not valid (contains other characters),
+// the method returns false.
+bool ReadHexDigits(JSONReader::Token& token, int digits) {
+  for (int i = 1; i <= digits; ++i) {
+    wchar_t c = *(token.begin + token.length + i);
+    if ('\0' == c)
+      return false;
+    if (!(('0' <= c && c <= '9') || ('a' <= c && c <= 'f') ||
+          ('A' <= c && c <= 'F'))) {
+      return false;
+    }
+  }
+
+  token.length += digits;
+  return true;
+}
+
+}  // anonymous namespace
+
+/* static */
+bool JSONReader::Read(const std::string& json, Value** root) {
+  return JsonToValue(json, root, true);
+}
+
+/* static */
+bool JSONReader::JsonToValue(const std::string& json, Value** root,
+                             bool check_root) {
+  // Assume input is UTF8.  The conversion from UTF8 to wstring removes null
+  // bytes for us (a good thing).
+  std::wstring json_wide(UTF8ToWide(json));
+  const wchar_t* json_cstr = json_wide.c_str();
+
+  // When the input JSON string starts with a UTF-8 Byte-Order-Mark
+  // (0xEF, 0xBB, 0xBF), the UTF8ToWide() function converts it to a Unicode
+  // BOM (U+FEFF). To avoid the JSONReader::BuildValue() function from
+  // mis-treating a Unicode BOM as an invalid character and returning false,
+  // skip a converted Unicode BOM if it exists.
+  if (!json_wide.empty() && json_cstr[0] == 0xFEFF) {
+    ++json_cstr;
+  }
+
+  JSONReader reader(json_cstr);
+
+  Value* temp_root = NULL;
+  bool success = reader.BuildValue(&temp_root, check_root);
+
+  // Only modify root_ if we have valid JSON and nothing else.
+  if (success && reader.ParseToken().type == Token::END_OF_INPUT) {
+    *root = temp_root;
+    return true;
+  }
+
+  if (temp_root)
+    delete temp_root;
+  return false;
+}
+
+JSONReader::JSONReader(const wchar_t* json_start_pos)
+  : json_pos_(json_start_pos), stack_depth_(0) {}
+
+bool JSONReader::BuildValue(Value** node, bool is_root) {
+  ++stack_depth_;
+  if (stack_depth_ > kStackLimit)
+    return false;
+
+  Token token = ParseToken();
+  // The root token must be an array or an object.
+  if (is_root && token.type != Token::OBJECT_BEGIN &&
+      token.type != Token::ARRAY_BEGIN) {
+    return false;
+  }
+
+  switch (token.type) {
+    case Token::END_OF_INPUT:
+    case Token::INVALID_TOKEN:
+      return false;
+
+    case Token::NULL_TOKEN:
+      *node = Value::CreateNullValue();
+      break;
+
+    case Token::BOOL_TRUE:
+      *node = Value::CreateBooleanValue(true);
+      break;
+
+    case Token::BOOL_FALSE:
+      *node = Value::CreateBooleanValue(false);
+      break;
+
+    case Token::NUMBER:
+      if (!DecodeNumber(token, node))
+        return false;
+      break;
+
+    case Token::STRING:
+      if (!DecodeString(token, node))
+        return false;
+      break;
+
+    case Token::ARRAY_BEGIN:
+      {
+        json_pos_ += token.length;
+        token = ParseToken();
+
+        ListValue* array = new ListValue;
+        while (token.type != Token::ARRAY_END) {
+          Value* array_node = NULL;
+          if (!BuildValue(&array_node, false)) {
+            delete array;
+            return false;
+          }
+          array->Append(array_node);
+
+          // After a list value, we expect a comma or the end of the list.
+          token = ParseToken();
+          if (token.type == Token::LIST_SEPARATOR) {
+            json_pos_ += token.length;
+            token = ParseToken();
+            // Trailing commas are invalid
+            if (token.type == Token::ARRAY_END) {
+              delete array;
+              return false;
+            }
+          } else if (token.type != Token::ARRAY_END) {
+            // Unexpected value after list value.  Bail out.
+            delete array;
+            return false;
+          }
+        }
+        if (token.type != Token::ARRAY_END) {
+          delete array;
+          return false;
+        }
+        *node = array;
+        break;
+      }
+
+    case Token::OBJECT_BEGIN:
+      {
+        json_pos_ += token.length;
+        token = ParseToken();
+
+        DictionaryValue* dict = new DictionaryValue;
+        while (token.type != Token::OBJECT_END) {
+          if (token.type != Token::STRING) {
+            delete dict;
+            return false;
+          }
+          Value* dict_key_value = NULL;
+          if (!DecodeString(token, &dict_key_value)) {
+            delete dict;
+            return false;
+          }
+          // Convert the key into a wstring.
+          std::wstring dict_key;
+          bool success = dict_key_value->GetAsString(&dict_key);
+          DCHECK(success);
+          delete dict_key_value;
+
+          json_pos_ += token.length;
+          token = ParseToken();
+          if (token.type != Token::OBJECT_PAIR_SEPARATOR) {
+            delete dict;
+            return false;
+          }
+
+          json_pos_ += token.length;
+          token = ParseToken();
+          Value* dict_value = NULL;
+          if (!BuildValue(&dict_value, false)) {
+            delete dict;
+            return false;
+          }
+          dict->Set(dict_key, dict_value);
+
+          // After a key/value pair, we expect a comma or the end of the
+          // object.
+          token = ParseToken();
+          if (token.type == Token::LIST_SEPARATOR) {
+            json_pos_ += token.length;
+            token = ParseToken();
+            // Trailing commas are invalid.  TODO(tc): Should we allow trailing
+            // commas in objects?  Seems harmless and quite convenient...
+            if (token.type == Token::OBJECT_END) {
+              delete dict;
+              return false;
+            }
+          } else if (token.type != Token::OBJECT_END) {
+            // Unexpected value after last object value.  Bail out.
+            delete dict;
+            return false;
+          }
+        }
+        if (token.type != Token::OBJECT_END) {
+          delete dict;
+          return false;
+        }
+        *node = dict;
+        break;
+      }
+
+    default:
+      // We got a token that's not a value.
+      return false;
+  }
+  json_pos_ += token.length;
+
+  --stack_depth_;
+  return true;
+}
+
+JSONReader::Token JSONReader::ParseNumberToken() {
+  // We just grab the number here.  We validate the size in DecodeNumber.
+  // According   to RFC4627, a valid number is: [minus] int [frac] [exp]
+  Token token(Token::NUMBER, json_pos_, 0);
+  wchar_t c = *json_pos_;
+  if ('-' == c) {
+    ++token.length;
+    c = token.NextChar();
+  }
+
+  if (!ReadInt(token, false))
+    return kInvalidToken;
+
+  // Optional fraction part
+  c = token.NextChar();
+  if ('.' == c) {
+    ++token.length;
+    if (!ReadInt(token, true))
+      return kInvalidToken;
+    c = token.NextChar();
+  }
+
+  // Optional exponent part
+  if ('e' == c || 'E' == c) {
+    ++token.length;
+    c = token.NextChar();
+    if ('-' == c || '+' == c) {
+      ++token.length;
+      c = token.NextChar();
+    }
+    if (!ReadInt(token, true))
+      return kInvalidToken;
+  }
+
+  return token;
+}
+
+bool JSONReader::DecodeNumber(const Token& token, Value** node) {
+  // Determine if we want to try to parse as an int or a double.
+  bool is_double = false;
+  for (int i = 0; i < token.length; ++i) {
+    wchar_t c = *(token.begin + i);
+    if ('e' == c || 'E' == c || '.' == c) {
+      is_double = true;
+      break;
+    }
+  }
+
+  if (is_double) {
+    // Try parsing as a double.
+    double num_double;
+    int parsed_values = swscanf_s(token.begin, L"%lf", &num_double);
+    // Make sure we're not -INF, INF or NAN.
+    if (1 == parsed_values && _finite(num_double)) {
+      *node = Value::CreateRealValue(num_double);
+      return true;
+    }
+  } else {
+    int num_int;
+    int parsed_values = swscanf_s(token.begin, L"%d", &num_int);
+    if (1 == parsed_values) {
+      // Ensure the parsed value matches the string.  This makes sure we don't
+      // overflow/underflow.
+      const std::wstring& back_to_str = StringPrintf(L"%d", num_int);
+      if (0 == wcsncmp(back_to_str.c_str(), token.begin,
+                       back_to_str.length())) {
+        *node = Value::CreateIntegerValue(num_int);
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+JSONReader::Token JSONReader::ParseStringToken() {
+  Token token(Token::STRING, json_pos_, 1);
+  wchar_t c = token.NextChar();
+  while ('\0' != c) {
+    if ('\\' == c) {
+      ++token.length;
+      c = token.NextChar();
+      // Make sure the escaped char is valid.
+      switch (c) {
+        case 'x':
+          if (!ReadHexDigits(token, 2))
+            return kInvalidToken;
+          break;
+        case 'u':
+          if (!ReadHexDigits(token, 4))
+            return kInvalidToken;
+          break;
+        case '\\':
+        case '/':
+        case 'b':
+        case 'f':
+        case 'n':
+        case 'r':
+        case 't':
+        case '"':
+          break;
+        default:
+          return kInvalidToken;
+      }
+    } else if ('"' == c) {
+      ++token.length;
+      return token;
+    }
+    ++token.length;
+    c = token.NextChar();
+  }
+  return kInvalidToken;
+}
+
+bool JSONReader::DecodeString(const Token& token, Value** node) {
+  std::wstring decoded_str;
+  decoded_str.reserve(token.length - 2);
+
+  for (int i = 1; i < token.length - 1; ++i) {
+    wchar_t c = *(token.begin + i);
+    if ('\\' == c) {
+      ++i;
+      c = *(token.begin + i);
+      switch (c) {
+        case '"':
+        case '/':
+        case '\\':
+          decoded_str.push_back(c);
+          break;
+        case 'b':
+          decoded_str.push_back('\b');
+          break;
+        case 'f':
+          decoded_str.push_back('\f');
+          break;
+        case 'n':
+          decoded_str.push_back('\n');
+          break;
+        case 'r':
+          decoded_str.push_back('\r');
+          break;
+        case 't':
+          decoded_str.push_back('\t');
+          break;
+
+        case 'x':
+          decoded_str.push_back((HexToInt(*(token.begin + i + 1)) << 4) +
+                                HexToInt(*(token.begin + i + 2)));
+          i += 2;
+          break;
+        case 'u':
+          decoded_str.push_back((HexToInt(*(token.begin + i + 1)) << 12 ) +
+                                (HexToInt(*(token.begin + i + 2)) << 8) +
+                                (HexToInt(*(token.begin + i + 3)) << 4) +
+                                HexToInt(*(token.begin + i + 4)));
+          i += 4;
+          break;
+
+        default:
+          // We should only have valid strings at this point.  If not,
+          // ParseStringToken didn't do it's job.
+          NOTREACHED();
+          return false;
+      }
+    } else {
+      // Not escaped
+      decoded_str.push_back(c);
+    }
+  }
+  *node = Value::CreateStringValue(decoded_str);
+
+  return true;
+}
+
+JSONReader::Token JSONReader::ParseToken() {
+  static const std::wstring kNullString(L"null");
+  static const std::wstring kTrueString(L"true");
+  static const std::wstring kFalseString(L"false");
+
+  EatWhitespaceAndComments();
+
+  Token token(Token::INVALID_TOKEN, 0, 0);
+  switch (*json_pos_) {
+    case '\0':
+      token.type = Token::END_OF_INPUT;
+      break;
+
+    case 'n':
+      if (NextStringMatch(kNullString))
+        token = Token(Token::NULL_TOKEN, json_pos_, 4);
+      break;
+
+    case 't':
+      if (NextStringMatch(kTrueString))
+        token = Token(Token::BOOL_TRUE, json_pos_, 4);
+      break;
+
+    case 'f':
+      if (NextStringMatch(kFalseString))
+        token = Token(Token::BOOL_FALSE, json_pos_, 5);
+      break;
+
+    case '[':
+      token = Token(Token::ARRAY_BEGIN, json_pos_, 1);
+      break;
+
+    case ']':
+      token = Token(Token::ARRAY_END, json_pos_, 1);
+      break;
+
+    case ',':
+      token = Token(Token::LIST_SEPARATOR, json_pos_, 1);
+      break;
+
+    case '{':
+      token = Token(Token::OBJECT_BEGIN, json_pos_, 1);
+      break;
+
+    case '}':
+      token = Token(Token::OBJECT_END, json_pos_, 1);
+      break;
+
+    case ':':
+      token = Token(Token::OBJECT_PAIR_SEPARATOR, json_pos_, 1);
+      break;
+
+    case '0':
+    case '1':
+    case '2':
+    case '3':
+    case '4':
+    case '5':
+    case '6':
+    case '7':
+    case '8':
+    case '9':
+    case '-':
+      token = ParseNumberToken();
+      break;
+
+    case '"':
+      token = ParseStringToken();
+      break;
+  }
+  return token;
+}
+
+bool JSONReader::NextStringMatch(const std::wstring& str) {
+  for (size_t i = 0; i < str.length(); ++i) {
+    if ('\0' == *json_pos_)
+      return false;
+    if (*(json_pos_ + i) != str[i])
+      return false;
+  }
+  return true;
+}
+
+void JSONReader::EatWhitespaceAndComments() {
+  while ('\0' != *json_pos_) {
+    switch (*json_pos_) {
+      case ' ':
+      case '\n':
+      case '\r':
+      case '\t':
+        ++json_pos_;
+        break;
+      case '/':
+        // TODO(tc): This isn't in the RFC so it should be a parser flag.
+        if (!EatComment())
+          return;
+        break;
+      default:
+        // Not a whitespace char, just exit.
+        return;
+    }
+  }
+}
+
+bool JSONReader::EatComment() {
+  if ('/' != *json_pos_)
+    return false;
+
+  wchar_t next_char = *(json_pos_ + 1);
+  if ('/' == next_char) {
+    // Line comment, read until \n or \r
+    json_pos_ += 2;
+    while ('\0' != *json_pos_) {
+      switch (*json_pos_) {
+        case '\n':
+        case '\r':
+          ++json_pos_;
+          return true;
+        default:
+          ++json_pos_;
+      }
+    }
+  } else if ('*' == next_char) {
+    // Block comment, read until */
+    json_pos_ += 2;
+    while ('\0' != *json_pos_) {
+      switch (*json_pos_) {
+        case '*':
+          if ('/' == *(json_pos_ + 1)) {
+            json_pos_ += 2;
+            return true;
+          }
+        default:
+          ++json_pos_;
+      }
+    }
+  } else {
+    return false;
+  }
+  return true;
+}
diff --git a/base/json_reader.h b/base/json_reader.h
new file mode 100644
index 0000000..97f68fd
--- /dev/null
+++ b/base/json_reader.h
@@ -0,0 +1,165 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// A JSON parser.  Converts strings of JSON into a Value object (see
+// base/values.h).
+// http://www.ietf.org/rfc/rfc4627.txt?number=4627
+//
+// Known limitations/deviations from the RFC:
+// - Only knows how to parse ints within the range of a signed 32 bit int and
+//   decimal numbers within a double.
+// - Assumes input is encoded as UTF8.  The spec says we should allow UTF-16
+//   (BE or LE) and UTF-32 (BE or LE) as well.
+// - We limit nesting to 100 levels to prevent stack overflow (this is allowed
+//   by the RFC).
+// - A Unicode FAQ ("http://unicode.org/faq/utf_bom.html") writes a data
+//   stream may start with a Unicode Byte-Order-Mark (U+FEFF), i.e. the input
+//   UTF-8 string for the JSONReader::JsonToValue() function may start with a
+//   UTF-8 BOM (0xEF, 0xBB, 0xBF).
+//   To avoid the function from mis-treating a UTF-8 BOM as an invalid
+//   character, the function skips a Unicode BOM at the beginning of the
+//   Unicode string (converted from the input UTF-8 string) before parsing it.
+//
+// TODO(tc): It would be nice to give back an error string when we fail to parse JSON.
+// Parsing options:
+// - Relax trailing commas in arrays and objects
+// - Relax object keys being wrapped in double quotes
+// - Disable comment stripping
+
+#ifndef CHROME_COMMON_JSON_READER_H__
+#define CHROME_COMMON_JSON_READER_H__
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "testing/gtest/include/gtest/gtest_prod.h"
+
+class Value;
+
+class JSONReader {
+ public:
+  // A struct to hold a JS token.
+  class Token {
+   public:
+    enum Type {
+     OBJECT_BEGIN,           // {
+     OBJECT_END,             // }
+     ARRAY_BEGIN,            // [
+     ARRAY_END,              // ]
+     STRING,
+     NUMBER,
+     BOOL_TRUE,              // true
+     BOOL_FALSE,             // false
+     NULL_TOKEN,             // null
+     LIST_SEPARATOR,         // ,
+     OBJECT_PAIR_SEPARATOR,  // :
+     END_OF_INPUT,
+     INVALID_TOKEN,
+    };
+    Token(Type t, const wchar_t* b, int len)
+      : type(t), begin(b), length(len) {}
+
+    Type type;
+
+    // A pointer into JSONReader::json_pos_ that's the beginning of this token.
+    const wchar_t* begin;
+
+    // End should be one char past the end of the token.
+    int length;
+
+    // Get the character that's one past the end of this token.
+    wchar_t NextChar() {
+      return *(begin + length);
+    }
+  };
+
+  // Reads and parses |json| and populates |root|.  If |json| is not a
+  // properly formed JSON string, returns false and leaves root unaltered.
+  static bool Read(const std::string& json, Value** root);
+
+ private:
+  JSONReader(const wchar_t* json_start_pos);
+  DISALLOW_EVIL_CONSTRUCTORS(JSONReader);
+
+  FRIEND_TEST(JSONReaderTest, Reading);
+
+  // Pass through method from JSONReader::Read.  We have this so unittests can
+  // disable the root check.
+  static bool JsonToValue(const std::string& json, Value** root,
+                          bool check_root);
+
+  // Recursively build Value.  Returns false if we don't have a valid JSON
+  // string.  If |is_root| is true, we verify that the root element is either
+  // an object or an array.
+  bool BuildValue(Value** root, bool is_root);
+
+  // Parses a sequence of characters into a Token::NUMBER. If the sequence of
+  // characters is not a valid number, returns a Token::INVALID_TOKEN. Note
+  // that DecodeNumber is used to actually convert from a string to an
+  // int/double.
+  Token ParseNumberToken();
+
+  // Try and convert the substring that token holds into an int or a double. If
+  // we can (ie., no overflow), return true and create the appropriate value
+  // for |node|.  Return false if we can't do the conversion.
+  bool DecodeNumber(const Token& token, Value** node);
+
+  // Parses a sequence of characters into a Token::STRING. If the sequence of
+  // characters is not a valid string, returns a Token::INVALID_TOKEN. Note
+  // that DecodeString is used to actually decode the escaped string into an
+  // actual wstring.
+  Token ParseStringToken();
+
+  // Convert the substring into a value string.  This should always succeed
+  // (otherwise ParseStringToken would have failed), but returns a success bool
+  // just in case.
+  bool DecodeString(const Token& token, Value** node);
+
+  // Grabs the next token in the JSON stream.  This does not increment the
+  // stream so it can be used to look ahead at the next token.
+  Token ParseToken();
+
+  // Increments json_pos_ past leading whitespace and comments.
+  void EatWhitespaceAndComments();
+
+  // If json_pos_ is at the start of a comment, eat it, otherwise, returns
+  // false.
+  bool EatComment();
+
+  // Checks if json_pos_ matches str.
+  bool NextStringMatch(const std::wstring& str);
+
+  // Pointer to the current position in the input string.
+  const wchar_t* json_pos_;
+
+  // Used to keep track of how many nested lists/dicts there are.
+  int stack_depth_;
+};
+
+#endif  // CHROME_COMMON_JSON_READER_H__
diff --git a/base/json_reader_unittest.cc b/base/json_reader_unittest.cc
new file mode 100644
index 0000000..f218531
--- /dev/null
+++ b/base/json_reader_unittest.cc
@@ -0,0 +1,412 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "base/json_reader.h"
+#include "base/values.h"
+
+TEST(JSONReaderTest, Reading) {
+  // some whitespace checking
+  Value* root = NULL;
+  ASSERT_TRUE(JSONReader::JsonToValue("   null   ", &root, false));
+  ASSERT_TRUE(root);
+  ASSERT_TRUE(root->IsType(Value::TYPE_NULL));
+  delete root;
+
+  // Invalid JSON string
+  root = NULL;
+  ASSERT_FALSE(JSONReader::JsonToValue("nu", &root, false));
+  ASSERT_FALSE(root);
+
+  // Simple bool
+  root = NULL;
+  ASSERT_TRUE(JSONReader::JsonToValue("true  ", &root, false));
+  ASSERT_TRUE(root);
+  ASSERT_TRUE(root->IsType(Value::TYPE_BOOLEAN));
+  delete root;
+
+  // Test number formats
+  root = NULL;
+  ASSERT_TRUE(JSONReader::JsonToValue("43", &root, false));
+  ASSERT_TRUE(root);
+  ASSERT_TRUE(root->IsType(Value::TYPE_INTEGER));
+  int int_val = 0;
+  ASSERT_TRUE(root->GetAsInteger(&int_val));
+  ASSERT_EQ(43, int_val);
+  delete root;
+
+  // According to RFC4627, oct, hex, and leading zeros are invalid JSON.
+  root = NULL;
+  ASSERT_FALSE(JSONReader::JsonToValue("043", &root, false));
+  ASSERT_FALSE(root);
+  root = NULL;
+  ASSERT_FALSE(JSONReader::JsonToValue("0x43", &root, false));
+  ASSERT_FALSE(root);
+  root = NULL;
+  ASSERT_FALSE(JSONReader::JsonToValue("00", &root, false));
+  ASSERT_FALSE(root);
+
+  // Test 0 (which needs to be special cased because of the leading zero
+  // clause).
+  root = NULL;
+  ASSERT_TRUE(JSONReader::JsonToValue("0", &root, false));
+  ASSERT_TRUE(root);
+  ASSERT_TRUE(root->IsType(Value::TYPE_INTEGER));
+  int_val = 1;
+  ASSERT_TRUE(root->GetAsInteger(&int_val));
+  ASSERT_EQ(0, int_val);
+  delete root;
+
+  // Numbers that overflow ints should fail
+  root = NULL;
+  ASSERT_FALSE(JSONReader::JsonToValue("2147483648", &root, false));
+  ASSERT_FALSE(root);
+  root = NULL;
+  ASSERT_FALSE(JSONReader::JsonToValue("-2147483649", &root, false));
+  ASSERT_FALSE(root);
+
+  // Parse a double
+  root = NULL;
+  ASSERT_TRUE(JSONReader::JsonToValue("43.1", &root, false));
+  ASSERT_TRUE(root);
+  ASSERT_TRUE(root->IsType(Value::TYPE_REAL));
+  double real_val = 0.0;
+  ASSERT_TRUE(root->GetAsReal(&real_val));
+  ASSERT_DOUBLE_EQ(43.1, real_val);
+  delete root;
+
+  root = NULL;
+  ASSERT_TRUE(JSONReader::JsonToValue("4.3e-1", &root, false));
+  ASSERT_TRUE(root);
+  ASSERT_TRUE(root->IsType(Value::TYPE_REAL));
+  real_val = 0.0;
+  ASSERT_TRUE(root->GetAsReal(&real_val));
+  ASSERT_DOUBLE_EQ(.43, real_val);
+  delete root;
+
+  root = NULL;
+  ASSERT_TRUE(JSONReader::JsonToValue("2.1e0", &root, false));
+  ASSERT_TRUE(root);
+  ASSERT_TRUE(root->IsType(Value::TYPE_REAL));
+  real_val = 0.0;
+  ASSERT_TRUE(root->GetAsReal(&real_val));
+  ASSERT_DOUBLE_EQ(2.1, real_val);
+  delete root;
+
+  root = NULL;
+  ASSERT_TRUE(JSONReader::JsonToValue("2.1e+0001", &root, false));
+  ASSERT_TRUE(root);
+  ASSERT_TRUE(root->IsType(Value::TYPE_REAL));
+  real_val = 0.0;
+  ASSERT_TRUE(root->GetAsReal(&real_val));
+  ASSERT_DOUBLE_EQ(21.0, real_val);
+  delete root;
+
+  root = NULL;
+  ASSERT_TRUE(JSONReader::JsonToValue("0.01", &root, false));
+  ASSERT_TRUE(root);
+  ASSERT_TRUE(root->IsType(Value::TYPE_REAL));
+  real_val = 0.0;
+  ASSERT_TRUE(root->GetAsReal(&real_val));
+  ASSERT_DOUBLE_EQ(0.01, real_val);
+  delete root;
+
+  root = NULL;
+  ASSERT_TRUE(JSONReader::JsonToValue("1.00", &root, false));
+  ASSERT_TRUE(root);
+  ASSERT_TRUE(root->IsType(Value::TYPE_REAL));
+  real_val = 0.0;
+  ASSERT_TRUE(root->GetAsReal(&real_val));
+  ASSERT_DOUBLE_EQ(1.0, real_val);
+  delete root;
+
+  // Fractional parts must have a digit before and after the decimal point.
+  root = NULL;
+  ASSERT_FALSE(JSONReader::JsonToValue("1.", &root, false));
+  ASSERT_FALSE(root);
+  root = NULL;
+  ASSERT_FALSE(JSONReader::JsonToValue(".1", &root, false));
+  ASSERT_FALSE(root);
+  root = NULL;
+  ASSERT_FALSE(JSONReader::JsonToValue("1.e10", &root, false));
+  ASSERT_FALSE(root);
+
+  // Exponent must have a digit following the 'e'.
+  root = NULL;
+  ASSERT_FALSE(JSONReader::JsonToValue("1e", &root, false));
+  ASSERT_FALSE(root);
+  root = NULL;
+  ASSERT_FALSE(JSONReader::JsonToValue("1E", &root, false));
+  ASSERT_FALSE(root);
+  root = NULL;
+  ASSERT_FALSE(JSONReader::JsonToValue("1e1.", &root, false));
+  ASSERT_FALSE(root);
+  root = NULL;
+  ASSERT_FALSE(JSONReader::JsonToValue("1e1.0", &root, false));
+  ASSERT_FALSE(root);
+
+  // INF/-INF/NaN are not valid
+  root = NULL;
+  ASSERT_FALSE(JSONReader::JsonToValue("1e1000", &root, false));
+  ASSERT_FALSE(root);
+  root = NULL;
+  ASSERT_FALSE(JSONReader::JsonToValue("-1e1000", &root, false));
+  ASSERT_FALSE(root);
+  root = NULL;
+  ASSERT_FALSE(JSONReader::JsonToValue("NaN", &root, false));
+  ASSERT_FALSE(root);
+
+  // Invalid number formats
+  root = NULL;
+  ASSERT_FALSE(JSONReader::JsonToValue("4.3.1", &root, false));
+  ASSERT_FALSE(root);
+  root = NULL;
+  ASSERT_FALSE(JSONReader::JsonToValue("4e3.1", &root, false));
+  ASSERT_FALSE(root);
+
+  // Test string parser
+  root = NULL;
+  ASSERT_TRUE(JSONReader::JsonToValue("\"hello world\"", &root, false));
+  ASSERT_TRUE(root);
+  ASSERT_TRUE(root->IsType(Value::TYPE_STRING));
+  std::wstring str_val;
+  ASSERT_TRUE(root->GetAsString(&str_val));
+  ASSERT_EQ(L"hello world", str_val);
+  delete root;
+
+  // Empty string
+  root = NULL;
+  ASSERT_TRUE(JSONReader::JsonToValue("\"\"", &root, false));
+  ASSERT_TRUE(root);
+  ASSERT_TRUE(root->IsType(Value::TYPE_STRING));
+  str_val.clear();
+  ASSERT_TRUE(root->GetAsString(&str_val));
+  ASSERT_EQ(L"", str_val);
+  delete root;
+
+  // Test basic string escapes
+  root = NULL;
+  ASSERT_TRUE(JSONReader::JsonToValue("\" \\\"\\\\\\/\\b\\f\\n\\r\\t\"", &root,
+                                      false));
+  ASSERT_TRUE(root);
+  ASSERT_TRUE(root->IsType(Value::TYPE_STRING));
+  str_val.clear();
+  ASSERT_TRUE(root->GetAsString(&str_val));
+  ASSERT_EQ(L" \"\\/\b\f\n\r\t", str_val);
+  delete root;
+
+  // Test hex and unicode escapes including the null character.
+  root = NULL;
+  ASSERT_TRUE(JSONReader::JsonToValue("\"\\x41\\x00\\u1234\"", &root, false));
+  ASSERT_TRUE(root);
+  ASSERT_TRUE(root->IsType(Value::TYPE_STRING));
+  str_val.clear();
+  ASSERT_TRUE(root->GetAsString(&str_val));
+  ASSERT_EQ(std::wstring(L"A\0\x1234", 3), str_val);
+  delete root;
+
+  // Test invalid strings
+  root = NULL;
+  ASSERT_FALSE(JSONReader::JsonToValue("\"no closing quote", &root, false));
+  ASSERT_FALSE(root);
+  root = NULL;
+  ASSERT_FALSE(JSONReader::JsonToValue("\"\\z invalid escape char\"", &root,
+                                       false));
+  ASSERT_FALSE(root);
+  root = NULL;
+  ASSERT_FALSE(JSONReader::JsonToValue("\"\\xAQ invalid hex code\"", &root,
+                                       false));
+  ASSERT_FALSE(root);
+  root = NULL;
+  ASSERT_FALSE(JSONReader::JsonToValue("not enough hex chars\\x1\"", &root,
+                                       false));
+  ASSERT_FALSE(root);
+  root = NULL;
+  ASSERT_FALSE(JSONReader::JsonToValue("\"not enough escape chars\\u123\"",
+                                       &root, false));
+  ASSERT_FALSE(root);
+  root = NULL;
+  ASSERT_FALSE(JSONReader::JsonToValue("\"extra backslash at end of input\\\"",
+                                       &root, false));
+  ASSERT_FALSE(root);
+
+  // Basic array
+  root = NULL;
+  ASSERT_TRUE(JSONReader::Read("[true, false, null]", &root));
+  ASSERT_TRUE(root);
+  ASSERT_TRUE(root->IsType(Value::TYPE_LIST));
+  ListValue* list = static_cast<ListValue*>(root);
+  ASSERT_EQ(3, list->GetSize());
+  delete root;
+
+  // Empty array
+  root = NULL;
+  ASSERT_TRUE(JSONReader::Read("[]", &root));
+  ASSERT_TRUE(root);
+  ASSERT_TRUE(root->IsType(Value::TYPE_LIST));
+  list = static_cast<ListValue*>(root);
+  ASSERT_EQ(0, list->GetSize());
+  delete root;
+
+  // Nested arrays
+  root = NULL;
+  ASSERT_TRUE(JSONReader::Read("[[true], [], [false, [], [null]], null]", &root));
+  ASSERT_TRUE(root);
+  ASSERT_TRUE(root->IsType(Value::TYPE_LIST));
+  list = static_cast<ListValue*>(root);
+  ASSERT_EQ(4, list->GetSize());
+  delete root;
+
+  // Invalid, missing close brace.
+  root = NULL;
+  ASSERT_FALSE(JSONReader::Read("[[true], [], [false, [], [null]], null", &root));
+  ASSERT_FALSE(root);
+
+  // Invalid, too many commas
+  root = NULL;
+  ASSERT_FALSE(JSONReader::Read("[true,, null]", &root));
+  ASSERT_FALSE(root);
+
+  // Invalid, no commas
+  root = NULL;
+  ASSERT_FALSE(JSONReader::Read("[true null]", &root));
+  ASSERT_FALSE(root);
+
+  // Invalid, trailing comma
+  root = NULL;
+  ASSERT_FALSE(JSONReader::Read("[true,]", &root));
+  ASSERT_FALSE(root);
+
+  // Test objects
+  root = NULL;
+  ASSERT_TRUE(JSONReader::Read("{}", &root));
+  ASSERT_TRUE(root);
+  ASSERT_TRUE(root->IsType(Value::TYPE_DICTIONARY));
+  delete root;
+
+  root = NULL;
+  ASSERT_TRUE(JSONReader::Read(
+    "{\"number\":9.87654321, \"null\":null , \"\\x53\" : \"str\" }", &root));
+  ASSERT_TRUE(root);
+  ASSERT_TRUE(root->IsType(Value::TYPE_DICTIONARY));
+  DictionaryValue* dict_val = static_cast<DictionaryValue*>(root);
+  real_val = 0.0;
+  ASSERT_TRUE(dict_val->GetReal(L"number", &real_val));
+  ASSERT_DOUBLE_EQ(9.87654321, real_val);
+  Value* null_val = NULL;
+  ASSERT_TRUE(dict_val->Get(L"null", &null_val));
+  ASSERT_TRUE(null_val->IsType(Value::TYPE_NULL));
+  str_val.clear();
+  ASSERT_TRUE(dict_val->GetString(L"S", &str_val));
+  ASSERT_EQ(L"str", str_val);
+  delete root;
+
+  // Test nesting
+  root = NULL;
+  ASSERT_TRUE(JSONReader::Read(
+    "{\"inner\":{\"array\":[true]},\"false\":false,\"d\":{}}", &root));
+  ASSERT_TRUE(root);
+  ASSERT_TRUE(root->IsType(Value::TYPE_DICTIONARY));
+  dict_val = static_cast<DictionaryValue*>(root);
+  DictionaryValue* inner_dict = NULL;
+  ASSERT_TRUE(dict_val->GetDictionary(L"inner", &inner_dict));
+  ListValue* inner_array = NULL;
+  ASSERT_TRUE(inner_dict->GetList(L"array", &inner_array));
+  ASSERT_EQ(1, inner_array->GetSize());
+  bool bool_value = true;
+  ASSERT_TRUE(dict_val->GetBoolean(L"false", &bool_value));
+  ASSERT_FALSE(bool_value);
+  inner_dict = NULL;
+  ASSERT_TRUE(dict_val->GetDictionary(L"d", &inner_dict));
+  delete root;
+
+  // Invalid, no closing brace
+  root = NULL;
+  ASSERT_FALSE(JSONReader::Read("{\"a\": true", &root));
+  ASSERT_FALSE(root);
+
+  // Invalid, keys must be quoted
+  root = NULL;
+  ASSERT_FALSE(JSONReader::Read("{foo:true}", &root));
+  ASSERT_FALSE(root);
+
+  // Invalid, trailing comma
+  root = NULL;
+  ASSERT_FALSE(JSONReader::Read("{\"a\":true,}", &root));
+  ASSERT_FALSE(root);
+
+  // Invalid, too many commas
+  root = NULL;
+  ASSERT_FALSE(JSONReader::Read("{\"a\":true,,\"b\":false}", &root));
+  ASSERT_FALSE(root);
+
+  // Invalid, no separator
+  root = NULL;
+  ASSERT_FALSE(JSONReader::Read("{\"a\" \"b\"}", &root));
+  ASSERT_FALSE(root);
+
+  // Test stack overflow
+  root = NULL;
+  std::string evil(1000000, '[');
+  evil.append(std::string(1000000, ']'));
+  ASSERT_FALSE(JSONReader::Read(evil, &root));
+  ASSERT_FALSE(root);
+
+  // A few thousand adjacent lists is fine.
+  std::string not_evil("[");
+  not_evil.reserve(15010);
+  for (int i = 0; i < 5000; ++i) {
+    not_evil.append("[],");
+  }
+  not_evil.append("[]]");
+  ASSERT_TRUE(JSONReader::Read(not_evil, &root));
+  ASSERT_TRUE(root);
+  ASSERT_TRUE(root->IsType(Value::TYPE_LIST));
+  list = static_cast<ListValue*>(root);
+  ASSERT_EQ(5001, list->GetSize());
+  delete root;
+
+  // Test utf8 encoded input
+  root = NULL;
+  ASSERT_TRUE(JSONReader::JsonToValue("\"\xe7\xbd\x91\xe9\xa1\xb5\"", &root,
+                                      false));
+  ASSERT_TRUE(root);
+  ASSERT_TRUE(root->IsType(Value::TYPE_STRING));
+  str_val.clear();
+  ASSERT_TRUE(root->GetAsString(&str_val));
+  ASSERT_EQ(L"\x7f51\x9875", str_val);
+  delete root;
+
+  // Test invalid root objects.
+  root = NULL;
+  ASSERT_FALSE(JSONReader::Read("null", &root));
+  ASSERT_FALSE(JSONReader::Read("true", &root));
+  ASSERT_FALSE(JSONReader::Read("10", &root));
+  ASSERT_FALSE(JSONReader::Read("\"root\"", &root));
+}
diff --git a/base/json_writer.cc b/base/json_writer.cc
new file mode 100644
index 0000000..512e0a1
--- /dev/null
+++ b/base/json_writer.cc
@@ -0,0 +1,191 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/json_writer.h"
+
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/values.h"
+#include "base/string_escape.h"
+
+const char kPrettyPrintLineEnding[] = "\r\n";
+
+/* static */
+void JSONWriter::Write(const Value* const node, bool pretty_print,
+                       std::string* json) {
+  json->clear();
+  // Is there a better way to estimate the size of the output?
+  json->reserve(1024);
+  JSONWriter writer(pretty_print, json);
+  writer.BuildJSONString(node, 0);
+  if (pretty_print)
+    json->append(kPrettyPrintLineEnding);
+}
+
+JSONWriter::JSONWriter(bool pretty_print, std::string* json)
+  : pretty_print_(pretty_print),
+    json_string_(json) {
+  DCHECK(json);
+}
+
+void JSONWriter::BuildJSONString(const Value* const node, int depth) {
+  switch(node->GetType()) {
+    case Value::TYPE_NULL:
+      json_string_->append("null");
+      break;
+
+    case Value::TYPE_BOOLEAN:
+      {
+        bool value;
+        bool result = node->GetAsBoolean(&value);
+        DCHECK(result);
+        json_string_->append(value ? "true" : "false");
+        break;
+      }
+
+    case Value::TYPE_INTEGER:
+      {
+        int value;
+        bool result = node->GetAsInteger(&value);
+        DCHECK(result);
+        StringAppendF(json_string_, "%d", value);
+        break;
+      }
+
+    case Value::TYPE_REAL:
+      {
+        double value;
+        bool result = node->GetAsReal(&value);
+        DCHECK(result);
+        std::string real = StringPrintf("%g", value);
+        // Ensure that the number has a .0 if there's no decimal or 'e'.  This
+        // makes sure that when we read the JSON back, it's interpreted as a
+        // real rather than an int.
+        if (real.find('.') == std::string::npos &&
+            real.find('e') == std::string::npos &&
+            real.find('E') == std::string::npos) {
+          real.append(".0");
+        }
+        json_string_->append(real);
+        break;
+      }
+
+    case Value::TYPE_STRING:
+      {
+        std::wstring value;
+        bool result = node->GetAsString(&value);
+        DCHECK(result);
+        AppendQuotedString(value);
+        break;
+      }
+
+    case Value::TYPE_LIST:
+      {
+        json_string_->append("[");
+        if (pretty_print_)
+          json_string_->append(" ");
+
+        const ListValue* list = static_cast<const ListValue*>(node);
+        for (size_t i = 0; i < list->GetSize(); ++i) {
+          if (i != 0) {
+            json_string_->append(",");
+            if (pretty_print_)
+              json_string_->append(" ");
+          }
+
+          Value* value = NULL;
+          bool result = list->Get(i, &value);
+          DCHECK(result);
+          BuildJSONString(value, depth);
+        }
+
+        if (pretty_print_)
+          json_string_->append(" ");
+        json_string_->append("]");
+        break;
+      }
+
+    case Value::TYPE_DICTIONARY:
+      {
+        json_string_->append("{");
+        if (pretty_print_)
+          json_string_->append(kPrettyPrintLineEnding);
+
+        const DictionaryValue* dict =
+          static_cast<const DictionaryValue*>(node);
+        for (DictionaryValue::key_iterator key_itr = dict->begin_keys();
+             key_itr != dict->end_keys();
+             ++key_itr) {
+
+          if (key_itr != dict->begin_keys()) {
+            json_string_->append(",");
+            if (pretty_print_)
+              json_string_->append(kPrettyPrintLineEnding);
+          }
+
+          Value* value = NULL;
+          bool result = dict->Get(*key_itr, &value);
+          DCHECK(result);
+
+          if (pretty_print_)
+            IndentLine(depth + 1);
+          AppendQuotedString(*key_itr);
+          if (pretty_print_) {
+            json_string_->append(": ");
+          } else {
+            json_string_->append(":");
+          }
+          BuildJSONString(value, depth + 1);
+        }
+
+        if (pretty_print_) {
+          json_string_->append(kPrettyPrintLineEnding);
+          IndentLine(depth);
+          json_string_->append("}");
+        } else {
+          json_string_->append("}");
+        }
+        break;
+      }
+
+    default:
+      // TODO(jhughes): handle TYPE_BINARY
+      NOTREACHED() << "unknown json type";
+  }
+}
+
+void JSONWriter::AppendQuotedString(const std::wstring& str) {
+  string_escape::JavascriptDoubleQuote(str, true, json_string_);
+}
+
+void JSONWriter::IndentLine(int depth) {
+  // It may be faster to keep an indent string so we don't have to keep
+  // reallocating.
+  json_string_->append(std::string(depth * 3, ' '));
+}
diff --git a/base/json_writer.h b/base/json_writer.h
new file mode 100644
index 0000000..9f8f8d3
--- /dev/null
+++ b/base/json_writer.h
@@ -0,0 +1,71 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_COMMON_JSON_WRITER_H__
+#define CHROME_COMMON_JSON_WRITER_H__
+
+#include <string>
+
+#include "base/basictypes.h"
+
+class Value;
+
+class JSONWriter {
+ public:
+  // Given a root node, generates a JSON string and puts it into |json|.
+  // If |pretty_print| is true, return a slightly nicer formated json string
+  // (pads with whitespace to help readability).  If |pretty_print| is false,
+  // we try to generate as compact a string as possible.
+  // TODO(tc): Should we generate json if it would be invalid json (e.g.,
+  // |node| is not a DictionaryValue/ListValue or if there are inf/-inf float
+  // values)?
+  static void Write(const Value* const node, bool pretty_print,
+                    std::string* json);
+
+ private:
+  JSONWriter(bool pretty_print, std::string* json);
+  DISALLOW_EVIL_CONSTRUCTORS(JSONWriter);
+
+  // Called recursively to build the JSON string.  Whe completed, value is
+  // json_string_ will contain the JSON.
+  void BuildJSONString(const Value* const node, int depth);
+
+  // Appends a quoted, escaped, version of str to json_string_.
+  void AppendQuotedString(const std::wstring& str);
+
+  // Adds space to json_string_ for the indent level.
+  void IndentLine(int depth);
+
+  // Where we write JSON data as we generate it.
+  std::string* json_string_;
+
+  bool pretty_print_;
+};
+
+#endif  // CHROME_COMMON_JSON_WRITER_H__
diff --git a/base/json_writer_unittest.cc b/base/json_writer_unittest.cc
new file mode 100644
index 0000000..4cbe2d0
--- /dev/null
+++ b/base/json_writer_unittest.cc
@@ -0,0 +1,82 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "base/json_writer.h"
+#include "base/values.h"
+
+TEST(JSONWriterTest, Writing) {
+  // Test null
+  Value* root = Value::CreateNullValue();
+  std::string output_js;
+  JSONWriter::Write(root, false, &output_js);
+  ASSERT_EQ("null", output_js);
+  delete root;
+
+  // Test empty dict
+  root = new DictionaryValue;
+  JSONWriter::Write(root, false, &output_js);
+  ASSERT_EQ("{}", output_js);
+  delete root;
+
+  // Test empty list
+  root = new ListValue;
+  JSONWriter::Write(root, false, &output_js);
+  ASSERT_EQ("[]", output_js);
+  delete root;
+
+  // Test Real values should always have a decimal or an 'e'.
+  root = Value::CreateRealValue(1.0);
+  JSONWriter::Write(root, false, &output_js);
+  ASSERT_EQ("1.0", output_js);
+  delete root;
+
+  // Writer unittests like empty list/dict nesting,
+  // list list nesting, etc.
+  DictionaryValue root_dict;
+  ListValue* list = new ListValue;
+  root_dict.Set(L"list", list);
+  DictionaryValue* inner_dict = new DictionaryValue;
+  list->Append(inner_dict);
+  inner_dict->SetInteger(L"inner int", 10);
+  ListValue* inner_list = new ListValue;
+  list->Append(inner_list);
+  list->Append(Value::CreateBooleanValue(true));
+
+  JSONWriter::Write(&root_dict, false, &output_js);
+  ASSERT_EQ("{\"list\":[{\"inner int\":10},[],true]}", output_js);
+  JSONWriter::Write(&root_dict, true, &output_js);
+  ASSERT_EQ("{\r\n"
+            "   \"list\": [ {\r\n"
+            "      \"inner int\": 10\r\n"
+            "   }, [  ], true ]\r\n"
+            "}\r\n",
+            output_js);
+}
+
diff --git a/base/linked_ptr.h b/base/linked_ptr.h
new file mode 100644
index 0000000..9204d54
--- /dev/null
+++ b/base/linked_ptr.h
@@ -0,0 +1,200 @@
+// linked_ptr.h
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// A "smart" pointer type with reference tracking.  Every pointer to a
+// particular object is kept on a circular linked list.  When the last pointer
+// to an object is destroyed or reassigned, the object is deleted.
+//
+// Used properly, this deletes the object when the last reference goes away.
+// There are several caveats:
+// - Like all reference counting schemes, cycles lead to leaks.
+// - Each smart pointer is actually two pointers (8 bytes instead of 4).
+// - Every time a pointer is released, the entire list of pointers to that
+//   object is traversed.  This class is therefore NOT SUITABLE when there
+//   will often be more than two or three pointers to a particular object.
+// - References are only tracked as long as linked_ptr<> objects are copied.
+//   If a linked_ptr<> is converted to a raw pointer and back, BAD THINGS
+//   will happen (double deletion).
+//
+// A good use of this class is storing object references in STL containers.
+// You can safely put linked_ptr<> in a vector<>.
+// Other uses may not be as good.
+//
+// Note: If you use an incomplete type with linked_ptr<>, the class
+// *containing* linked_ptr<> must have a constructor and destructor (even
+// if they do nothing!).
+//
+// Thread Safety:
+//   A linked_ptr is NOT thread safe. Copying a linked_ptr object is
+//   effectively a read-write operation.
+//
+// Alternative: to linked_ptr is shared_ptr, which
+//  - is also two pointers in size (8 bytes for 32 bit addresses)
+//  - is thread safe for copying and deletion
+//  - supports weak_ptrs
+
+#ifndef BASE_LINKED_PTR_H_
+#define BASE_LINKED_PTR_H_
+
+#include "base/logging.h"  // for CHECK macros
+
+// This is used internally by all instances of linked_ptr<>.  It needs to be
+// a non-template class because different types of linked_ptr<> can refer to
+// the same object (linked_ptr<Superclass>(obj) vs linked_ptr<Subclass>(obj)).
+// So, it needs to be possible for different types of linked_ptr to participate
+// in the same circular linked list, so we need a single class type here.
+//
+// DO NOT USE THIS CLASS DIRECTLY YOURSELF.  Use linked_ptr<T>.
+class linked_ptr_internal {
+ public:
+  // Create a new circle that includes only this instance.
+  void join_new() {
+    next_ = this;
+  }
+
+  // Join an existing circle.
+  void join(linked_ptr_internal const* ptr) {
+    next_ = ptr->next_;
+    ptr->next_ = this;
+  }
+
+  // Leave whatever circle we're part of.  Returns true iff we were the
+  // last member of the circle.  Once this is done, you can join() another.
+  bool depart() {
+    if (next_ == this) return true;
+    linked_ptr_internal const* p = next_;
+    while (p->next_ != this) p = p->next_;
+    p->next_ = next_;
+    return false;
+  }
+
+ private:
+  mutable linked_ptr_internal const* next_;
+};
+
+template <typename T>
+class linked_ptr {
+ public:
+  typedef T element_type;
+
+  // Take over ownership of a raw pointer.  This should happen as soon as
+  // possible after the object is created.
+  explicit linked_ptr(T* ptr = NULL) { capture(ptr); }
+  ~linked_ptr() { depart(); }
+
+  // Copy an existing linked_ptr<>, adding ourselves to the list of references.
+  template <typename U> linked_ptr(linked_ptr<U> const& ptr) { copy(&ptr); }
+  linked_ptr(linked_ptr const& ptr) { DCHECK_NE(&ptr, this); copy(&ptr); }
+
+  // Assignment releases the old value and acquires the new.
+  template <typename U> linked_ptr& operator=(linked_ptr<U> const& ptr) {
+    depart();
+    copy(&ptr);
+    return *this;
+  }
+
+  linked_ptr& operator=(linked_ptr const& ptr) {
+    if (&ptr != this) {
+      depart();
+      copy(&ptr);
+    }
+    return *this;
+  }
+
+  // Smart pointer members.
+  void reset(T* ptr = NULL) { depart(); capture(ptr); }
+  T* get() const { return value_; }
+  T* operator->() const { return value_; }
+  T& operator*() const { return *value_; }
+  // Release ownership of the pointed object and returns it.
+  // Sole ownership by this linked_ptr object is required.
+  T* release() {
+    bool last = link_.depart();
+    CHECK(last);
+    T* v = value_;
+    value_ = NULL;
+    return v;
+  }
+
+  bool operator==(T* p) const { return value_ == p; }
+  bool operator!=(T* p) const { return value_ != p; }
+  template <typename U>
+  bool operator==(linked_ptr<U> const& ptr) const {
+    return value_ == ptr.get();
+  }
+  template <typename U>
+  bool operator!=(linked_ptr<U> const& ptr) const {
+    return value_ != ptr.get();
+  }
+
+ private:
+  template <typename U>
+  friend class linked_ptr;
+
+  T* value_;
+  linked_ptr_internal link_;
+
+  void depart() {
+    if (link_.depart()) delete value_;
+  }
+
+  void capture(T* ptr) {
+    value_ = ptr;
+    link_.join_new();
+  }
+
+  template <typename U> void copy(linked_ptr<U> const* ptr) {
+    value_ = ptr->get();
+    if (value_)
+      link_.join(&ptr->link_);
+    else
+      link_.join_new();
+  }
+};
+
+template<typename T> inline
+bool operator==(T* ptr, const linked_ptr<T>& x) {
+  return ptr == x.get();
+}
+
+template<typename T> inline
+bool operator!=(T* ptr, const linked_ptr<T>& x) {
+  return ptr != x.get();
+}
+
+// A function to convert T* into linked_ptr<T>
+// Doing e.g. make_linked_ptr(new FooBarBaz<type>(arg)) is a shorter notation
+// for linked_ptr<FooBarBaz<type> >(new FooBarBaz<type>(arg))
+template <typename T>
+linked_ptr<T> make_linked_ptr(T* ptr) {
+  return linked_ptr<T>(ptr);
+}
+
+#endif  // BASE_LINKED_PTR_H_
diff --git a/base/linked_ptr_unittest.cc b/base/linked_ptr_unittest.cc
new file mode 100644
index 0000000..c749ce9
--- /dev/null
+++ b/base/linked_ptr_unittest.cc
@@ -0,0 +1,136 @@
+// linked_ptr_unittest.cc
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <string>
+#include <iostream>
+
+#include "base/linked_ptr.h"
+
+#include "base/string_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+int num = 0;
+
+std::string history;
+
+// Class which tracks allocation/deallocation
+struct A {
+  A(): mynum(num++) { history += StringPrintf("A%d ctor\n", mynum); }
+  virtual ~A() { history += StringPrintf("A%d dtor\n", mynum); }
+  virtual void Use() { history += StringPrintf("A%d use\n", mynum); }
+  int mynum;
+};
+
+// Subclass
+struct B: public A {
+  B() { history += StringPrintf("B%d ctor\n", mynum); }
+  ~B() { history += StringPrintf("B%d dtor\n", mynum); }
+  virtual void Use() { history += StringPrintf("B%d use\n", mynum); }
+};
+
+}  // namespace
+
+TEST(LinkedPtrTest, Test) {
+  {
+    linked_ptr<A> a0, a1, a2;
+    a0 = a0;
+    a1 = a2;
+    ASSERT_EQ(a0.get(), static_cast<A*>(NULL));
+    ASSERT_EQ(a1.get(), static_cast<A*>(NULL));
+    ASSERT_EQ(a2.get(), static_cast<A*>(NULL));
+    ASSERT_TRUE(a0 == NULL);
+    ASSERT_TRUE(a1 == NULL);
+    ASSERT_TRUE(a2 == NULL);
+
+    {
+      linked_ptr<A> a3(new A);
+      a0 = a3;
+      ASSERT_TRUE(a0 == a3);
+      ASSERT_TRUE(a0 != NULL);
+      ASSERT_TRUE(a0.get() == a3);
+      ASSERT_TRUE(a0 == a3.get());
+      linked_ptr<A> a4(a0);
+      a1 = a4;
+      linked_ptr<A> a5(new A);
+      ASSERT_TRUE(a5.get() != a3);
+      ASSERT_TRUE(a5 != a3.get());
+      a2 = a5;
+      linked_ptr<B> b0(new B);
+      linked_ptr<A> a6(b0);
+      ASSERT_TRUE(b0 == a6);
+      ASSERT_TRUE(a6 == b0);
+      ASSERT_TRUE(b0 != NULL);
+      a5 = b0;
+      a5 = b0;
+      a3->Use();
+      a4->Use();
+      a5->Use();
+      a6->Use();
+      b0->Use();
+      (*b0).Use();
+      b0.get()->Use();
+    }
+
+    a0->Use();
+    a1->Use();
+    a2->Use();
+
+    a1 = a2;
+    a2.reset(new A);
+    a0.reset();
+
+    linked_ptr<A> a7;
+  }
+
+  ASSERT_EQ(history,
+    "A0 ctor\n"
+    "A1 ctor\n"
+    "A2 ctor\n"
+    "B2 ctor\n"
+    "A0 use\n"
+    "A0 use\n"
+    "B2 use\n"
+    "B2 use\n"
+    "B2 use\n"
+    "B2 use\n"
+    "B2 use\n"
+    "B2 dtor\n"
+    "A2 dtor\n"
+    "A0 use\n"
+    "A0 use\n"
+    "A1 use\n"
+    "A3 ctor\n"
+    "A0 dtor\n"
+    "A3 dtor\n"
+    "A1 dtor\n"
+  );
+}
diff --git a/base/lock.cc b/base/lock.cc
new file mode 100644
index 0000000..afa1a6f
--- /dev/null
+++ b/base/lock.cc
@@ -0,0 +1,156 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Provide place to put profiling methods for the
+// Lock class.
+// The Lock class is used everywhere, and hence any changes
+// to lock.h tend to require a complete rebuild.  To facilitate
+// profiler development, all the profiling methods are listed
+// here.  Note that they are only instantiated in a debug
+// build, and the header provides all the trivial implementations
+// in a production build.
+
+#include "base/lock.h"
+#include "base/logging.h"
+
+Lock::Lock()
+    : lock_()
+    , recursion_count_shadow_(0) {
+#ifndef NDEBUG
+  recursion_used_ = false;
+  acquisition_count_ = 0;
+  contention_count_ = 0;
+#endif
+}
+
+Lock::~Lock() {
+#ifndef NDEBUG
+  // There should be no one to contend for the lock,
+  // ...but we need the memory barrier to get a good value.
+  lock_.Lock();
+  int final_recursion_count = recursion_count_shadow_;
+  lock_.Unlock();
+#endif
+
+  // Allow unit test exception only at end of method.
+#ifndef NDEBUG
+  DCHECK(0 == final_recursion_count);
+#endif
+}
+
+void Lock::Acquire() {
+#ifdef NDEBUG
+  lock_.Lock();
+  recursion_count_shadow_++;
+#else  // NDEBUG
+  if (!lock_.Try()) {
+    // We have contention.
+    lock_.Lock();
+    contention_count_++;
+  }
+  // ONLY access data after locking.
+  recursion_count_shadow_++;
+  if (1 == recursion_count_shadow_)
+    acquisition_count_++;
+  else if (2 == recursion_count_shadow_ && !recursion_used_)
+    // Usage Note: Set a break point to debug.
+    recursion_used_ = true;
+#endif  // NDEBUG
+}
+
+void Lock::Release() {
+  --recursion_count_shadow_;  // ONLY access while lock is still held.
+#ifndef NDEBUG
+  DCHECK(0 <= recursion_count_shadow_);
+#endif
+  lock_.Unlock();
+}
+
+bool Lock::Try() {
+  if (lock_.Try()) {
+    recursion_count_shadow_++;
+#ifndef NDEBUG
+    if (1 == recursion_count_shadow_)
+      acquisition_count_++;
+    else if (2 == recursion_count_shadow_ && !recursion_used_)
+      // Usage Note: Set a break point to debug.
+      recursion_used_ = true;
+#endif
+    return true;
+  } else {
+    return false;
+  }
+}
+
+// GetCurrentThreadRecursionCount returns the number of nested Acquire() calls
+// that have been made by the current thread holding this lock.  The calling
+// thread is ***REQUIRED*** to be *currently* holding the lock.   If that
+// calling requirement is violated, the return value is not well defined.
+// Return results are guaranteed correct if the caller has acquired this lock.
+// The return results might be incorrect otherwise.
+// This method is designed to be fast in non-debug mode by co-opting
+// synchronization using lock_ (no additional synchronization is used), but in
+// debug mode it slowly and carefully validates the requirement (and fires a
+// a DCHECK if it was called incorrectly).
+int32 Lock::GetCurrentThreadRecursionCount() {
+#ifndef NDEBUG
+  // If this DCHECK fails, then the most probable cause is:
+  // This method was called by class AutoUnlock during processing of a
+  // Wait() call made into the ConditonVariable class. That call to
+  // Wait() was made (incorrectly) without first Aquiring this Lock
+  // instance.
+  lock_.Lock();
+  int temp = recursion_count_shadow_;
+  lock_.Unlock();
+  // Unit tests catch an exception, so we need to be careful to test
+  // outside the critical section, since the Leave would be skipped!?!
+  DCHECK(temp >= 1);  // Allow unit test exception only at end of method.
+#endif  // DEBUG
+
+  // We hold lock, so this *is* correct value.
+  return recursion_count_shadow_;
+}
+
+
+AutoUnlock::AutoUnlock(Lock& lock) : lock_(&lock), release_count_(0) {
+  // We require our caller have the lock, so we can call for recursion count.
+  // CRITICALLY: Fetch value before we release the lock.
+  int32 count = lock_->GetCurrentThreadRecursionCount();
+  DCHECK(count > 0);  // Make sure we owned the lock.
+  while (count-- > 0) {
+    release_count_++;
+    lock_->Release();
+  }
+}
+
+AutoUnlock::~AutoUnlock() {
+  DCHECK(release_count_ >= 0);
+  while (release_count_-- > 0)
+    lock_->Acquire();
+}
diff --git a/base/lock.h b/base/lock.h
new file mode 100644
index 0000000..9613da0
--- /dev/null
+++ b/base/lock.h
@@ -0,0 +1,122 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_LOCK_H__
+#define BASE_LOCK_H__
+
+#include "base/lock_impl.h"
+
+// A convenient wrapper for a critical section.
+//
+// NOTE: A thread may acquire the same lock multiple times, but it must call
+// Release for each call to Acquire in order to finally release the lock.
+//
+// Complication: UnitTest for DeathTests catch DCHECK exceptions, so we need
+// to write code assuming DCHECK will throw.  This means we need to save any
+// assertable value in a local until we can safely throw.
+class Lock {
+ public:
+  Lock();
+  ~Lock();
+  void Acquire();
+  void Release();
+  // If the lock is not held, take it and return true. If the lock is already
+  // held by something else, immediately return false.
+  bool Try();
+
+ private:
+  LockImpl lock_;  // User-supplied underlying lock implementation.
+
+  // All private data is implicitly protected by spin_lock_.
+  // Be VERY careful to only access under that lock.
+  int32 recursion_count_shadow_;
+
+  // Allow access to GetCurrentThreadRecursionCount()
+  friend class AutoUnlock;
+  int32 GetCurrentThreadRecursionCount();
+
+#ifndef NDEBUG
+  // Even in Debug mode, the expensive tallies won't be calculated by default.
+  bool recursion_used_;
+  int32 acquisition_count_;
+
+  int32 contention_count_;
+#endif  // NDEBUG
+
+  DISALLOW_EVIL_CONSTRUCTORS(Lock);
+};
+
+// A helper class that acquires the given Lock while the AutoLock is in scope.
+class AutoLock {
+ public:
+  AutoLock(Lock& lock) : lock_(lock) {
+    lock_.Acquire();
+  }
+
+  ~AutoLock() {
+    lock_.Release();
+  }
+
+ private:
+  Lock& lock_;
+  DISALLOW_EVIL_CONSTRUCTORS(AutoLock);
+};
+
+// A helper macro to perform a single operation (expressed by expr)
+// in a lock
+#define LOCKED_EXPRESSION(lock, expr) \
+  do { \
+    AutoLock _auto_lock(lock);  \
+    (expr); \
+  } while (0)
+
+// AutoUnlock is a helper class for ConditionVariable instances
+// that is analogous to AutoLock.  It provides for nested Releases
+// of a lock for the Wait functionality of a ConditionVariable class.
+// The destructor automatically does the corresponding Acquire
+// calls (to return to the initial nested lock state).
+
+// Instances of AutoUnlock can ***ONLY*** validly be constructed if the
+// caller currently holds the lock provided as the constructor's argument.
+// If that ***REQUIREMENT*** is violated in debug mode, a DCHECK will
+// be generated in the Lock class.  In production (non-debug),
+// the results are undefined (and probably bad) if the caller
+// is not already holding the indicated lock.
+class ConditionVariable;
+class AutoUnlock {
+ private:  // Everything is private, so only our friend can use us.
+  friend class ConditionVariable;  // The only user of this class.
+  explicit AutoUnlock(Lock& lock);
+  ~AutoUnlock();
+
+  Lock* lock_;
+  int release_count_;
+};
+
+#endif  // BASE_LOCK_H__
diff --git a/base/lock_impl.h b/base/lock_impl.h
new file mode 100644
index 0000000..331bdc6
--- /dev/null
+++ b/base/lock_impl.h
@@ -0,0 +1,87 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_LOCK_IMPL_H__
+#define BASE_LOCK_IMPL_H__
+
+#include "base/basictypes.h"
+
+#if defined(WIN32)
+#include <Windows.h>
+#elif defined(__APPLE__)
+#include <libkern/OSAtomic.h>
+#endif
+
+// This class implements the underlying platform-specific spin-lock mechanism
+// used for the Lock class.  Most users should not use LockImpl directly, but
+// should instead use Lock.
+class LockImpl {
+ public:
+  LockImpl();
+  ~LockImpl();
+
+  // If the lock is not held, take it and return true.  If the lock is already
+  // held by something else, immediately return false.
+  bool Try();
+
+  // Take the lock, blocking until it is available if necessary.
+  void Lock();
+
+  // Release the lock.  This must only be called by the lock's holder: after
+  // a successful call to Try, or a call to Lock.
+  void Unlock();
+
+ private:
+#if defined(WIN32)
+  CRITICAL_SECTION critical_section_;
+#elif defined(__APPLE__)
+  OSSpinLock spin_lock_;
+#endif
+
+  DISALLOW_EVIL_CONSTRUCTORS(LockImpl);
+};
+
+class AutoLockImpl {
+ public:
+  AutoLockImpl(LockImpl* lock_impl)
+      : lock_impl_(lock_impl) {
+    lock_impl_->Lock();
+  }
+
+  ~AutoLockImpl() {
+    lock_impl_->Unlock();
+  }
+
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(AutoLockImpl);
+
+  LockImpl* lock_impl_;
+};
+
+#endif  // BASE_LOCK_IMPL_H__
diff --git a/base/logging.cc b/base/logging.cc
new file mode 100644
index 0000000..eba177e
--- /dev/null
+++ b/base/logging.cc
@@ -0,0 +1,396 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <ctime>
+#include <iomanip>
+#include <cstring>
+#include <windows.h>
+#include <algorithm>
+#include "base/base_switches.h"
+#include "base/command_line.h"
+#include "base/lock_impl.h"
+#include "base/logging.h"
+
+namespace logging {
+
+bool g_enable_dcheck = false;
+
+const char* const log_severity_names[LOG_NUM_SEVERITIES] = {
+  "INFO", "WARNING", "ERROR", "FATAL" };
+
+int min_log_level = 0;
+LogLockingState lock_log_file = LOCK_LOG_FILE;
+LoggingDestination logging_destination = LOG_ONLY_TO_FILE;
+
+const int kMaxFilteredLogLevel = LOG_WARNING;
+char* log_filter_prefix = NULL;
+
+// which log file to use? This is initialized by InitLogging or
+// will be lazily initialized to the default value when it is
+// first needed.
+wchar_t log_file_name[MAX_PATH] = { 0 };
+
+// this file is lazily opened and the handle may be NULL
+HANDLE log_file = NULL;
+
+// what should be prepended to each message?
+bool log_process_id = false;
+bool log_thread_id = false;
+bool log_timestamp = true;
+bool log_tickcount = false;
+
+// An assert handler override specified by the client to be called instead of
+// the debug message dialog.
+LogAssertHandlerFunction log_assert_handler = NULL;
+
+// The lock is used if log file locking is false. It helps us avoid problems
+// with multiple threads writing to the log file at the same time.  Use
+// LockImpl directly instead of using Lock, because Lock makes logging calls.
+static LockImpl* log_lock = NULL;
+
+// When we don't use a lock, we are using a global mutex. We need to do this
+// because LockFileEx is not thread safe.
+HANDLE log_mutex = NULL;
+
+// Called by logging functions to ensure that debug_file is initialized
+// and can be used for writing. Returns false if the file could not be
+// initialized. debug_file will be NULL in this case.
+bool InitializeLogFileHandle() {
+  if (log_file)
+    return true;
+
+  if (!log_file_name[0]) {
+    // nobody has called InitLogging to specify a debug log file, so here we
+    // initialize the log file name to the default
+    GetModuleFileName(NULL, log_file_name, MAX_PATH);
+    wchar_t* last_backslash = wcsrchr(log_file_name, '\\');
+    if (last_backslash)
+      last_backslash[1] = 0; // name now ends with the backslash
+    wcscat_s(log_file_name, L"debug.log");
+  }
+
+  log_file = CreateFile(log_file_name, GENERIC_WRITE,
+                        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+                        OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+  if (log_file == INVALID_HANDLE_VALUE || log_file == NULL) {
+    // try the current directory
+    log_file = CreateFile(L".\\debug.log", GENERIC_WRITE,
+                          FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+                          OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+    if (log_file == INVALID_HANDLE_VALUE || log_file == NULL) {
+      log_file = NULL;
+      return false;
+    }
+  }
+  SetFilePointer(log_file, 0, 0, FILE_END);
+  return true;
+}
+
+void InitLogMutex() {
+  if (!log_mutex) {
+    // \ is not a legal character in mutex names so we replace \ with /
+    std::wstring safe_name(log_file_name);
+    std::replace(safe_name.begin(), safe_name.end(), '\\', '/');
+    std::wstring t(L"Global\\");
+    t.append(safe_name);
+    log_mutex = ::CreateMutex(NULL, FALSE, t.c_str());
+  }
+}
+
+void InitLogging(const wchar_t* new_log_file, LoggingDestination logging_dest,
+                 LogLockingState lock_log, OldFileDeletionState delete_old) {
+  g_enable_dcheck = CommandLine().HasSwitch(switches::kEnableDCHECK);
+
+  if (log_file) {
+    // calling InitLogging twice or after some log call has already opened the
+    // default log file will re-initialize to the new options
+    CloseHandle(log_file);
+    log_file = NULL;
+  }
+
+  lock_log_file = lock_log;
+  logging_destination = logging_dest;
+
+  // ignore file options if logging is disabled or only to system
+  if (logging_destination == LOG_NONE ||
+      logging_destination == LOG_ONLY_TO_SYSTEM_DEBUG_LOG)
+    return;
+
+  wcscpy_s(log_file_name, MAX_PATH, new_log_file);
+  if (delete_old == DELETE_OLD_LOG_FILE)
+    DeleteFile(log_file_name);
+
+  if (lock_log_file == LOCK_LOG_FILE) {
+    InitLogMutex();
+  } else if (!log_lock) {
+    log_lock = new LockImpl();
+  }
+
+  InitializeLogFileHandle();
+}
+
+void SetMinLogLevel(int level) {
+  min_log_level = level;
+}
+
+int GetMinLogLevel() {
+  return min_log_level;
+}
+
+void SetLogFilterPrefix(const char* filter)  {
+  if (log_filter_prefix) {
+    delete[] log_filter_prefix;
+    log_filter_prefix = NULL;
+  }
+
+  if (filter) {
+    size_t size = strlen(filter)+1;
+    log_filter_prefix = new char[size];
+    strcpy_s(log_filter_prefix, size, filter);
+  }
+}
+
+void SetLogItems(bool enable_process_id, bool enable_thread_id,
+                 bool enable_timestamp, bool enable_tickcount) {
+  log_process_id = enable_process_id;
+  log_thread_id = enable_thread_id;
+  log_timestamp = enable_timestamp;
+  log_tickcount = enable_tickcount;
+}
+
+void SetLogAssertHandler(LogAssertHandlerFunction handler) {
+  log_assert_handler = handler;
+}
+
+// Displays a message box to the user with the error message in it. For
+// Windows programs, it's possible that the message loop is messed up on
+// a fatal error, and creating a MessageBox will cause that message loop
+// to be run. Instead, we try to spawn another process that displays its
+// command line. We look for "Debug Message.exe" in the same directory as
+// the application. If it exists, we use it, otherwise, we use a regular
+// message box.
+void DisplayDebugMessage(const std::string& str) {
+  if (str.empty())
+    return;
+
+  // look for the debug dialog program next to our application
+  wchar_t prog_name[MAX_PATH];
+  GetModuleFileNameW(NULL, prog_name, MAX_PATH);
+  wchar_t* backslash = wcsrchr(prog_name, '\\');
+  if (backslash)
+    backslash[1] = 0;
+  wcscat_s(prog_name, MAX_PATH, L"debug_message.exe");
+
+  // stupid CreateProcess requires a non-const command line and may modify it.
+  // We also want to use the wide string
+  int charcount = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, NULL, 0);
+  if (!charcount)
+    return;
+  scoped_array<wchar_t> cmdline(new wchar_t[charcount]);
+  if (!MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, cmdline.get(), charcount))
+    return;
+
+  STARTUPINFO startup_info;
+  memset(&startup_info, 0, sizeof(startup_info));
+  startup_info.cb = sizeof(startup_info);
+
+  PROCESS_INFORMATION process_info;
+  if (CreateProcessW(prog_name, cmdline.get(), NULL, NULL, false, 0, NULL,
+                     NULL, &startup_info, &process_info)) {
+    WaitForSingleObject(process_info.hProcess, INFINITE);
+    CloseHandle(process_info.hThread);
+    CloseHandle(process_info.hProcess);
+  } else {
+    // debug process broken, let's just do a message box
+    MessageBoxW(NULL, cmdline.get(), L"Fatal error",
+                MB_OK | MB_ICONHAND | MB_TOPMOST);
+  }
+}
+
+LogMessage::LogMessage(const char* file, int line, LogSeverity severity,
+                       int ctr)
+    : severity_(severity) {
+  Init(file, line);
+}
+
+LogMessage::LogMessage(const char* file, int line, const CheckOpString& result)
+    : severity_(LOG_FATAL) {
+  Init(file, line);
+  stream_ << "Check failed: " << (*result.str_);
+}
+
+LogMessage::LogMessage(const char* file, int line)
+     : severity_(LOG_INFO) {
+  Init(file, line);
+}
+
+LogMessage::LogMessage(const char* file, int line, LogSeverity severity)
+    : severity_(severity) {
+  Init(file, line);
+}
+
+// writes the common header info to the stream
+void LogMessage::Init(const char* file, int line) {
+  // log only the filename
+  const char* last_slash = strrchr(file, '\\');
+  if (last_slash)
+    file = last_slash + 1;
+
+  // TODO(darin): It might be nice if the columns were fixed width.
+
+  stream_ <<  '[';
+  if (log_process_id)
+    stream_ << GetCurrentProcessId() << ':';
+  if (log_thread_id)
+    stream_ << GetCurrentThreadId() << ':';
+  if (log_timestamp) {
+     time_t t = time(NULL);
+#if _MSC_VER >= 1400
+    struct tm local_time = {0};
+    localtime_s(&local_time, &t);
+    struct tm* tm_time = &local_time;
+#else
+    struct tm* tm_time = localtime(&t);
+#endif
+    stream_ << std::setfill('0')
+            << std::setw(2) << 1 + tm_time->tm_mon
+            << std::setw(2) << tm_time->tm_mday
+            << '/'
+            << std::setw(2) << tm_time->tm_hour
+            << std::setw(2) << tm_time->tm_min
+            << std::setw(2) << tm_time->tm_sec
+            << ':';
+  }
+  if (log_tickcount)
+    stream_ << GetTickCount() << ':';
+  stream_ << log_severity_names[severity_] << ":" << file << "(" << line << ")] ";
+
+  message_start_ = stream_.tellp();
+}
+
+LogMessage::~LogMessage() {
+  // TODO(brettw) modify the macros so that nothing is executed when the log
+  // level is too high.
+  if (severity_ < min_log_level)
+    return;
+
+  std::string str_newline(stream_.str());
+  str_newline.append("\r\n");
+
+  if (log_filter_prefix && severity_ <= kMaxFilteredLogLevel &&
+      str_newline.compare(message_start_, strlen(log_filter_prefix),
+                          log_filter_prefix) != 0) {
+    return;
+  }
+
+  if (logging_destination == LOG_ONLY_TO_SYSTEM_DEBUG_LOG ||
+      logging_destination == LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG)
+    OutputDebugStringA(str_newline.c_str());
+
+  // write to log file
+  if (logging_destination != LOG_NONE &&
+      logging_destination != LOG_ONLY_TO_SYSTEM_DEBUG_LOG &&
+      InitializeLogFileHandle()) {
+    // we can have multiple threads and/or processes, so try to prevent them from
+    // clobbering each other's writes
+    if (lock_log_file == LOCK_LOG_FILE) {
+      // Ensure that the mutex is initialized in case the client app did not
+      // call InitLogging. This is not thread safe. See below
+      InitLogMutex();
+
+      DWORD r = ::WaitForSingleObject(log_mutex, INFINITE);
+      DCHECK(r != WAIT_ABANDONED);
+    } else {
+      // use the lock
+      if (!log_lock) {
+        // The client app did not call InitLogging, and so the lock has not
+        // been created. We do this on demand, but if two threads try to do
+        // this at the same time, there will be a race condition to create
+        // the lock. This is why InitLogging should be called from the main
+        // thread at the beginning of execution.
+        log_lock = new LockImpl();
+      }
+      log_lock->Lock();
+    }
+
+    SetFilePointer(log_file, 0, 0, SEEK_END);
+    DWORD num_written;
+    WriteFile(log_file, (void*)str_newline.c_str(), (DWORD)str_newline.length(), &num_written, NULL);
+
+    if (lock_log_file == LOCK_LOG_FILE) {
+      ReleaseMutex(log_mutex);
+    } else {
+      log_lock->Unlock();
+    }
+  }
+
+  if (severity_ == LOG_FATAL) {
+    // display a message or break into the debugger on a fatal error
+    if (::IsDebuggerPresent()) {
+      __debugbreak();
+    } else {
+      if (log_assert_handler) {
+        // make a copy of the string for the handler out of paranoia
+        log_assert_handler(std::string(stream_.str()));
+      } else {
+        // don't use the string with the newline, get a fresh version to send to
+        // the debug message process
+        DisplayDebugMessage(stream_.str());
+        // Crash the process to generate a dump.
+        __debugbreak();
+      }
+    }
+  }
+}
+
+void CloseLogFile() {
+  if (!log_file)
+    return;
+
+  CloseHandle(log_file);
+  log_file = NULL;
+}
+
+} // namespace logging
+
+std::ostream& operator<<(std::ostream& out, const wchar_t* wstr) {
+  if (!wstr || !wstr[0])
+    return out;
+
+  // compute the length of the buffer we'll need
+  int charcount = WideCharToMultiByte(CP_UTF8, 0, wstr, -1,
+                                      NULL, 0, NULL, NULL);
+  if (charcount == 0)
+    return out;
+
+  // convert
+  scoped_array<char> buf(new char[charcount]);
+  WideCharToMultiByte(CP_UTF8, 0, wstr, -1, buf.get(), charcount, NULL, NULL);
+  return out << buf.get();
+}
diff --git a/base/logging.h b/base/logging.h
new file mode 100644
index 0000000..615b1e0
--- /dev/null
+++ b/base/logging.h
@@ -0,0 +1,523 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_LOGGING_H__
+#define BASE_LOGGING_H__
+
+#include <string>
+#include <cstring>
+#include <sstream>
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+
+//
+// Optional message capabilities
+// -----------------------------
+// Assertion failed messages and fatal errors are displayed in a dialog box
+// before the application exits. However, running this UI creates a message
+// loop, which causes application messages to be processed and potentially
+// dispatched to existing application windows. Since the application is in a
+// bad state when this assertion dialog is displayed, these messages may not
+// get processed and hang the dialog, or the application might go crazy.
+//
+// Therefore, it can be beneficial to display the error dialog in a separate
+// process from the main application. When the logging system needs to display
+// a fatal error dialog box, it will look for a program called
+// "DebugMessage.exe" in the same directory as the application executable. It
+// will run this application with the message as the command line, and will
+// not include the name of the application as is traditional for easier
+// parsing.
+//
+// The code for DebugMessage.exe is only one line. In WinMain, do:
+//   MessageBox(NULL, GetCommandLineW(), L"Fatal Error", 0);
+//
+// If DebugMessage.exe is not found, the logging code will use a normal
+// MessageBox, potentially causing the problems discussed above.
+
+
+// Instructions
+// ------------
+//
+// Make a bunch of macros for logging.  The way to log things is to stream
+// things to LOG(<a particular severity level>).  E.g.,
+//
+//   LOG(INFO) << "Found " << num_cookies << " cookies";
+//
+// You can also do conditional logging:
+//
+//   LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
+//
+// The above will cause log messages to be output on the 1st, 11th, 21st, ...
+// times it is executed.  Note that the special COUNTER value is used to
+// identify which repetition is happening.
+//
+// The CHECK(condition) macro is active in both debug and release builds and
+// effectively performs a LOG(FATAL) which terminates the process and
+// generates a crashdump unless a debugger is attached.
+//
+// There are also "debug mode" logging macros like the ones above:
+//
+//   DLOG(INFO) << "Found cookies";
+//
+//   DLOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
+//
+// All "debug mode" logging is compiled away to nothing for non-debug mode
+// compiles.  LOG_IF and development flags also work well together
+// because the code can be compiled away sometimes.
+//
+// We also have
+//
+//   LOG_ASSERT(assertion);
+//   DLOG_ASSERT(assertion);
+//
+// which is syntactic sugar for {,D}LOG_IF(FATAL, assert fails) << assertion;
+//
+// We also override the standard 'assert' to use 'DLOG_ASSERT'.
+//
+// The supported severity levels for macros that allow you to specify one
+// are (in increasing order of severity) INFO, WARNING, ERROR, and FATAL.
+//
+// There is also the special severity of DFATAL, which logs FATAL in
+// debug mode, ERROR in normal mode.
+//
+// Very important: logging a message at the FATAL severity level causes
+// the program to terminate (after the message is logged).
+
+namespace logging {
+
+// Where to record logging output? A flat file and/or system debug log via
+// OutputDebugString. Defaults to LOG_ONLY_TO_FILE.
+enum LoggingDestination { LOG_NONE,
+                          LOG_ONLY_TO_FILE,
+                          LOG_ONLY_TO_SYSTEM_DEBUG_LOG,
+                          LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG };
+
+// Indicates that the log file should be locked when being written to.
+// Often, there is no locking, which is fine for a single threaded program.
+// If logging is being done from multiple threads or there can be more than
+// one process doing the logging, the file should be locked during writes to
+// make each log outut atomic. Other writers will block.
+//
+// All processes writing to the log file must have their locking set for it to
+// work properly. Defaults to DONT_LOCK_LOG_FILE.
+enum LogLockingState { LOCK_LOG_FILE, DONT_LOCK_LOG_FILE };
+
+// On startup, should we delete or append to an existing log file (if any)?
+// Defaults to APPEND_TO_OLD_LOG_FILE.
+enum OldFileDeletionState { DELETE_OLD_LOG_FILE, APPEND_TO_OLD_LOG_FILE };
+
+// Sets the log file name and other global logging state. Calling this function
+// is recommended, and is normally done at the beginning of application init.
+// If you don't call it, all the flags will be initialized to their default
+// values, and there is a race condition that may leak a critical section
+// object if two threads try to do the first log at the same time.
+// See the definition of the enums above for descriptions and default values.
+//
+// The default log file is initialized to "debug.log" in the application
+// directory. You probably don't want this, especially since the program
+// directory may not be writable on an enduser's system.
+#if defined(WIN32)
+void InitLogging(const wchar_t* log_file, LoggingDestination logging_dest,
+                 LogLockingState lock_log, OldFileDeletionState delete_old);
+#else
+// TODO(avi): do we want to do a unification of character types here?
+void InitLogging(const char* log_file, LoggingDestination logging_dest,
+                 LogLockingState lock_log, OldFileDeletionState delete_old);
+#endif
+
+// Sets the log level. Anything at or above this level will be written to the
+// log file/displayed to the user (if applicable). Anything below this level
+// will be silently ignored. The log level defaults to 0 (everything is logged)
+// if this function is not called.
+void SetMinLogLevel(int level);
+
+// Gets the curreng log level.
+int GetMinLogLevel();
+
+// Sets the log filter prefix.  Any log message below LOG_ERROR severity that
+// doesn't start with this prefix with be silently ignored.  The filter defaults
+// to NULL (everything is logged) if this function is not called.  Messages
+// with severity of LOG_ERROR or higher will not be filtered.
+void SetLogFilterPrefix(const char* filter);
+
+// Sets the common items you want to be prepended to each log message.
+// process and thread IDs default to off, the timestamp defaults to on.
+// If this function is not called, logging defaults to writing the timestamp
+// only.
+void SetLogItems(bool enable_process_id, bool enable_thread_id,
+                 bool enable_timestamp, bool enable_tickcount);
+
+// Sets the Log Assert Handler that will be used to notify of check failures.
+// The default handler shows a dialog box, however clients can use this
+// function to override with their own handling (e.g. a silent one for Unit
+// Tests)
+typedef void (*LogAssertHandlerFunction)(const std::string& str);
+void SetLogAssertHandler(LogAssertHandlerFunction handler);
+
+typedef int LogSeverity;
+const LogSeverity LOG_INFO = 0;
+const LogSeverity LOG_WARNING = 1;
+const LogSeverity LOG_ERROR = 2;
+const LogSeverity LOG_FATAL = 3;
+const LogSeverity LOG_NUM_SEVERITIES = 4;
+
+// LOG_DFATAL_LEVEL is LOG_FATAL in debug mode, ERROR in normal mode
+#ifdef NDEBUG
+const LogSeverity LOG_DFATAL_LEVEL = LOG_ERROR;
+#else
+const LogSeverity LOG_DFATAL_LEVEL = LOG_FATAL;
+#endif
+
+// A few definitions of macros that don't generate much code. These are used
+// by LOG() and LOG_IF, etc. Since these are used all over our code, it's
+// better to have compact code for these operations.
+#define COMPACT_GOOGLE_LOG_INFO \
+  logging::LogMessage(__FILE__, __LINE__)
+#define COMPACT_GOOGLE_LOG_WARNING \
+  logging::LogMessage(__FILE__, __LINE__, logging::LOG_WARNING)
+#define COMPACT_GOOGLE_LOG_ERROR \
+  logging::LogMessage(__FILE__, __LINE__, logging::LOG_ERROR)
+#define COMPACT_GOOGLE_LOG_FATAL \
+  logging::LogMessage(__FILE__, __LINE__, logging::LOG_FATAL)
+#define COMPACT_GOOGLE_LOG_DFATAL \
+  logging::LogMessage(__FILE__, __LINE__, logging::LOG_DFATAL_LEVEL)
+
+// wingdi.h defines ERROR to be 0. When we call LOG(ERROR), it gets
+// substituted with 0, and it expands to COMPACT_GOOGLE_LOG_0. To allow us
+// to keep using this syntax, we define this macro to do the same thing
+// as COMPACT_GOOGLE_LOG_ERROR, and also define ERROR the same way that
+// the Windows SDK does for consistency.
+#define ERROR 0
+#define COMPACT_GOOGLE_LOG_0 \
+  logging::LogMessage(__FILE__, __LINE__, logging::LOG_ERROR)
+
+// We use the preprocessor's merging operator, "##", so that, e.g.,
+// LOG(INFO) becomes the token COMPACT_GOOGLE_LOG_INFO.  There's some funny
+// subtle difference between ostream member streaming functions (e.g.,
+// ostream::operator<<(int) and ostream non-member streaming functions
+// (e.g., ::operator<<(ostream&, string&): it turns out that it's
+// impossible to stream something like a string directly to an unnamed
+// ostream. We employ a neat hack by calling the stream() member
+// function of LogMessage which seems to avoid the problem.
+
+#define LOG(severity) COMPACT_GOOGLE_LOG_ ## severity.stream()
+#define SYSLOG(severity) LOG(severity)
+
+#define LOG_IF(severity, condition) \
+  !(condition) ? (void) 0 : logging::LogMessageVoidify() & LOG(severity)
+#define SYSLOG_IF(severity, condition) LOG_IF(severity, condition)
+
+#define LOG_ASSERT(condition)  \
+  LOG_IF(FATAL, !(condition)) << "Assert failed: " #condition ". "
+#define SYSLOG_ASSERT(condition) \
+  SYSLOG_IF(FATAL, !(condition)) << "Assert failed: " #condition ". "
+
+// CHECK dies with a fatal error if condition is not true.  It is *not*
+// controlled by NDEBUG, so the check will be executed regardless of
+// compilation mode.
+#define CHECK(condition) \
+  LOG_IF(FATAL, !(condition)) << "Check failed: " #condition ". "
+
+// A container for a string pointer which can be evaluated to a bool -
+// true iff the pointer is NULL.
+struct CheckOpString {
+  CheckOpString(std::string* str) : str_(str) { }
+  // No destructor: if str_ is non-NULL, we're about to LOG(FATAL),
+  // so there's no point in cleaning up str_.
+  operator bool() const { return str_ != NULL; }
+  std::string* str_;
+};
+
+// Build the error message string.  This is separate from the "Impl"
+// function template because it is not performance critical and so can
+// be out of line, while the "Impl" code should be inline.
+template<class t1, class t2>
+std::string* MakeCheckOpString(const t1& v1, const t2& v2, const char* names) {
+  std::ostringstream ss;
+  ss << names << " (" << v1 << " vs. " << v2 << ")";
+  std::string* msg = new std::string(ss.str());
+  return msg;
+}
+
+extern std::string* MakeCheckOpStringIntInt(int v1, int v2, const char* names);
+
+template<int, int>
+std::string* MakeCheckOpString(const int& v1, const int& v2, const char* names) {
+  return MakeCheckOpStringIntInt(v1, v2, names);
+}
+
+// Plus some debug-logging macros that get compiled to nothing for production
+//
+// DEBUG_MODE is for uses like
+//   if (DEBUG_MODE) foo.CheckThatFoo();
+// instead of
+//   #ifndef NDEBUG
+//     foo.CheckThatFoo();
+//   #endif
+
+#ifndef NDEBUG
+
+#define DLOG(severity) LOG(severity)
+#define DLOG_IF(severity, condition) LOG_IF(severity, condition)
+#define DLOG_ASSERT(condition) LOG_ASSERT(condition)
+
+// debug-only checking.  not executed in NDEBUG mode.
+enum { DEBUG_MODE = 1 };
+#define DCHECK(condition) \
+  LOG_IF(FATAL, !(condition)) << "Check failed: " #condition ". "
+
+// Helper macro for binary operators.
+// Don't use this macro directly in your code, use DCHECK_EQ et al below.
+#define DCHECK_OP(name, op, val1, val2)  \
+  if (logging::CheckOpString _result = \
+      logging::Check##name##Impl((val1), (val2), #val1 " " #op " " #val2)) \
+    logging::LogMessage(__FILE__, __LINE__, _result).stream()
+
+// Helper functions for string comparisons.
+// To avoid bloat, the definitions are in logging.cc.
+#define DECLARE_DCHECK_STROP_IMPL(func, expected) \
+  std::string* Check##func##expected##Impl(const char* s1, \
+                                           const char* s2, \
+                                           const char* names);
+DECLARE_DCHECK_STROP_IMPL(strcmp, true)
+DECLARE_DCHECK_STROP_IMPL(strcmp, false)
+DECLARE_DCHECK_STROP_IMPL(_stricmp, true)
+DECLARE_DCHECK_STROP_IMPL(_stricmp, false)
+#undef DECLARE_DCHECK_STROP_IMPL
+
+// Helper macro for string comparisons.
+// Don't use this macro directly in your code, use CHECK_STREQ et al below.
+#define DCHECK_STROP(func, op, expected, s1, s2) \
+  while (CheckOpString _result = \
+      logging::Check##func##expected##Impl((s1), (s2), \
+                                           #s1 " " #op " " #s2)) \
+    LOG(FATAL) << *_result.str_
+
+// String (char*) equality/inequality checks.
+// CASE versions are case-insensitive.
+//
+// Note that "s1" and "s2" may be temporary strings which are destroyed
+// by the compiler at the end of the current "full expression"
+// (e.g. DCHECK_STREQ(Foo().c_str(), Bar().c_str())).
+
+#define DCHECK_STREQ(s1, s2) DCHECK_STROP(strcmp, ==, true, s1, s2)
+#define DCHECK_STRNE(s1, s2) DCHECK_STROP(strcmp, !=, false, s1, s2)
+#define DCHECK_STRCASEEQ(s1, s2) DCHECK_STROP(_stricmp, ==, true, s1, s2)
+#define DCHECK_STRCASENE(s1, s2) DCHECK_STROP(_stricmp, !=, false, s1, s2)
+
+#define DCHECK_INDEX(I,A) DCHECK(I < (sizeof(A)/sizeof(A[0])))
+#define DCHECK_BOUND(B,A) DCHECK(B <= (sizeof(A)/sizeof(A[0])))
+
+#else  // NDEBUG
+
+#define DLOG(severity) \
+  true ? (void) 0 : logging::LogMessageVoidify() & LOG(severity)
+
+#define DLOG_IF(severity, condition) \
+  true ? (void) 0 : logging::LogMessageVoidify() & LOG(severity)
+
+#define DLOG_ASSERT(condition) \
+  true ? (void) 0 : LOG_ASSERT(condition)
+
+enum { DEBUG_MODE = 0 };
+
+// This macro can be followed by a sequence of stream parameters in
+// non-debug mode. The DCHECK and friends macros use this so that
+// the expanded expression DCHECK(foo) << "asdf" is still syntactically
+// valid, even though the expression will get optimized away.
+#define NDEBUG_EAT_STREAM_PARAMETERS \
+  logging::LogMessage(__FILE__, __LINE__).stream()
+
+// Set to true in InitLogging when we want to enable the dchecks in release.
+extern bool g_enable_dcheck;
+#define DCHECK(condition) \
+    !logging::g_enable_dcheck ? void (0) : \
+        LOG_IF(FATAL, !(condition)) << "Check failed: " #condition ". "
+
+// Helper macro for binary operators.
+// Don't use this macro directly in your code, use DCHECK_EQ et al below.
+#define DCHECK_OP(name, op, val1, val2)  \
+  if (logging::g_enable_dcheck) \
+    if (logging::CheckOpString _result = \
+        logging::Check##name##Impl((val1), (val2), #val1 " " #op " " #val2)) \
+      logging::LogMessage(__FILE__, __LINE__, _result).stream()
+
+#define DCHECK_STREQ(str1, str2) \
+  while (false) NDEBUG_EAT_STREAM_PARAMETERS
+
+#define DCHECK_STRCASEEQ(str1, str2) \
+  while (false) NDEBUG_EAT_STREAM_PARAMETERS
+
+#define DCHECK_STRNE(str1, str2) \
+  while (false) NDEBUG_EAT_STREAM_PARAMETERS
+
+#define DCHECK_STRCASENE(str1, str2) \
+  while (false) NDEBUG_EAT_STREAM_PARAMETERS
+
+#endif  // NDEBUG
+
+// Helper functions for DCHECK_OP macro.
+// The (int, int) specialization works around the issue that the compiler
+// will not instantiate the template version of the function on values of
+// unnamed enum type - see comment below.
+#define DEFINE_DCHECK_OP_IMPL(name, op) \
+  template <class t1, class t2> \
+  inline std::string* Check##name##Impl(const t1& v1, const t2& v2, \
+                                        const char* names) { \
+    if (v1 op v2) return NULL; \
+    else return MakeCheckOpString(v1, v2, names); \
+  } \
+  inline std::string* Check##name##Impl(int v1, int v2, const char* names) { \
+    if (v1 op v2) return NULL; \
+    else return MakeCheckOpString(v1, v2, names); \
+  }
+DEFINE_DCHECK_OP_IMPL(EQ, ==)
+DEFINE_DCHECK_OP_IMPL(NE, !=)
+DEFINE_DCHECK_OP_IMPL(LE, <=)
+DEFINE_DCHECK_OP_IMPL(LT, < )
+DEFINE_DCHECK_OP_IMPL(GE, >=)
+DEFINE_DCHECK_OP_IMPL(GT, > )
+#undef DEFINE_DCHECK_OP_IMPL
+
+// Equality/Inequality checks - compare two values, and log a LOG_FATAL message
+// including the two values when the result is not as expected.  The values
+// must have operator<<(ostream, ...) defined.
+//
+// You may append to the error message like so:
+//   DCHECK_NE(1, 2) << ": The world must be ending!";
+//
+// We are very careful to ensure that each argument is evaluated exactly
+// once, and that anything which is legal to pass as a function argument is
+// legal here.  In particular, the arguments may be temporary expressions
+// which will end up being destroyed at the end of the apparent statement,
+// for example:
+//   DCHECK_EQ(string("abc")[1], 'b');
+//
+// WARNING: These may not compile correctly if one of the arguments is a pointer
+// and the other is NULL. To work around this, simply static_cast NULL to the
+// type of the desired pointer.
+
+#define DCHECK_EQ(val1, val2) DCHECK_OP(EQ, ==, val1, val2)
+#define DCHECK_NE(val1, val2) DCHECK_OP(NE, !=, val1, val2)
+#define DCHECK_LE(val1, val2) DCHECK_OP(LE, <=, val1, val2)
+#define DCHECK_LT(val1, val2) DCHECK_OP(LT, < , val1, val2)
+#define DCHECK_GE(val1, val2) DCHECK_OP(GE, >=, val1, val2)
+#define DCHECK_GT(val1, val2) DCHECK_OP(GT, > , val1, val2)
+
+
+#define NOTREACHED() DCHECK(false)
+
+// Redefine the standard assert to use our nice log files
+#undef assert
+#define assert(x) DLOG_ASSERT(x)
+
+// This class more or less represents a particular log message.  You
+// create an instance of LogMessage and then stream stuff to it.
+// When you finish streaming to it, ~LogMessage is called and the
+// full message gets streamed to the appropriate destination.
+//
+// You shouldn't actually use LogMessage's constructor to log things,
+// though.  You should use the LOG() macro (and variants thereof)
+// above.
+class LogMessage {
+ public:
+  LogMessage(const char* file, int line, LogSeverity severity, int ctr);
+
+  // Two special constructors that generate reduced amounts of code at
+  // LOG call sites for common cases.
+  //
+  // Used for LOG(INFO): Implied are:
+  // severity = LOG_INFO, ctr = 0
+  //
+  // Using this constructor instead of the more complex constructor above
+  // saves a couple of bytes per call site.
+  LogMessage(const char* file, int line);
+
+  // Used for LOG(severity) where severity != INFO.  Implied
+  // are: ctr = 0
+  //
+  // Using this constructor instead of the more complex constructor above
+  // saves a couple of bytes per call site.
+  LogMessage(const char* file, int line, LogSeverity severity);
+
+  // A special constructor used for check failures.
+  // Implied severity = LOG_FATAL
+  LogMessage(const char* file, int line, const CheckOpString& result);
+
+  ~LogMessage();
+
+  std::ostream& stream() { return stream_; }
+
+ private:
+  void Init(const char* file, int line);
+
+  LogSeverity severity_;
+  std::ostringstream stream_;
+  int message_start_;  // offset of the start of the message (past prefix info).
+
+  DISALLOW_EVIL_CONSTRUCTORS(LogMessage);
+};
+
+// A non-macro interface to the log facility; (useful
+// when the logging level is not a compile-time constant).
+inline void LogAtLevel(int const log_level, std::string const &msg) {
+  LogMessage(__FILE__, __LINE__, log_level).stream() << msg;
+}
+
+// This class is used to explicitly ignore values in the conditional
+// logging macros.  This avoids compiler warnings like "value computed
+// is not used" and "statement has no effect".
+class LogMessageVoidify {
+ public:
+  LogMessageVoidify() { }
+  // This has to be an operator with a precedence lower than << but
+  // higher than ?:
+  void operator&(std::ostream&) { }
+};
+
+// Closes the log file explicitly if open.
+// NOTE: Since the log file is opened as necessary by the action of logging
+//       statements, there's no guarantee that it will stay closed
+//       after this call.
+void CloseLogFile();
+
+} // namespace Logging
+
+// These functions are provided as a convenience for logging, which is where we
+// use streams (it is against Google style to use streams in other places). It
+// is designed to allow you to emit non-ASCII Unicode strings to the log file,
+// which is normally ASCII. It is relatively slow, so try not to use it for
+// common cases. Non-ASCII characters will be converted to UTF-8 by these operators.
+std::ostream& operator<<(std::ostream& out, const wchar_t* wstr);
+inline std::ostream& operator<<(std::ostream& out, const std::wstring& wstr) {
+  return out << wstr.c_str();
+}
+
+#endif  // BASE_LOGGING_H__
diff --git a/base/md5.cc b/base/md5.cc
new file mode 100644
index 0000000..f2e1c4a
--- /dev/null
+++ b/base/md5.cc
@@ -0,0 +1,279 @@
+// The original file was copied from sqlite, and was in the public domain.
+// Modifications Copyright 2006 Google Inc. All Rights Reserved
+
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+#include <string>
+
+#include "base/md5.h"
+
+#include "base/basictypes.h"
+
+struct Context {
+  uint32 buf[4];
+  uint32 bits[2];
+  unsigned char in[64];
+};
+
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+static void byteReverse (unsigned char *buf, unsigned longs){
+        uint32 t;
+        do {
+                t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 |
+                            ((unsigned)buf[1]<<8 | buf[0]);
+                *(uint32 *)buf = t;
+                buf += 4;
+        } while (--longs);
+}
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+        ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.  MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void MD5Transform(uint32 buf[4], const uint32 in[16]){
+        register uint32 a, b, c, d;
+
+        a = buf[0];
+        b = buf[1];
+        c = buf[2];
+        d = buf[3];
+
+        MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478,  7);
+        MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12);
+        MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17);
+        MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22);
+        MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf,  7);
+        MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12);
+        MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17);
+        MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22);
+        MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8,  7);
+        MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12);
+        MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17);
+        MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22);
+        MD5STEP(F1, a, b, c, d, in[12]+0x6b901122,  7);
+        MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12);
+        MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17);
+        MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22);
+
+        MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562,  5);
+        MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340,  9);
+        MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14);
+        MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20);
+        MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d,  5);
+        MD5STEP(F2, d, a, b, c, in[10]+0x02441453,  9);
+        MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14);
+        MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20);
+        MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6,  5);
+        MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6,  9);
+        MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14);
+        MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20);
+        MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905,  5);
+        MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8,  9);
+        MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14);
+        MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20);
+
+        MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942,  4);
+        MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11);
+        MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16);
+        MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23);
+        MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44,  4);
+        MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11);
+        MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16);
+        MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23);
+        MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6,  4);
+        MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11);
+        MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16);
+        MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23);
+        MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039,  4);
+        MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11);
+        MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16);
+        MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23);
+
+        MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244,  6);
+        MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10);
+        MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15);
+        MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21);
+        MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3,  6);
+        MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10);
+        MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15);
+        MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21);
+        MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f,  6);
+        MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10);
+        MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15);
+        MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21);
+        MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82,  6);
+        MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10);
+        MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15);
+        MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21);
+
+        buf[0] += a;
+        buf[1] += b;
+        buf[2] += c;
+        buf[3] += d;
+}
+
+/*
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(MD5Context *pCtx){
+        struct Context *ctx = (struct Context *)pCtx;
+        ctx->buf[0] = 0x67452301;
+        ctx->buf[1] = 0xefcdab89;
+        ctx->buf[2] = 0x98badcfe;
+        ctx->buf[3] = 0x10325476;
+        ctx->bits[0] = 0;
+        ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(MD5Context *pCtx, const void *inbuf, size_t len){
+        struct Context *ctx = (struct Context *)pCtx;
+        const unsigned char* buf = (const unsigned char*)inbuf;
+        uint32 t;
+
+        /* Update bitcount */
+
+        t = ctx->bits[0];
+        if ((ctx->bits[0] = t + ((uint32)len << 3)) < t)
+                ctx->bits[1]++; /* Carry from low to high */
+        ctx->bits[1] += static_cast<uint32>(len >> 29);
+
+        t = (t >> 3) & 0x3f;    /* Bytes already in shsInfo->data */
+
+        /* Handle any leading odd-sized chunks */
+
+        if ( t ) {
+                unsigned char *p = (unsigned char *)ctx->in + t;
+
+                t = 64-t;
+                if (len < t) {
+                        memcpy(p, buf, len);
+                        return;
+                }
+                memcpy(p, buf, t);
+                byteReverse(ctx->in, 16);
+                MD5Transform(ctx->buf, (uint32 *)ctx->in);
+                buf += t;
+                len -= t;
+        }
+
+        /* Process data in 64-byte chunks */
+
+        while (len >= 64) {
+                memcpy(ctx->in, buf, 64);
+                byteReverse(ctx->in, 16);
+                MD5Transform(ctx->buf, (uint32 *)ctx->in);
+                buf += 64;
+                len -= 64;
+        }
+
+        /* Handle any remaining bytes of data. */
+
+        memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(MD5Digest* digest, MD5Context *pCtx){
+        struct Context *ctx = (struct Context *)pCtx;
+        unsigned count;
+        unsigned char *p;
+
+        /* Compute number of bytes mod 64 */
+        count = (ctx->bits[0] >> 3) & 0x3F;
+
+        /* Set the first char of padding to 0x80.  This is safe since there is
+           always at least one byte free */
+        p = ctx->in + count;
+        *p++ = 0x80;
+
+        /* Bytes of padding needed to make 64 bytes */
+        count = 64 - 1 - count;
+
+        /* Pad out to 56 mod 64 */
+        if (count < 8) {
+                /* Two lots of padding:  Pad the first block to 64 bytes */
+                memset(p, 0, count);
+                byteReverse(ctx->in, 16);
+                MD5Transform(ctx->buf, (uint32 *)ctx->in);
+
+                /* Now fill the next block with 56 bytes */
+                memset(ctx->in, 0, 56);
+        } else {
+                /* Pad block to 56 bytes */
+                memset(p, 0, count-8);
+        }
+        byteReverse(ctx->in, 14);
+
+        /* Append length in bits and transform */
+        ((uint32 *)ctx->in)[ 14 ] = ctx->bits[0];
+        ((uint32 *)ctx->in)[ 15 ] = ctx->bits[1];
+
+        MD5Transform(ctx->buf, (uint32 *)ctx->in);
+        byteReverse((unsigned char *)ctx->buf, 4);
+        memcpy(digest->a, ctx->buf, 16);
+        memset(ctx, 0, sizeof(ctx));    /* In case it's sensitive */
+}
+
+std::string MD5DigestToBase16(const MD5Digest& digest){
+  static char const zEncode[] = "0123456789abcdef";
+
+  std::string ret;
+  ret.resize(32);
+
+  int j = 0;
+  for(int i = 0; i < 16; i ++){
+    int a = digest.a[i];
+    ret[j++] = zEncode[(a>>4)&0xf];
+    ret[j++] = zEncode[a & 0xf];
+  }
+  return ret;
+}
+
+void MD5Sum(const void* data, size_t length, MD5Digest* digest) {
+  MD5Context ctx;
+  MD5Init(&ctx);
+  MD5Update(&ctx, static_cast<const unsigned char*>(data), length);
+  MD5Final(digest, &ctx);
+}
+
+std::string MD5String(const std::string& str) {
+  MD5Digest digest;
+  MD5Sum(str.data(), str.length(), &digest);
+  return MD5DigestToBase16(digest);
+}
diff --git a/base/md5.h b/base/md5.h
new file mode 100644
index 0000000..5fc6795
--- /dev/null
+++ b/base/md5.h
@@ -0,0 +1,82 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_MD5_H__
+#define BASE_MD5_H__
+
+#include <string>
+
+// These functions perform MD5 operations. The simplest call is MD5Sum to
+// generate the MD5 sum of the given data.
+//
+// You can also compute the MD5 sum of data incrementally by making multiple
+// calls to MD5Update:
+//    MD5Context ctx; // intermediate MD5 data: do not use
+//    MD5Init(&ctx);
+//    MD5Update(&ctx, data1, length1);
+//    MD5Update(&ctx, data2, length2);
+//    ...
+//
+//    MD5Digest digest; // the result of the computation
+//    MD5Final(&digest, &ctx);
+//
+// You can call MD5DigestToBase16 to generate a string of the digest.
+
+// The output of an MD5 operation
+typedef struct MD5Digest_struct {
+  unsigned char a[16];
+} MD5Digest;
+
+// Used for storing intermediate data during an MD5 computation. Callers
+// should not access the data.
+typedef char MD5Context[88];
+
+// Computes the MD5 sum of the given data buffer with the given length.
+// The given 'digest' structure will be filled with the result data.
+void MD5Sum(const void* data, size_t length, MD5Digest* digest);
+
+// Initializes the given MD5 context structure for subsequent calls to
+// MD5Update.
+void MD5Init(MD5Context* context);
+
+// For the given buffer of data, updates the given MD5 context with the sum of
+// the data. You can call this any number of times during the computation,
+// exept that MD5Init must have been called first.
+void MD5Update(MD5Context* context, const void* buf, size_t len);
+
+// Finalizes the MD5 operation and fills the buffer with the digest.
+void MD5Final(MD5Digest* digest, MD5Context* pCtx);
+
+// Converts a digest into human-readable hexadecimal.
+std::string MD5DigestToBase16(const MD5Digest& digest);
+
+// Returns the MD5 (in hexadecimal) of a string.
+std::string MD5String(const std::string& str);
+
+#endif // BASE_MD5_H__
diff --git a/base/memory_debug.cc b/base/memory_debug.cc
new file mode 100644
index 0000000..6acfe83
--- /dev/null
+++ b/base/memory_debug.cc
@@ -0,0 +1,79 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifdef PURIFY
+// this #define is used to prevent people from directly using pure.h
+// instead of memory_debug.h
+#define PURIFY_PRIVATE_INCLUDE
+#include "base/third_party/purify/pure.h"
+#endif
+
+#include "base/memory_debug.h"
+
+namespace base {
+
+bool MemoryDebug::memory_in_use_ = false;
+
+void MemoryDebug::SetMemoryInUseEnabled(bool enabled) {
+  memory_in_use_ = enabled;
+}
+
+void MemoryDebug::DumpAllMemoryInUse() {
+#ifdef PURIFY
+  if (memory_in_use_)
+    PurifyAllInuse();
+#endif
+}
+
+void MemoryDebug::DumpNewMemoryInUse() {
+#ifdef PURIFY
+  if (memory_in_use_)
+    PurifyNewInuse();
+#endif
+}
+
+void MemoryDebug::DumpAllLeaks() {
+#ifdef PURIFY
+  PurifyAllLeaks();
+#endif
+}
+
+void MemoryDebug::DumpNewLeaks() {
+#ifdef PURIFY
+  PurifyNewLeaks();
+#endif
+}
+
+void MemoryDebug::MarkAsInitialized(void* addr, size_t size) {
+#ifdef PURIFY
+  PurifyMarkAsInitialized(addr, size);
+#endif
+}
+
+} // namespace base
diff --git a/base/memory_debug.h b/base/memory_debug.h
new file mode 100644
index 0000000..c1283dc
--- /dev/null
+++ b/base/memory_debug.h
@@ -0,0 +1,66 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Functions used to debug memory usage, leaks, and other memory issues.
+// All methods are effectively no-ops unless this program is being run through
+// a supported memory tool (currently, only Purify)
+
+#ifndef BASE_MEMORY_DEBUG_H_
+
+namespace base {
+
+class MemoryDebug {
+public:
+  // Since MIU messages are a lot of data, and we don't always want this data,
+  // we have a global switch.  If disabled, *MemoryInUse are no-ops.
+  static void SetMemoryInUseEnabled(bool enabled);
+
+  // Dump information about all memory in use.
+  static void DumpAllMemoryInUse();
+  // Dump information about new memory in use since the last
+  // call to DumpAllMemoryInUse() or DumpNewMemoryInUse().
+  static void DumpNewMemoryInUse();
+
+  // Dump information about all current memory leaks.
+  static void DumpAllLeaks();
+  // Dump information about new memory leaks since the last
+  // call to DumpAllLeaks() or DumpNewLeaks()
+  static void DumpNewLeaks();
+
+  // Mark |size| bytes of memory as initialized, so it doesn't produce any UMRs
+  // or UMCs.
+  static void MarkAsInitialized(void* addr, size_t size);
+
+private:
+  static bool memory_in_use_;
+};
+
+} // namespace base
+
+#endif
\ No newline at end of file
diff --git a/base/message_loop.cc b/base/message_loop.cc
new file mode 100644
index 0000000..623ec99
--- /dev/null
+++ b/base/message_loop.cc
@@ -0,0 +1,969 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <algorithm>
+
+#include "base/message_loop.h"
+
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/thread_local_storage.h"
+#include "base/win_util.h"
+
+// a TLS index to the message loop for the current thread
+// Note that if we start doing complex stuff in other static initializers
+// this could cause problems.
+/*static*/ TLSSlot MessageLoop::tls_index_ = ThreadLocalStorage::Alloc();
+
+//------------------------------------------------------------------------------
+
+static const wchar_t kWndClass[] = L"Chrome_MessageLoopWindow";
+
+// Windows Message numbers handled by WindowMessageProc.
+
+// Message sent to get an additional time slice for pumping (processing) another
+// task (a series of such messages creates a continuous task pump).
+static const int kMsgPumpATask =        WM_USER + 1;
+
+// Message sent by Quit() to cause our main message pump to terminate as soon as
+// all pending task and message queues have been emptied.
+static const int kMsgQuit =             WM_USER + 2;
+
+// Logical events for Histogram profiling. Run with -message-loop-histogrammer
+// to get an accounting of messages and actions taken on each thread.
+static const int kTaskRunEvent =        WM_USER + 16;  // 0x411
+static const int kSleepingApcEvent =    WM_USER + 17;  // 0x411
+static const int kPollingSignalEvent =  WM_USER + 18;  // 0x412
+static const int kSleepingSignalEvent = WM_USER + 19;  // 0x413
+static const int kTimerEvent =          WM_USER + 20;  // 0x414
+
+// Provide range of message IDs for use in histogramming and debug display.
+static const int kLeastNonZeroMessageId = 1;
+static const int kMaxMessageId = 1099;
+static const int kNumberOfDistinctMessagesDisplayed = 1100;
+
+//------------------------------------------------------------------------------
+
+static LRESULT CALLBACK MessageLoopWndProc(HWND hwnd, UINT message,
+                                           WPARAM wparam, LPARAM lparam) {
+  switch (message) {
+    case kMsgQuit:
+    case kMsgPumpATask: {
+      UINT_PTR message_loop_id = static_cast<UINT_PTR>(wparam);
+      MessageLoop* current_message_loop =
+          reinterpret_cast<MessageLoop*>(message_loop_id);
+      DCHECK(MessageLoop::current() == current_message_loop);
+      return current_message_loop->MessageWndProc(hwnd, message, wparam,
+                                                  lparam);
+    }
+  }
+  return ::DefWindowProc(hwnd, message, wparam, lparam);
+}
+
+#ifndef NDEBUG
+// Force exercise of polling model.
+#define CHROME_MAXIMUM_WAIT_OBJECTS 8
+#else
+#define CHROME_MAXIMUM_WAIT_OBJECTS MAXIMUM_WAIT_OBJECTS
+#endif
+
+//------------------------------------------------------------------------------
+// A strategy of -1 uses the default case. All strategies are selected as
+// positive integers.
+// static
+int MessageLoop::strategy_selector_ = -1;
+
+// static
+void MessageLoop::SetStrategy(int strategy) {
+  DCHECK(-1 == strategy_selector_);
+  strategy_selector_ = strategy;
+}
+
+//------------------------------------------------------------------------------
+// Upon a SEH exception in this thread, it restores the original unhandled
+// exception filter.
+static int SEHFilter(LPTOP_LEVEL_EXCEPTION_FILTER old_filter) {
+  ::SetUnhandledExceptionFilter(old_filter);
+  return EXCEPTION_CONTINUE_SEARCH;
+}
+
+// Retrieves a pointer to the current unhandled exception filter. There
+// is no standalone getter method.
+static LPTOP_LEVEL_EXCEPTION_FILTER GetTopSEHFilter() {
+  LPTOP_LEVEL_EXCEPTION_FILTER top_filter = NULL;
+  top_filter = ::SetUnhandledExceptionFilter(0);
+  ::SetUnhandledExceptionFilter(top_filter);
+  return top_filter;
+}
+
+//------------------------------------------------------------------------------
+
+MessageLoop::MessageLoop() : message_hwnd_(NULL),
+                             exception_restoration_(false),
+                             nestable_tasks_allowed_(true),
+                             dispatcher_(NULL),
+                             quit_received_(false),
+                             quit_now_(false),
+                             task_pump_message_pending_(false),
+                             run_depth_(0) {
+  DCHECK(tls_index_) << "static initializer failed";
+  DCHECK(!current()) << "should only have one message loop per thread";
+  ThreadLocalStorage::Set(tls_index_, this);
+  InitMessageWnd();
+}
+
+MessageLoop::~MessageLoop() {
+  DCHECK(this == current());
+  ThreadLocalStorage::Set(tls_index_, NULL);
+  DCHECK(!dispatcher_);
+  DCHECK(!quit_received_ && !quit_now_);
+  // Most tasks that have not been Run() are deleted in the |timer_manager_|
+  // destructor after we remove our tls index.  We delete the tasks in our
+  // queues here so their destuction is similar to the tasks in the
+  // |timer_manager_|.
+  DeletePendingTasks();
+  ReloadWorkQueue();
+  DeletePendingTasks();
+}
+
+void MessageLoop::SetThreadName(const std::string& thread_name) {
+  DCHECK(thread_name_.empty());
+  thread_name_ = thread_name;
+  StartHistogrammer();
+}
+
+void MessageLoop::AddObserver(Observer *obs) {
+  DCHECK(this == current());
+  observers_.AddObserver(obs);
+}
+
+void MessageLoop::RemoveObserver(Observer *obs) {
+  DCHECK(this == current());
+  observers_.RemoveObserver(obs);
+}
+
+void MessageLoop::Run() {
+  Run(NULL);
+}
+
+// Runs the loop in two different SEH modes:
+// enable_SEH_restoration_ = false : any unhandled exception goes to the last
+// one that calls SetUnhandledExceptionFilter().
+// enable_SEH_restoration_ = true : any unhandled exception goes to the filter
+// that was existed before the loop was run.
+void MessageLoop::Run(Dispatcher* dispatcher) {
+  if (exception_restoration_) {
+    LPTOP_LEVEL_EXCEPTION_FILTER current_filter = GetTopSEHFilter();
+    __try {
+      RunInternal(dispatcher);
+    } __except(SEHFilter(current_filter)) {
+    }
+  } else {
+    RunInternal(dispatcher);
+  }
+}
+
+//------------------------------------------------------------------------------
+// Methods supporting various strategies for servicing the numerous queues.
+// IF this was just a simple PeekMessage() loop (servicing all passible work
+// queues), then Windows would try to achieve the following order according to
+// MSDN documentation about PeekMessage with no filter):
+//    * Sent messages
+//    * Posted messages
+//    * Sent messages (again)
+//    * WM_PAINT messages
+//    * WM_TIMER messages
+//
+// Summary: none of the above classes is starved, and sent messages has twice
+// the chance of being processed (i.e., reduced service time).
+
+void MessageLoop::RunInternal(Dispatcher* dispatcher) {
+  // Preserve ability to be called recursively.
+  ScopedStateSave save(this);  // State is restored on exit.
+  dispatcher_ = dispatcher;
+  StartHistogrammer();
+
+  DCHECK(this == current());
+  //
+  // Process all pending messages and signaled objects.
+  //
+  // Flush these queues before exiting due to a kMsgQuit or else we risk not
+  // shutting down properly as some operations may depend on further event
+  // processing. (Note: some tests may use quit_now_ to exit more swiftly,
+  // and leave messages pending, so don't assert the above fact).
+  //
+
+  RunTraditional();
+  DCHECK(quit_received_ || quit_now_);
+}
+
+typedef bool (MessageLoop::*ProcessingMethod)();
+typedef ProcessingMethod ProcessingMethods[];
+
+void MessageLoop::RunTraditional() {
+  run_depth_++;
+  for (;;) {
+    // If we do any work, we may create more messages etc., and more work
+    // may possibly be waiting in another task group.  In addition, each method
+    // call here typically limits work to 1 (worst case 2) items.  As a result,
+    // when we (for example) ProcessNextWindowsMessage() there is a good chance
+    // there are still more waiting (same thing for ProcessNextDeferredTask(),
+    // which responds to only one signaled object.).  On the other hand, when
+    // any of these methods return having done no work, then it is pretty
+    // unlikely that calling them again quickly will find any work to do.
+    // Finally, if they all say they had no work, then it is a good time to
+    // consider sleeping (waiting) for more work.
+    bool more_work_is_plausible = false;
+    more_work_is_plausible |= ProcessNextWindowsMessage();
+    if (quit_now_)
+      break;
+
+    more_work_is_plausible |= ProcessNextDeferredTask();
+    more_work_is_plausible |= ProcessNextObject();
+    if (more_work_is_plausible)
+      continue;
+
+    if (quit_received_)
+      break;
+
+    // Run any timer that is ready to run. It may create messages etc.
+    if (ProcessSomeTimers())
+      continue;
+
+    // We run delayed non nestable tasks only after all nestable tasks have
+    // run, to preserve FIFO ordering.
+    more_work_is_plausible = ProcessNextDelayedNonNestableTask();
+    if (more_work_is_plausible)
+      continue;
+
+    // We service APCs in WaitForWork, without returning.
+    WaitForWork();  // Wait (sleep) until we have work to do again.
+  }
+
+  run_depth_--;
+}
+
+bool MessageLoop::ProcessNextDelayedNonNestableTask() {
+  if (run_depth_ != 1)
+    return false;
+
+  if (delayed_non_nestable_queue_.Empty())
+    return false;
+
+  RunTask(delayed_non_nestable_queue_.Pop());
+  return true;
+}
+
+//------------------------------------------------------------------------------
+// Wrapper functions for use in above message loop frameworks.
+
+bool MessageLoop::ProcessNextDeferredTask() {
+  ReloadWorkQueue();
+  return QueueOrRunTask(NULL);
+}
+
+bool MessageLoop::ProcessSomeTimers() {
+  return timer_manager_.RunSomePendingTimers();
+}
+
+//------------------------------------------------------------------------------
+
+void MessageLoop::Quit() {
+  EnsureMessageGetsPosted(kMsgQuit);
+}
+
+bool MessageLoop::WatchObject(HANDLE object, Watcher* watcher) {
+  DCHECK(this == current());
+  DCHECK(object);
+  DCHECK_NE(object, INVALID_HANDLE_VALUE);
+
+  std::vector<HANDLE>::iterator it = find(objects_.begin(), objects_.end(),
+                                          object);
+  if (watcher) {
+    if (it == objects_.end()) {
+     static size_t warning_multiple = 1;
+     if (objects_.size() >= warning_multiple * MAXIMUM_WAIT_OBJECTS / 2) {
+       LOG(INFO) << "More than " << warning_multiple * MAXIMUM_WAIT_OBJECTS / 2
+           << " objects being watched";
+       // This DCHECK() is an artificial limitation, meant to warn us if we
+       // start creating too many objects.  It can safely be raised to a higher
+       // level, and the program is designed to handle much larger values.
+       // Before raising this limit, make sure that there is a very good reason
+       // (in your debug testing) to be watching this many objects.
+       DCHECK(2 <= warning_multiple);
+       ++warning_multiple;
+      }
+      objects_.push_back(object);
+      watchers_.push_back(watcher);
+    } else {
+      watchers_[it - objects_.begin()] = watcher;
+    }
+  } else if (it != objects_.end()) {
+    std::vector<HANDLE>::difference_type index = it - objects_.begin();
+    objects_.erase(it);
+    watchers_.erase(watchers_.begin() + index);
+  }
+  return true;
+}
+
+// Possibly called on a background thread!
+void MessageLoop::PostDelayedTask(const tracked_objects::Location& from_here,
+                                  Task* task, int delay_ms) {
+  task->SetBirthPlace(from_here);
+  DCHECK(delay_ms >= 0);
+  DCHECK(!task->is_owned_by_message_loop());
+  task->set_posted_task_delay(delay_ms);
+  DCHECK(task->is_owned_by_message_loop());
+  PostTaskInternal(task);
+}
+
+void MessageLoop::PostTaskInternal(Task* task) {
+  // Warning: Don't try to short-circuit, and handle this thread's tasks more
+  // directly, as it could starve handling of foreign threads.  Put every task
+  // into this queue.
+
+  // Local stack variables to use IF we need to process after releasing locks.
+  HWND message_hwnd;
+  {
+    AutoLock lock1(incoming_queue_lock_);
+    bool was_empty = incoming_queue_.Empty();
+    incoming_queue_.Push(task);
+    if (!was_empty)
+      return;  // Someone else should have started the sub-pump.
+
+    // We may have to start the sub-pump.
+    AutoLock lock2(task_pump_message_lock_);
+    if (task_pump_message_pending_)
+      return;  // Someone else continued the pumping.
+    task_pump_message_pending_ = true;  // We'll send one.
+    message_hwnd = message_hwnd_;
+  }  // Release both locks.
+  // We may have just posted a kMsgQuit, and so this instance may now destroyed!
+  // Do not invoke non-static methods, or members in any way!
+
+  // PostMessage may fail, as the hwnd may have vanished due to kMsgQuit.
+  PostMessage(message_hwnd, kMsgPumpATask, reinterpret_cast<UINT_PTR>(this), 0);
+}
+
+void MessageLoop::InitMessageWnd() {
+  HINSTANCE hinst = GetModuleHandle(NULL);
+
+  WNDCLASSEX wc = {0};
+  wc.cbSize = sizeof(wc);
+  wc.lpfnWndProc = MessageLoopWndProc;
+  wc.hInstance = hinst;
+  wc.lpszClassName = kWndClass;
+  RegisterClassEx(&wc);
+
+  message_hwnd_ = CreateWindow(kWndClass, 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0,
+                               hinst, 0);
+  DCHECK(message_hwnd_);
+}
+
+LRESULT MessageLoop::MessageWndProc(HWND hwnd, UINT message,
+                                    WPARAM wparam, LPARAM lparam) {
+  DCHECK(hwnd == message_hwnd_);
+  switch (message) {
+    case kMsgPumpATask: {
+      ProcessPumpReplacementMessage();  // Avoid starving paint and timer.
+      if (!nestable_tasks_allowed_)
+        return 0;
+      PumpATaskDuringWndProc();
+      return 0;
+    }
+
+    case kMsgQuit: {
+      quit_received_ = true;
+      return 0;
+    }
+  }
+  return ::DefWindowProc(hwnd, message, wparam, lparam);
+}
+
+void MessageLoop::WillProcessMessage(const MSG& msg) {
+  FOR_EACH_OBSERVER(Observer, observers_, WillProcessMessage(msg));
+}
+
+void MessageLoop::DidProcessMessage(const MSG& msg) {
+  FOR_EACH_OBSERVER(Observer, observers_, DidProcessMessage(msg));
+}
+
+void MessageLoop::SetNestableTasksAllowed(bool allowed) {
+  nestable_tasks_allowed_ = allowed;
+  if (!nestable_tasks_allowed_)
+    return;
+  // Start the native pump if we are not already pumping.
+  EnsurePumpATaskWasPosted();
+}
+
+bool MessageLoop::NestableTasksAllowed() const {
+  return nestable_tasks_allowed_;
+}
+
+
+bool MessageLoop::ProcessNextWindowsMessage() {
+  MSG msg;
+  if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+    return ProcessMessageHelper(msg);
+  }
+  return false;
+}
+
+bool MessageLoop::ProcessMessageHelper(const MSG& msg) {
+  HistogramEvent(msg.message);
+
+  if (WM_QUIT == msg.message) {
+    // Repost the QUIT message so that it will be retrieved by the primary
+    // GetMessage() loop.
+    quit_now_ = true;
+    PostQuitMessage(static_cast<int>(msg.wParam));
+    return false;
+  }
+
+  // While running our main message pump, we discard kMsgPumpATask messages.
+  if  (msg.message == kMsgPumpATask && msg.hwnd == message_hwnd_)
+    return ProcessPumpReplacementMessage();
+
+  WillProcessMessage(msg);
+
+  if (dispatcher_) {
+    if (!dispatcher_->Dispatch(msg))
+      quit_now_ = true;
+  } else {
+    TranslateMessage(&msg);
+    DispatchMessage(&msg);
+  }
+
+  DidProcessMessage(msg);
+  return true;
+}
+
+bool MessageLoop::ProcessPumpReplacementMessage() {
+  MSG msg;
+  bool have_message = (0 != PeekMessage(&msg, NULL, 0, 0, PM_REMOVE));
+  DCHECK(!have_message || kMsgPumpATask != msg.message
+         || msg.hwnd != message_hwnd_);
+  {
+    // Since we discarded a kMsgPumpATask message, we must update the flag.
+    AutoLock lock(task_pump_message_lock_);
+    DCHECK(task_pump_message_pending_);
+    task_pump_message_pending_ = false;
+  }
+  return have_message && ProcessMessageHelper(msg);
+}
+
+// Create a mini-message-pump to force immediate processing of only Windows
+// WM_PAINT messages.
+void MessageLoop::PumpOutPendingPaintMessages() {
+  // Don't provide an infinite loop, but do enough peeking to get the job done.
+  // Actual common max is 4 peeks, but we'll be a little safe here.
+  const int kMaxPeekCount = 20;
+  int peek_count;
+  bool win2k(true);
+  if (win_util::GetWinVersion() > win_util::WINVERSION_2000)
+    win2k = false;
+  for (peek_count = 0; peek_count < kMaxPeekCount; ++peek_count) {
+    MSG msg;
+    if (win2k) {
+      if (!PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE))
+        break;
+    } else {
+      if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_QS_PAINT))
+        break;
+    }
+    ProcessMessageHelper(msg);
+    if (quit_now_ )  // Handle WM_QUIT.
+      break;
+  }
+  // Histogram what was really being used, to help to adjust kMaxPeekCount.
+  DHISTOGRAM_COUNTS(L"Loop.PumpOutPendingPaintMessages Peeks", peek_count);
+}
+
+//------------------------------------------------------------------------------
+// If we handle more than the OS limit on the number of objects that can be
+// waited for, we'll need to poll (sequencing through subsets of the objects
+// that can be passed in a single OS wait call).  The following is the polling
+// interval used in that (unusual) case. (I don't have a lot of justifcation
+// for the specific value, but it needed to be short enough that it would not
+// add a lot of latency, and long enough that we wouldn't thrash the CPU for no
+// reason... especially considering the silly user probably has a million tabs
+// open, etc.)
+static const int kMultipleWaitPollingInterval = 20;
+
+void MessageLoop::WaitForWork() {
+  bool original_can_run = nestable_tasks_allowed_;
+  int wait_flags = original_can_run ? MWMO_ALERTABLE | MWMO_INPUTAVAILABLE
+                                    : MWMO_INPUTAVAILABLE;
+
+  bool use_polling = false;  // Poll if too many objects for one OS Wait call.
+  for (;;) {
+    // Do initialization here, in case APC modifies object list.
+    size_t total_objs = original_can_run ? objects_.size() : 0;
+
+    int delay;
+    size_t polling_index = 0;  // The first unprocessed object index.
+    do {
+      size_t objs_len =
+          (polling_index < total_objs) ? total_objs - polling_index : 0;
+      if (objs_len >= CHROME_MAXIMUM_WAIT_OBJECTS) {
+        objs_len = CHROME_MAXIMUM_WAIT_OBJECTS - 1;
+        use_polling = true;
+      }
+      HANDLE* objs = objs_len ? polling_index + &objects_.front() : NULL;
+
+      // Only wait up to the time needed by the timer manager to fire the next
+      // set of timers.
+      delay = timer_manager_.GetCurrentDelay();
+      if (use_polling && delay > kMultipleWaitPollingInterval)
+        delay = kMultipleWaitPollingInterval;
+      if (delay < 0)  // Negative value means no timers waiting.
+        delay = INFINITE;
+
+      DWORD result;
+      result = MsgWaitForMultipleObjectsEx(static_cast<DWORD>(objs_len), objs,
+                                           delay, QS_ALLINPUT, wait_flags);
+
+      if (WAIT_IO_COMPLETION == result) {
+        HistogramEvent(kSleepingApcEvent);
+        // We'll loop here when we service an APC.  At it currently stands,
+        // *ONLY* the IO thread uses *any* APCs, so this should have no impact
+        // on the UI thread.
+        break;  // Break to outer loop, and waitforwork() again.
+      }
+
+      // Use unsigned type to simplify range detection;
+      size_t signaled_index = result - WAIT_OBJECT_0;
+      if (signaled_index < objs_len) {
+        SignalWatcher(polling_index + signaled_index);
+        HistogramEvent(kSleepingSignalEvent);
+        return;  // We serviced a signaled object.
+      }
+
+      if (objs_len == signaled_index)
+        return;  // A WM_* message is available.
+
+      DCHECK_NE(WAIT_FAILED, result) << GetLastError();
+
+      DCHECK(!objs || result == WAIT_TIMEOUT);
+      if (!use_polling)
+        return;
+      polling_index += objs_len;
+    } while (polling_index < total_objs);
+    // For compatibility, we didn't return sooner.  This made us do *some* wait
+    // call(s) before returning. This will probably change in next rev.
+    if (!delay || !timer_manager_.GetCurrentDelay())
+      return;  // No work done, but timer is ready to fire.
+  }
+}
+
+// Note: MsgWaitMultipleObjects() can't take a nil list, and that is why I had
+// to use SleepEx() to handle APCs when there were no objects.
+bool MessageLoop::ProcessNextObject() {
+  if (!nestable_tasks_allowed_)
+    return false;
+
+  size_t total_objs = objects_.size();
+  if (!total_objs) {
+    return false;
+  }
+
+  size_t polling_index = 0;  // The first unprocessed object index.
+  do {
+    DCHECK(polling_index < total_objs);
+    size_t objs_len = total_objs - polling_index;
+    if (objs_len >= CHROME_MAXIMUM_WAIT_OBJECTS)
+      objs_len = CHROME_MAXIMUM_WAIT_OBJECTS - 1;
+    HANDLE* objs = polling_index + &objects_.front();
+
+    // Identify 1 pending object, or allow an IO APC to be completed.
+    DWORD result = WaitForMultipleObjectsEx(static_cast<DWORD>(objs_len), objs,
+                                            FALSE,    // 1 signal is sufficient.
+                                            0,        // Wait 0ms.
+                                            false);  // Not alertable (no APC).
+
+    // Use unsigned type to simplify range detection;
+    size_t signaled_index = result - WAIT_OBJECT_0;
+    if (signaled_index < objs_len) {
+      SignalWatcher(polling_index + signaled_index);
+      HistogramEvent(kPollingSignalEvent);
+      return true;  // We serviced a signaled object.
+    }
+
+    // If an handle is invalid, it will be WAIT_FAILED.
+    DCHECK_EQ(WAIT_TIMEOUT, result) << GetLastError();
+    polling_index += objs_len;
+  } while (polling_index < total_objs);
+  return false;  // We serviced nothing.
+}
+
+bool MessageLoop::SignalWatcher(size_t object_index) {
+  BeforeTaskRunSetup();
+  DCHECK(objects_.size() > object_index);
+  // On reception of OnObjectSignaled() to a Watcher object, it may call
+  // WatchObject(). watchers_ and objects_ will be modified. This is
+  // expected, so don't be afraid if, while tracing a OnObjectSignaled()
+  // function, the corresponding watchers_[result] is inexistant.
+  watchers_[object_index]->OnObjectSignaled(objects_[object_index]);
+  // Signaled objects tend to be removed from the watch list, and then added
+  // back (appended).  As a result, they move to the end of the objects_ array,
+  // and this should make their service "fair" (no HANDLEs should be starved).
+  AfterTaskRunRestore();
+  return true;
+}
+
+bool MessageLoop::RunTimerTask(Timer* timer) {
+  HistogramEvent(kTimerEvent);
+  Task* task = timer->task();
+  if (task->is_owned_by_message_loop()) {
+    // We constructed it through PostTask().
+    DCHECK(!timer->repeating());
+    timer->set_task(NULL);
+    delete timer;
+    return QueueOrRunTask(task);
+  } else {
+    // This is an unknown timer task, and we *can't* delay running it, as a
+    // user might try to cancel it with TimerManager at any moment.
+    DCHECK(nestable_tasks_allowed_);
+    RunTask(task);
+    return true;
+  }
+}
+
+void MessageLoop::DiscardTimer(Timer* timer) {
+  Task* task = timer->task();
+  if (task->is_owned_by_message_loop()) {
+    DCHECK(!timer->repeating());
+    timer->set_task(NULL);
+    delete timer;  // We constructed it through PostDelayedTask().
+    delete task;  // We were given ouwnership in PostTask().
+  }
+}
+
+bool MessageLoop::QueueOrRunTask(Task* new_task) {
+  if (!nestable_tasks_allowed_) {
+    // Task can't be executed right now. Add it to the queue.
+    if (new_task)
+      work_queue_.Push(new_task);
+    return false;
+  }
+
+  // Queue new_task first so we execute the task in FIFO order.
+  if (new_task)
+    work_queue_.Push(new_task);
+
+  // Execute oldest task.
+  while (!work_queue_.Empty()) {
+    Task* task = work_queue_.Pop();
+    if (task->nestable() || run_depth_ == 1) {
+      RunTask(task);
+      // Show that we ran a task (Note: a new one might arrive as a
+      // consequence!).
+      return true;
+    } else {
+      // We couldn't run the task now because we're in a nested message loop
+      // and the task isn't nestable.
+      delayed_non_nestable_queue_.Push(task);
+    }
+  }
+
+  // Nothing happened.
+  return false;
+}
+
+void MessageLoop::RunTask(Task* task) {
+  BeforeTaskRunSetup();
+  HistogramEvent(kTaskRunEvent);
+  // task may self-delete during Run() if we don't happen to own it.
+  // ...so check *before* we Run, since we can't check after.
+  bool we_own_task = task->is_owned_by_message_loop();
+  task->Run();
+  if (we_own_task)
+    task->RecycleOrDelete();  // Relinquish control, and probably delete.
+  AfterTaskRunRestore();
+}
+
+void MessageLoop::BeforeTaskRunSetup() {
+  DCHECK(nestable_tasks_allowed_);
+  // Execute the task and assume the worst: It is probably not reentrant.
+  nestable_tasks_allowed_ = false;
+}
+
+void MessageLoop::AfterTaskRunRestore() {
+  nestable_tasks_allowed_ = true;
+}
+
+void MessageLoop::PumpATaskDuringWndProc() {
+  // TODO(jar): Perchance we should check on signaled objects here??
+  // Signals are generally starved during a native message loop.  Even if we
+  // try to service a signaled object now, we wouldn't automatically get here
+  // (i.e., the native pump would not re-start) when the next object was
+  // signaled. If we really want to avoid starving signaled objects, we need
+  // to translate them into Tasks that can be passed in via PostTask.
+  // If these native message loops (and sub-pumping activities) are short
+  // lived, then the starvation won't be that long :-/.
+
+  if (!ProcessNextDeferredTask())
+    return;  // Nothing to do, so lets stop the sub-pump.
+
+  // We ran a task, so make sure we come back and try to run more tasks.
+  EnsurePumpATaskWasPosted();
+}
+
+void MessageLoop::EnsurePumpATaskWasPosted() {
+  {
+    AutoLock lock(task_pump_message_lock_);
+    if (task_pump_message_pending_)
+      return;  // Someone else continued the pumping.
+    task_pump_message_pending_ = true;  // We'll send one.
+  }
+  EnsureMessageGetsPosted(kMsgPumpATask);
+}
+
+void MessageLoop::EnsureMessageGetsPosted(int message) const {
+  const int kRetryCount = 30;
+  const int kSleepDurationWhenFailing = 100;
+  for (int i = 0; i < kRetryCount; ++i) {
+    // Posting to our own windows should always succeed.  If it doesn't we're in
+    // big trouble.
+    if (PostMessage(message_hwnd_, message,
+                    reinterpret_cast<UINT_PTR>(this), 0))
+      return;
+    Sleep(kSleepDurationWhenFailing);
+  }
+  LOG(FATAL) << "Crash with last error " << GetLastError();
+  int* p = NULL;
+  *p = 0;  // Crash.
+}
+
+void MessageLoop::ReloadWorkQueue() {
+  // We can improve performance of our loading tasks from incoming_queue_ to
+  // work_queue_ by wating until the last minute (work_queue_ is empty) to load.
+  // That reduces the number of locks-per-task significantly when our queues get
+  // large.  The optimization is disabled on threads that make use of the
+  // priority queue (prioritization requires all our tasks to be in the
+  // work_queue_ ASAP).
+  if (!work_queue_.Empty() && !work_queue_.use_priority_queue())
+    return;  // Wait till we *really* need to lock and load.
+
+  // Acquire all we can from the inter-thread queue with one lock acquisition.
+  TaskQueue new_task_list;  // Null terminated list.
+  {
+    AutoLock lock(incoming_queue_lock_);
+    if (incoming_queue_.Empty())
+      return;
+    std::swap(incoming_queue_, new_task_list);
+    DCHECK(incoming_queue_.Empty());
+  }  // Release lock.
+
+  while (!new_task_list.Empty()) {
+    Task* task = new_task_list.Pop();
+    DCHECK(task->is_owned_by_message_loop());
+
+    if (task->posted_task_delay() > 0)
+      timer_manager_.StartTimer(task->posted_task_delay(), task, false);
+    else
+      work_queue_.Push(task);
+  }
+}
+
+void MessageLoop::DeletePendingTasks() {
+  /* Comment this out as it's causing crashes.
+  while (!work_queue_.Empty()) {
+    Task* task = work_queue_.Pop();
+    if (task->is_owned_by_message_loop())
+      delete task;
+  }
+
+  while (!delayed_non_nestable_queue_.Empty()) {
+    Task* task = delayed_non_nestable_queue_.Pop();
+    if (task->is_owned_by_message_loop())
+      delete task;
+  }
+  */
+}
+
+//------------------------------------------------------------------------------
+// Implementation of the work_queue_ as a ProiritizedTaskQueue
+
+void MessageLoop::PrioritizedTaskQueue::push(Task * task) {
+  queue_.push(PrioritizedTask(task, --next_sequence_number_));
+}
+
+bool MessageLoop::PrioritizedTaskQueue::PrioritizedTask::operator < (
+    PrioritizedTask const & right) const {
+  int compare = task_->priority_ - right.task_->priority_;
+  if (compare)
+    return compare < 0;
+  // Don't compare directly, but rather subtract.  This handles overflow
+  // as sequence numbers wrap around.
+  compare = sequence_number_ - right.sequence_number_;
+  DCHECK(compare);  // Sequence number are unique for a "long time."
+  // Make sure we don't starve anything with a low priority.
+  CHECK(INT_MAX/8 > compare);  // We don't get close to wrapping.
+  CHECK(INT_MIN/8 < compare);  // We don't get close to wrapping.
+  return compare < 0;
+}
+
+//------------------------------------------------------------------------------
+// Implementation of a TaskQueue as a null terminated list, with end pointers.
+
+void MessageLoop::TaskQueue::Push(Task* task) {
+  if (!first_)
+    first_ = task;
+  else
+    last_->set_next_task(task);
+  last_ = task;
+}
+
+Task* MessageLoop::TaskQueue::Pop() {
+  DCHECK((!first_) == !last_);
+  Task* task = first_;
+  if (first_) {
+    first_ = task->next_task();
+    if (!first_)
+      last_ = NULL;
+    else
+      task->set_next_task(NULL);
+  }
+  return task;
+}
+
+//------------------------------------------------------------------------------
+// Implementation of a Task queue that automatically switches into a priority
+// queue if it observes any non-zero priorities on tasks.
+
+void MessageLoop::OptionallyPrioritizedTaskQueue::Push(Task* task) {
+  if (use_priority_queue_) {
+    prioritized_queue_.push(task);
+  } else {
+    queue_.Push(task);
+    if (task->priority()) {
+      use_priority_queue_ = true;  // From now on.
+      while (!queue_.Empty())
+        prioritized_queue_.push(queue_.Pop());
+    }
+  }
+}
+
+Task* MessageLoop::OptionallyPrioritizedTaskQueue::Pop() {
+  if (!use_priority_queue_)
+    return queue_.Pop();
+  Task* task = prioritized_queue_.front();
+  prioritized_queue_.pop();
+  return task;
+}
+
+bool MessageLoop::OptionallyPrioritizedTaskQueue::Empty() {
+  if (use_priority_queue_)
+    return prioritized_queue_.empty();
+  return queue_.Empty();
+}
+
+//------------------------------------------------------------------------------
+// Method and data for histogramming events and actions taken by each instance
+// on each thread.
+
+// static
+bool MessageLoop::enable_histogrammer_ = false;
+
+// static
+void MessageLoop::EnableHistogrammer(bool enable) {
+  enable_histogrammer_ = enable;
+}
+
+void MessageLoop::StartHistogrammer() {
+  if (enable_histogrammer_ && !message_histogram_.get()
+      && StatisticsRecorder::WasStarted()) {
+    message_histogram_.reset(new LinearHistogram(
+        ASCIIToWide("MsgLoop:" + thread_name_).c_str(),
+                    kLeastNonZeroMessageId,
+                    kMaxMessageId,
+                    kNumberOfDistinctMessagesDisplayed));
+    message_histogram_->SetFlags(message_histogram_->kHexRangePrintingFlag);
+    message_histogram_->SetRangeDescriptions(event_descriptions_);
+  }
+}
+
+void MessageLoop::HistogramEvent(int event) {
+  if (message_histogram_.get())
+    message_histogram_->Add(event);
+}
+
+// Add one undocumented windows message to clean up our display.
+#ifndef WM_SYSTIMER
+#define WM_SYSTIMER 0x118
+#endif
+
+// Provide a macro that takes an expression (such as a constant, or macro
+// constant) and creates a pair to initalize an array of pairs.  In this case,
+// our pair consists of the expressions value, and the "stringized" version
+// of the expression (i.e., the exrpression put in quotes).  For example, if
+// we have:
+//    #define FOO 2
+//    #define BAR 5
+// then the following:
+//    VALUE_TO_NUMBER_AND_NAME(FOO + BAR)
+// will expand to:
+//   {7, "FOO + BAR"}
+// We use the resulting array as an argument to our histogram, which reads the
+// number as a bucket identifier, and proceeds to use the corresponding name
+// in the pair (i.e., the quoted string) when printing out a histogram.
+#define VALUE_TO_NUMBER_AND_NAME(name) {name, #name},
+
+
+// static
+const LinearHistogram::DescriptionPair MessageLoop::event_descriptions_[] = {
+  // Only provide an extensive list in debug mode.  In release mode, we have to
+  // read the octal values.... but we save about 450 strings, each of length
+  // 10 from our binary image.
+#ifndef NDEBUG
+  // Prepare to include a list of names provided in a special header file4.
+#define A_NAMED_MESSAGE_FROM_WINUSER_H VALUE_TO_NUMBER_AND_NAME
+#include "base/windows_message_list.h"
+#undef A_NAMED_MESSAGE_FROM_WINUSER_H
+  // Add an undocumented message that appeared in our list :-/.
+  VALUE_TO_NUMBER_AND_NAME(WM_SYSTIMER)
+#endif  // NDEBUG
+
+  // Provide some pretty print capability in our histogram for our internal
+  // messages.
+
+  // Values we use for WM_USER+n
+  VALUE_TO_NUMBER_AND_NAME(kMsgPumpATask)
+  VALUE_TO_NUMBER_AND_NAME(kMsgQuit)
+
+  // A few events we handle (kindred to messages), and used to profile actions.
+  VALUE_TO_NUMBER_AND_NAME(kTaskRunEvent)
+  VALUE_TO_NUMBER_AND_NAME(kSleepingApcEvent)
+  VALUE_TO_NUMBER_AND_NAME(kSleepingSignalEvent)
+  VALUE_TO_NUMBER_AND_NAME(kPollingSignalEvent)
+  VALUE_TO_NUMBER_AND_NAME(kTimerEvent)
+
+  {-1, NULL}  // The list must be null terminated, per API to histogram.
+};
diff --git a/base/message_loop.h b/base/message_loop.h
new file mode 100644
index 0000000..6bb7395
--- /dev/null
+++ b/base/message_loop.h
@@ -0,0 +1,611 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_MESSAGE_LOOP_H__
+#define BASE_MESSAGE_LOOP_H__
+
+#include <windows.h>
+#include <deque>
+#include <queue>
+#include <string>
+#include <vector>
+
+#include "base/histogram.h"
+#include "base/observer_list.h"
+#include "base/id_map.h"
+#include "base/task.h"
+#include "base/timer.h"
+#include "base/thread_local_storage.h"
+
+//
+// A MessageLoop is used to process events for a particular thread.
+// There is at most one MessageLoop instance per thread.
+// Events include Windows Message Queue messages, Tasks submitted to PostTask
+// or managed by TimerManager, APC calls (as time permits), and signals sent to
+// a registered set of HANDLES.
+// Processing events corresponds (respectively) to dispatching Windows messages,
+// running Tasks, yielding time to APCs, and calling Watchers when the
+// corresponding HANDLE is signaled.
+
+//
+// NOTE: Unless otherwise specified, a MessageLoop's methods may only be called
+// on the thread where the MessageLoop's Run method executes.
+//
+// WARNING: MessageLoop has task reentrancy protection. This means that if a
+// task is being processed, a second task cannot start until the first task is
+// finished. Reentrancy can happen when processing a task, and an inner message
+// pump is created.  That inner pump then processes windows messages which could
+// implicitly start an inner task. Inner messages pumps are created with dialogs
+// (DialogBox), common dialogs (GetOpenFileName), OLE functions (DoDragDrop),
+// printer functions (StartDoc) and *many* others.
+// Sample workaround when inner task processing is needed:
+//   bool old_state = MessageLoop::current()->NestableTasksAllowed();
+//   MessageLoop::current()->SetNestableTasksAllowed(true);
+//   HRESULT hr = DoDragDrop(...); // Implicitly runs a modal message loop here.
+//   MessageLoop::current()->SetNestableTasksAllowed(old_state);
+//   // Process hr  (the result returned by DoDragDrop().
+//
+// Please be **SURE** your task is reentrant and all global variables are stable
+// and accessible before calling SetNestableTasksAllowed(true).
+//
+
+// Message loop has several distinct functions.  It provides message pumps,
+// responds to windows message dispatches, manipulates queues of Tasks.
+// The most central operation is the implementation of message pumps, along with
+// several subtleties.
+
+// MessageLoop currently implements several different message pumps.  A message
+// pump is (traditionally) something that reads from an incoming queue, and then
+// dispatches the work.
+//
+// The first message pump, RunTraditional(), is among other things a
+// traditional Windows Message pump.  It contains a nearly infinite loop that
+// peeks out messages, and then dispatches them.
+// Intermixed with those peeks are checks on a queue of Tasks, checks for
+// signaled objects, and checks to see if TimerManager has tasks to run.
+// When there are no events to be serviced, this pump goes into a wait state.
+// For 99.99% of all events, this first message pump handles all processing.
+//
+// When a task, or windows event, invokes on the stack a native dialog box or
+// such, that window typically provides a bare bones (native?) message pump.
+// That bare-bones message pump generally supports little more than a peek of
+// the Windows message queue, followed by a dispatch of the peeked message.
+// MessageLoop extends that bare-bones message pump to also service Tasks, at
+// the cost of some complexity.
+// The basic structure of the extension (refered to as a sub-pump) is that a
+// special message,kMsgPumpATask, is repeatedly injected into the Windows
+// Message queue. Each time the kMsgPumpATask message is peeked, checks are made
+// for an extended set of events, including the availability of Tasks to run.
+//
+// After running a task, the special message kMsgPumpATask is again posted to
+// the Windows Message queue, ensuring a future time slice for processing a
+// future event.
+//
+// To prevent flooding the Windows Message queue, care is taken to be sure that
+// at most one kMsgPumpATask message is EVER pending in the Winow's Message
+// queue.
+//
+// There are a few additional complexities in this system where, when there are
+// no Tasks to run, this otherwise infinite stream of messages which drives the
+// sub-pump is halted.  The pump is automatically re-started when Tasks are
+// queued.
+//
+// A second complexity is that the presence of this stream of posted tasks may
+// prevent a bare-bones message pump from ever peeking a WM_PAINT or WM_TIMER.
+// Such paint and timer events always give priority to a posted message, such as
+// kMsgPumpATask messages.  As a result, care is taken to do some peeking in
+// between the posting of each kMsgPumpATask message (i.e., after kMsgPumpATask
+// is peeked, and before a replacement kMsgPumpATask is posted).
+//
+//
+// NOTE: Although it may seem odd that messages are used to start and stop this
+// flow (as opposed to signaling objects, etc.), it should be understood that
+// the native message pump will *only* respond to messages.  As a result, it is
+// an excellent choice.  It is also helpful that the starter messages that are
+// placed in the queue when new task arrive also awakens the RunTraditional()
+// loop.
+
+//------------------------------------------------------------------------------
+// Define a macro to record where (in the sourec code) each Task is posted from.
+#define FROM_HERE tracked_objects::Location(__FUNCTION__, __FILE__, __LINE__)
+
+//------------------------------------------------------------------------------
+class MessageLoop {
+ public:
+
+  // Select a non-default strategy for serving pending requests, that is to be
+  // used by all MessageLoop instances.  This is called only once before
+  // constructing any instances.
+  static void SetStrategy(int strategy);
+  static void EnableHistogrammer(bool enable_histogrammer);
+
+  // Used with WatchObject to asynchronously monitor the signaled state of a
+  // HANDLE object.
+  class Watcher {
+   public:
+    virtual ~Watcher() {}
+    // Called from MessageLoop::Run when a signalled object is detected.
+    virtual void OnObjectSignaled(HANDLE object) = 0;
+  };
+
+  // Dispatcher is used during a nested invocation of Run to dispatch events.
+  // If Run is invoked with a non-NULL Dispatcher, MessageLoop does not
+  // dispatch events (or invoke TranslateMessage), rather every message is
+  // passed to Dispatcher's Dispatch method for dispatch. It is up to the
+  // Dispatcher to dispatch, or not, the event.
+  //
+  // The nested loop is exited by either posting a quit, or returning false
+  // from Dispatch.
+  class Dispatcher {
+   public:
+    // Define a macro for use in the PostTask() or PostDelayedTask()
+    // invocations.  The definition varies depending upon mode (DEBUG, etc.),
+    // but for now we'll just define it as an int.  In other modes it may
+    // encapsulate the file and line number of the source code where it is
+    // expanded.
+
+    virtual ~Dispatcher() {}
+    // Dispatches the event. If true is returned processing continues as
+    // normal. If false is returned, the nested loop exits immediately.
+    virtual bool Dispatch(const MSG& msg) = 0;
+  };
+
+  // Have the current thread's message loop watch for a signaled object.
+  // Pass a null watcher to stop watching the object.
+  bool WatchObject(HANDLE, Watcher*);
+
+  // An Observer is an object that receives global notifications from the
+  // MessageLoop.
+  //
+  // NOTE: An Observer implementation should be extremely fast!
+  //
+  class Observer {
+   public:
+    virtual ~Observer() {}
+
+    // This method is called before processing a message.
+    // The message may be undefined in which case msg.message is 0
+    virtual void WillProcessMessage(const MSG& msg) = 0;
+
+    // This method is called when control returns from processing a UI message.
+    // The message may be undefined in which case msg.message is 0
+    virtual void DidProcessMessage(const MSG& msg) = 0;
+  };
+
+  // Add an Observer, which will start receiving notifications immediately.
+  void AddObserver(Observer* observer);
+
+  // Remove an Observer.  It is safe to call this method while an Observer is
+  // receiving a notification callback.
+  void RemoveObserver(Observer* observer);
+
+  // Call the task's Run method asynchronously from within a message loop at
+  // some point in the future.  With the PostTask variant, tasks are invoked in
+  // FIFO order, inter-mixed with normal UI event processing.  With the
+  // PostDelayedTask variant, tasks are called after at least approximately
+  // 'delay_ms' have elapsed.
+  //
+  // The MessageLoop takes ownership of the Task, and deletes it after it
+  // has been Run().
+  //
+  // NOTE: This method may be called on any thread.  The Task will be invoked
+  // on the thread that executes MessageLoop::Run().
+
+  void PostTask(const tracked_objects::Location& from_here, Task* task) {
+    PostDelayedTask(from_here, task, 0);
+  }
+
+  void PostDelayedTask(const tracked_objects::Location& from_here, Task* task,
+                       int delay_ms);
+
+  // A variant on PostTask that deletes the given object.  This is useful
+  // if the object needs to live until the next run of the MessageLoop (for
+  // example, deleting a RenderProcessHost from within an IPC callback is not
+  // good).
+  //
+  // NOTE: This method may be called on any thread.  The object will be deleted
+  // on the thread that executes MessageLoop::Run().  If this is not the same
+  // as the thread that calls PostDelayedTask(FROM_HERE, ), then T MUST inherit
+  // from RefCountedThreadSafe<T>!
+  template <class T>
+  void DeleteSoon(const tracked_objects::Location& from_here, T* object) {
+    PostTask(from_here, new DeleteTask<T>(object));
+  }
+
+  // A variant on PostTask that releases the given reference counted object
+  // (by calling its Release method).  This is useful if the object needs to
+  // live until the next run of the MessageLoop, or if the object needs to be
+  // released on a particular thread.
+  //
+  // NOTE: This method may be called on any thread.  The object will be
+  // released (and thus possibly deleted) on the thread that executes
+  // MessageLoop::Run().  If this is not the same as the thread that calls
+  // PostDelayedTask(FROM_HERE, ), then T MUST inherit from
+  // RefCountedThreadSafe<T>!
+  template <class T>
+  void ReleaseSoon(const tracked_objects::Location& from_here, T* object) {
+    PostTask(from_here, new ReleaseTask<T>(object));
+  }
+
+  // Run the message loop
+  void Run();
+
+  // See description of Dispatcher for how Run uses Dispatcher.
+  void Run(Dispatcher* dispatcher);
+
+  // Signals the Run method to return after it is done processing all pending
+  // messages.  This method may be called from any thread, but no effort is
+  // made to support concurrent calls to this method from multiple threads.
+  //
+  // For example, the first call to Quit may lead to the MessageLoop being
+  // deleted once its Run method returns, so a second call from another thread
+  // could be problematic.
+  void Quit();
+
+  // Invokes Quit on the current MessageLoop when run. Useful to schedule an
+  // arbitrary MessageLoop to Quit.
+  class QuitTask : public Task {
+   public:
+    virtual void Run() {
+      MessageLoop::current()->Quit();
+    }
+  };
+
+  // Wnd Proc for message_hwnd_.
+  LRESULT MessageWndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
+
+  // Normally, it is not necessary to instantiate a MessageLoop.  Instead, it
+  // is typical to make use of the current thread's MessageLoop instance.
+  MessageLoop();
+  ~MessageLoop();
+
+  // Optional call to connect the thread name with this loop.
+  void SetThreadName(const std::string& thread_name);
+  std::string thread_name() const { return thread_name_; }
+
+  // Returns the MessageLoop object for the current thread, or null if none.
+  static MessageLoop* current() {
+    return static_cast<MessageLoop*>(ThreadLocalStorage::Get(tls_index_));
+  }
+
+  // Returns the TimerManager object for the current thread.
+  TimerManager* timer_manager() { return &timer_manager_; }
+
+  // Give a chance to code processing additional messages to notify the
+  // message loop delegates that another message has been processed.
+  void WillProcessMessage(const MSG& msg);
+  void DidProcessMessage(const MSG& msg);
+
+  // Enables or disables the recursive task processing. This happens in the case
+  // of recursive message loops. Some unwanted message loop may occurs when
+  // using common controls or printer functions. By default, recursive task
+  // processing is disabled.
+  //
+  // The specific case where tasks get queued is:
+  // - The thread is running a message loop.
+  // - It receives a task #1 and execute it.
+  // - The task #1 implicitly start a message loop, like a MessageBox in the
+  //   unit test. This can also be StartDoc or GetSaveFileName.
+  // - The thread receives a task #2 before or while in this second message
+  //   loop.
+  // - With NestableTasksAllowed set to true, the task #2 will run right away.
+  //   Otherwise, it will get executed right after task #1 completes at "thread
+  //   message loop level".
+  void SetNestableTasksAllowed(bool allowed);
+  bool NestableTasksAllowed() const;
+
+  // Enables or disables the restoration during an exception of the unhandled
+  // exception filter that was active when Run() was called. This can happen
+  // if some third party code call SetUnhandledExceptionFilter() and never
+  // restores the previous filter.
+  void set_exception_restoration(bool restore) {
+    exception_restoration_ = restore;
+  }
+
+  // Public entry point for TimerManager to request the Run() of a task.  If we
+  // created the task during an PostTask(FROM_HERE, ), then we will also perform
+  // destructions, and we'll have the option of queueing the task.  If we didn't
+  // create the timer, then we will Run it immediately.
+  bool RunTimerTask(Timer* timer);
+
+  // Since some Timer's are owned by MessageLoop, the TimerManager (when it is
+  // being destructed) passses us the timers to discard (without doing a Run()).
+  void DiscardTimer(Timer* timer);
+
+  // Applications can call this to encourage us to process all pending WM_PAINT
+  // messages.
+  // This method will process all paint messages the Windows Message queue can
+  // provide, up to some fixed number (to avoid any infinite loops).
+  void PumpOutPendingPaintMessages();
+
+  //----------------------------------------------------------------------------
+ private:
+  struct ScopedStateSave {
+    explicit ScopedStateSave(MessageLoop* loop)
+        : loop_(loop),
+          dispatcher_(loop->dispatcher_),
+          quit_now_(loop->quit_now_),
+          quit_received_(loop->quit_received_) {
+      loop->quit_now_ = loop->quit_received_ = false;
+    }
+
+    ~ScopedStateSave() {
+      loop_->quit_received_ = quit_received_;
+      loop_->quit_now_ = quit_now_;
+      loop_->dispatcher_ = dispatcher_;
+    }
+
+   private:
+    MessageLoop* loop_;
+    Dispatcher* dispatcher_;
+    bool quit_now_;
+    bool quit_received_;
+  };  // struct ScopedStateSave
+
+  // A prioritized queue with interface that mostly matches std::queue<>.
+  // For debugging/performance testing, you can swap in std::queue<Task*>.
+  class PrioritizedTaskQueue {
+   public:
+    PrioritizedTaskQueue() : next_sequence_number_(0) {}
+    ~PrioritizedTaskQueue() {}
+    void pop()    { queue_.pop(); }
+    bool empty()  { return queue_.empty(); }
+    size_t size() { return queue_.size(); }
+    Task* front() { return queue_.top().task(); }
+    void push(Task * task);
+
+   private:
+    class PrioritizedTask {
+     public:
+      PrioritizedTask(Task* task, int sequence_number)
+        : task_(task),
+          sequence_number_(sequence_number),
+          priority_(task->priority()) {}
+      Task* task() { return task_; }
+      bool operator < (PrioritizedTask const & right) const ;
+
+     private:
+      Task* task_;
+      // Number to ensure (default) FIFO ordering in a PriorityQueue.
+      int sequence_number_;
+      // Priority of task when pushed.
+      int priority_;
+    };  // class PrioritizedTask
+
+    std::priority_queue<PrioritizedTask> queue_;
+    // Default sequence number used when push'ing (monotonically decreasing).
+    int next_sequence_number_;
+    DISALLOW_EVIL_CONSTRUCTORS(PrioritizedTaskQueue);
+  };
+
+  // Implementation of a TaskQueue as a null terminated list, with end pointers.
+  class TaskQueue {
+   public:
+    TaskQueue() : first_(NULL), last_(NULL) {}
+    void Push(Task* task);
+    Task* Pop();  // Extract the next Task from the queue, and return it.
+    bool Empty() const { return !first_; }
+    friend void std::swap<TaskQueue>(TaskQueue&, TaskQueue&);
+   private:
+    Task* first_;
+    Task* last_;
+  };
+
+  // Implementation of a Task queue that automatically switches into a priority
+  // queue if it observes any non-zero priorities in tasks.
+  class OptionallyPrioritizedTaskQueue {
+   public:
+    OptionallyPrioritizedTaskQueue() : use_priority_queue_(false) {}
+    void Push(Task* task);
+    Task* Pop();  // Extract next Task from queue, and return it.
+    bool Empty();
+    bool use_priority_queue() const { return use_priority_queue_; }
+
+   private:
+    bool use_priority_queue_;
+    PrioritizedTaskQueue prioritized_queue_;
+    TaskQueue queue_;
+    DISALLOW_EVIL_CONSTRUCTORS(OptionallyPrioritizedTaskQueue);
+  };
+
+  void InitMessageWnd();
+
+  // The actual message loop implementation. Called by all flavors of Run().
+  // It will run the message loop in a SEH try block or not depending on the
+  // set_SEH_restoration() flag.
+  void RunInternal(Dispatcher* dispatcher);
+
+  //----------------------------------------------------------------------------
+  // A list of alternate message loop priority systems.  The strategy_selector_
+  // determines which one to actually use.
+  void RunTraditional();
+
+  //----------------------------------------------------------------------------
+  // A list of method wrappers with identical calling signatures (no arguments)
+  // for use in the main message loop.  Method pointers to these methods may be
+  // called round-robin from the main message loop, on any desired schedule.
+
+  bool ProcessNextDeferredTask();
+  bool ProcessNextDelayedNonNestableTask();
+  bool ProcessNextObject();
+  bool ProcessSomeTimers();
+
+  //----------------------------------------------------------------------------
+  // Process some pending messages.
+  // Returns true if a message was processed.
+  bool ProcessNextWindowsMessage();
+
+  // Wait until either an object is signaled, a message is available, a timer
+  // needs attention, or our incoming_queue_ has gotten a task.
+  // Handle (without returning) any APCs (only IO thread currently has APCs.)
+  void WaitForWork();
+
+  // Helper function for processing window messages. This includes handling
+  // WM_QUIT, message translation and dispatch, etc.
+  //
+  // If dispatcher_ is non-NULL this method does NOT dispatch the event, instead
+  // it invokes Dispatch on the dispatcher_.
+  bool ProcessMessageHelper(const MSG& msg);
+
+  // When we encounter a kMsgPumpATask, the following helper can be called to
+  // peek and process a replacement message, such as a WM_PAINT or WM_TIMER.
+  // The goal is to make the kMsgPumpATask as non-intrusive as possible, even
+  // though a continuous stream of such messages are posted.  This method
+  // carefully peeks a message while there is no chance for a kMsgPumpATask to
+  // be pending, then releases the lock (allowing a replacement kMsgPumpATask to
+  // possibly be posted), and finally dispatches that peeked replacement.
+  // Note that the re-post of kMsgPumpATask may be asynchronous to this thread!!
+  bool ProcessPumpReplacementMessage();
+
+  // Signals a watcher if a wait falls within the range of objects we're
+  // waiting on.  object_index is the offset in objects_ that was signaled.
+  // Returns true if an object was signaled.
+  bool SignalWatcher(size_t object_index);
+
+  // Run a work_queue_ task or new_task, and delete it (if it was processed by
+  // PostTask). If there are queued tasks, the oldest one is executed and
+  // new_task is queued. new_task is optional and can be NULL. In this NULL
+  // case, the method will run one pending task (if any exist). Returns true if
+  // it executes a task.
+  // Queued tasks accumulate only when there is a nonreentrant task currently
+  // processing, in which case the new_task is appended to the list
+  // work_queue_.  Such re-entrancy generally happens when an unrequested
+  // message pump (typical of a native dialog) is executing in the context of a
+  // task.
+  bool QueueOrRunTask(Task* new_task);
+
+  // Runs the specified task and deletes it.
+  void RunTask(Task* task);
+
+  // Make state adjustments just before and after running tasks so that we can
+  // continue to work if a native message loop is employed during a task.
+  void BeforeTaskRunSetup();
+  void AfterTaskRunRestore();
+
+  // When processing messages in our MessageWndProc(), we are sometimes called
+  // by a native message pump (i.e., We are not called out of our Run() pump).
+  // In those cases, we need to process tasks during the Windows Message
+  // callback.  This method processes a task, and also posts a new kMsgPumpATask
+  // messages to the Windows Msg Queue so that we are called back later (to
+  // process additional tasks).
+  void PumpATaskDuringWndProc();
+
+  // Load tasks from the incoming_queue_ into work_queue_ if the latter is
+  // empty.  The former requires a lock to access, while the latter is directly
+  // accessible on this thread.
+  void ReloadWorkQueue();
+
+  // Delete tasks that haven't run yet without running them.  Used in the
+  // destructor to make sure all the task's destructors get called.
+  void DeletePendingTasks();
+
+  // Make sure a kPumpATask message is in flight, which starts/continues the
+  // sub-pump.
+  void EnsurePumpATaskWasPosted();
+
+  // Do a PostMessage(), and crash if we can't eventually do the post.
+  void EnsureMessageGetsPosted(int message) const;
+
+  // Post a task to our incomming queue.
+  void MessageLoop::PostTaskInternal(Task* task);
+
+  // Start recording histogram info about events and action IF it was enabled
+  // and IF the statistics recorder can accept a registration of our histogram.
+  void StartHistogrammer();
+
+  // Add occurence of event to our histogram, so that we can see what is being
+  // done in a specific MessageLoop instance (i.e., specific thread).
+  // If message_histogram_ is NULL, this is a no-op.
+  void HistogramEvent(int event);
+
+  static TLSSlot tls_index_;
+  static int strategy_selector_;
+  static const LinearHistogram::DescriptionPair event_descriptions_[];
+  static bool enable_histogrammer_;
+
+  TimerManager timer_manager_;
+
+  // A list of tasks that need to be processed by this instance.  Note that this
+  // queue is only accessed (push/pop) by our current thread.
+  // As an optimization, when we don't need to use the prioritization of
+  // work_queue_, we use a null terminated list (TaskQueue) as our
+  // implementation of the queue. This saves on memory (list uses pointers
+  // internal to Task) and probably runs faster than the priority queue when
+  // there was no real prioritization.
+  OptionallyPrioritizedTaskQueue work_queue_;
+
+  // A vector of objects (and corresponding watchers) that are routinely
+  // serviced by this message loop's pump.
+  std::vector<HANDLE> objects_;
+  std::vector<Watcher*> watchers_;
+
+  ObserverList<Observer> observers_;
+  HWND message_hwnd_;
+  IDMap<Task> timed_tasks_;
+  // A recursion block that prevents accidentally running additonal tasks when
+  // insider a (accidentally induced?) nested message pump.
+  bool nestable_tasks_allowed_;
+
+  bool exception_restoration_;
+
+  Dispatcher* dispatcher_;
+  bool quit_received_;
+  bool quit_now_;
+
+  std::string thread_name_;
+  // A profiling histogram showing the counts of various messages and events.
+  scoped_ptr<LinearHistogram> message_histogram_;
+
+  // A null terminated list which creates an incoming_queue of tasks that are
+  // aquired under a mutex for processing on this instance's thread. These tasks
+  // have not yet been sorted out into items for our work_queue_ vs items that
+  // will be handled by the TimerManager.
+  TaskQueue incoming_queue_;
+  // Protect access to incoming_queue_.
+  Lock incoming_queue_lock_;
+
+  // A null terminated list of non-nestable tasks that we had to delay because
+  // when it came time to execute them we were in a nested message loop.  They
+  // will execute once we're out of nested message loops.
+  TaskQueue delayed_non_nestable_queue_;
+
+  // Indicate if there is a kMsgPumpATask message pending in the Windows Message
+  // queue.  There is at most one such message, and it can drive execution of
+  // tasks when a native message pump is running.
+  bool task_pump_message_pending_;
+  // Protect access to task_pump_message_pending_.
+  Lock task_pump_message_lock_;
+
+  // Used to count how many Run() invocations are on the stack.
+  int run_depth_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(MessageLoop);
+};
+
+#endif  // BASE_MESSAGE_LOOP_H__
diff --git a/base/message_loop_unittest.cc b/base/message_loop_unittest.cc
new file mode 100644
index 0000000..374a618
--- /dev/null
+++ b/base/message_loop_unittest.cc
@@ -0,0 +1,827 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/scoped_handle.h"
+#include "base/thread.h"
+#include "base/ref_counted.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class MessageLoopTest : public testing::Test {
+ public:
+  virtual void SetUp() {
+    enable_recursive_task_ = MessageLoop::current()->NestableTasksAllowed();
+  }
+  virtual void TearDown() {
+    MessageLoop::current()->SetNestableTasksAllowed(enable_recursive_task_);
+  }
+ private:
+  bool enable_recursive_task_;
+};
+
+class Foo : public base::RefCounted<Foo> {
+ public:
+  Foo() : test_count_(0) {
+  }
+
+  void Test0() {
+    ++test_count_;
+  }
+
+  void Test1ConstRef(const std::string& a) {
+    ++test_count_;
+    result_.append(a);
+  }
+
+  void Test1Ptr(std::string* a) {
+    ++test_count_;
+    result_.append(*a);
+  }
+
+  void Test1Int(int a) {
+    test_count_ += a;
+  }
+
+  void Test2Ptr(std::string* a, std::string* b) {
+    ++test_count_;
+    result_.append(*a);
+    result_.append(*b);
+  }
+
+  void Test2Mixed(const std::string& a, std::string* b) {
+    ++test_count_;
+    result_.append(a);
+    result_.append(*b);
+  }
+
+  int test_count() const { return test_count_; }
+  const std::string& result() const { return result_; }
+
+ private:
+  int test_count_;
+  std::string result_;
+};
+
+class QuitMsgLoop : public base::RefCounted<QuitMsgLoop> {
+ public:
+  void QuitNow() {
+    MessageLoop::current()->Quit();
+  }
+};
+
+}  // namespace
+
+TEST(MessageLoopTest, PostTask) {
+  // Add tests to message loop
+  scoped_refptr<Foo> foo = new Foo();
+  std::string a("a"), b("b"), c("c"), d("d");
+  MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+      foo.get(), &Foo::Test0));
+  MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+    foo.get(), &Foo::Test1ConstRef, a));
+  MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+      foo.get(), &Foo::Test1Ptr, &b));
+  MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+      foo.get(), &Foo::Test1Int, 100));
+  MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+      foo.get(), &Foo::Test2Ptr, &a, &c));
+  MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+    foo.get(), &Foo::Test2Mixed, a, &d));
+
+  // After all tests, post a message that will shut down the message loop
+  scoped_refptr<QuitMsgLoop> quit = new QuitMsgLoop();
+  MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+      quit.get(), &QuitMsgLoop::QuitNow));
+
+  // Now kick things off
+  MessageLoop::current()->Run();
+
+  EXPECT_EQ(foo->test_count(), 105);
+  EXPECT_EQ(foo->result(), "abacad");
+}
+
+TEST(MessageLoopTest, InvokeLater_SEH) {
+  // Add tests to message loop
+  scoped_refptr<Foo> foo = new Foo();
+  std::string a("a"), b("b"), c("c"), d("d");
+  MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+      foo.get(), &Foo::Test0));
+  MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+      foo.get(), &Foo::Test1ConstRef, a));
+  MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+      foo.get(), &Foo::Test1Ptr, &b));
+  MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+      foo.get(), &Foo::Test1Int, 100));
+  MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+      foo.get(), &Foo::Test2Ptr, &a, &c));
+  MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+      foo.get(), &Foo::Test2Mixed, a, &d));
+
+  // After all tests, post a message that will shut down the message loop
+  scoped_refptr<QuitMsgLoop> quit = new QuitMsgLoop();
+  MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+      quit.get(), &QuitMsgLoop::QuitNow));
+
+  // Now kick things off with the SEH block active.
+  MessageLoop::current()->set_exception_restoration(true);
+  MessageLoop::current()->Run();
+  MessageLoop::current()->set_exception_restoration(false);
+
+  EXPECT_EQ(foo->test_count(), 105);
+  EXPECT_EQ(foo->result(), "abacad");
+}
+
+namespace {
+
+class NestingTest : public Task {
+ public:
+  explicit NestingTest(int* depth) : depth_(depth) {
+  }
+  void Run() {
+    if (*depth_ > 0) {
+      *depth_ -= 1;
+      MessageLoop::current()->PostTask(FROM_HERE, new NestingTest(depth_));
+
+      MessageLoop::current()->SetNestableTasksAllowed(true);
+      MessageLoop::current()->Run();
+    }
+    MessageLoop::current()->Quit();
+  }
+ private:
+  int* depth_;
+};
+
+LONG WINAPI BadExceptionHandler(EXCEPTION_POINTERS *ex_info) {
+  ADD_FAILURE() << "bad exception handler";
+  ::ExitProcess(ex_info->ExceptionRecord->ExceptionCode);
+  return EXCEPTION_EXECUTE_HANDLER;
+}
+
+// This task throws an SEH exception: initially write to an invalid address.
+// If the right SEH filter is installed, it will fix the error.
+class CrasherTask : public Task {
+ public:
+  // Ctor. If trash_SEH_handler is true, the task will override the unhandled
+  // exception handler with one sure to crash this test.
+  explicit CrasherTask(bool trash_SEH_handler)
+      : trash_SEH_handler_(trash_SEH_handler) {
+  }
+  void Run() {
+    Sleep(1);
+    if (trash_SEH_handler_)
+      ::SetUnhandledExceptionFilter(&BadExceptionHandler);
+    // Generate a SEH fault. We do it in asm to make sure we know how to undo
+    // the damage.
+    __asm {
+      mov eax, dword ptr [CrasherTask::bad_array_]
+      mov byte ptr [eax], 66
+    }
+    MessageLoop::current()->Quit();
+  }
+  // Points the bad array to a valid memory location.
+  static void FixError() {
+    bad_array_ = &valid_store_;
+  }
+
+ private:
+  bool trash_SEH_handler_;
+  static volatile char* bad_array_;
+  static char valid_store_;
+};
+
+volatile char* CrasherTask::bad_array_ = 0;
+char CrasherTask::valid_store_ = 0;
+
+// This SEH filter fixes the problem and retries execution. Fixing requires
+// that the last instruction: mov eax, [CrasherTask::bad_array_] to be retried
+// so we move the instruction pointer 5 bytes back.
+LONG WINAPI HandleCrasherTaskException(EXCEPTION_POINTERS *ex_info) {
+  if (ex_info->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION)
+    return EXCEPTION_EXECUTE_HANDLER;
+
+  CrasherTask::FixError();
+  ex_info->ContextRecord->Eip -= 5;
+  return EXCEPTION_CONTINUE_EXECUTION;
+}
+
+}  // namespace
+
+
+TEST(MessageLoopTest, Crasher) {
+  if (::IsDebuggerPresent())
+    return;
+
+  LPTOP_LEVEL_EXCEPTION_FILTER old_SEH_filter =
+      ::SetUnhandledExceptionFilter(&HandleCrasherTaskException);
+
+  MessageLoop::current()->PostTask(FROM_HERE, new CrasherTask(false));
+  MessageLoop::current()->set_exception_restoration(true);
+  MessageLoop::current()->Run();
+  MessageLoop::current()->set_exception_restoration(false);
+
+  ::SetUnhandledExceptionFilter(old_SEH_filter);
+}
+
+
+TEST(MessageLoopTest, CrasherNasty) {
+  if (::IsDebuggerPresent())
+    return;
+
+  LPTOP_LEVEL_EXCEPTION_FILTER old_SEH_filter =
+      ::SetUnhandledExceptionFilter(&HandleCrasherTaskException);
+
+  MessageLoop::current()->PostTask(FROM_HERE, new CrasherTask(true));
+  MessageLoop::current()->set_exception_restoration(true);
+  MessageLoop::current()->Run();
+  MessageLoop::current()->set_exception_restoration(false);
+
+  ::SetUnhandledExceptionFilter(old_SEH_filter);
+}
+
+TEST(MessageLoopTest, Nesting) {
+  int depth = 100;
+  MessageLoop::current()->PostTask(FROM_HERE, new NestingTest(&depth));
+  MessageLoop::current()->Run();
+  EXPECT_EQ(depth, 0);
+}
+
+namespace {
+
+const wchar_t* const kMessageBoxTitle = L"MessageLoop Unit Test";
+
+enum TaskType {
+  MESSAGEBOX,
+  ENDDIALOG,
+  RECURSIVE,
+  TIMEDMESSAGELOOP,
+  QUITMESSAGELOOP,
+  ORDERERD,
+  PUMPS,
+};
+
+// Saves the order in which the tasks executed.
+struct TaskItem {
+  TaskItem(TaskType t, int c, bool s)
+      : type(t),
+        cookie(c),
+        start(s) {
+  }
+
+  TaskType type;
+  int cookie;
+  bool start;
+
+  bool operator == (const TaskItem& other) const {
+    return type == other.type && cookie == other.cookie && start == other.start;
+  }
+};
+
+typedef std::vector<TaskItem> TaskList;
+
+std::ostream& operator <<(std::ostream& os, TaskType type) {
+  switch (type) {
+  case MESSAGEBOX:        os << "MESSAGEBOX"; break;
+  case ENDDIALOG:         os << "ENDDIALOG"; break;
+  case RECURSIVE:         os << "RECURSIVE"; break;
+  case TIMEDMESSAGELOOP:  os << "TIMEDMESSAGELOOP"; break;
+  case QUITMESSAGELOOP:   os << "QUITMESSAGELOOP"; break;
+  case ORDERERD:          os << "ORDERERD"; break;
+  case PUMPS:             os << "PUMPS"; break;
+  default:
+    NOTREACHED();
+    os << "Unknown TaskType";
+    break;
+  }
+  return os;
+}
+
+std::ostream& operator <<(std::ostream& os, const TaskItem& item) {
+  if (item.start)
+    return os << item.type << " " << item.cookie << " starts";
+  else
+    return os << item.type << " " << item.cookie << " ends";
+}
+
+// Saves the order the tasks ran.
+class OrderedTasks : public Task {
+ public:
+  OrderedTasks(TaskList* order, int cookie)
+      : order_(order),
+        type_(ORDERERD),
+        cookie_(cookie) {
+  }
+  OrderedTasks(TaskList* order, TaskType type, int cookie)
+      : order_(order),
+        type_(type),
+        cookie_(cookie) {
+  }
+
+  void RunStart() {
+    TaskItem item(type_, cookie_, true);
+    DLOG(INFO) << item;
+    order_->push_back(item);
+  }
+  void RunEnd() {
+    TaskItem item(type_, cookie_, false);
+    DLOG(INFO) << item;
+    order_->push_back(item);
+  }
+
+  virtual void Run() {
+    RunStart();
+    RunEnd();
+  }
+
+ protected:
+  TaskList* order() const {
+    return order_;
+  }
+
+  int cookie() const {
+    return cookie_;
+  }
+
+ private:
+  TaskList* order_;
+  TaskType type_;
+  int cookie_;
+};
+
+// MessageLoop implicitly start a "modal message loop". Modal dialog boxes,
+// common controls (like OpenFile) and StartDoc printing function can cause
+// implicit message loops.
+class MessageBoxTask : public OrderedTasks {
+ public:
+  MessageBoxTask(TaskList* order, int cookie, bool is_reentrant)
+      : OrderedTasks(order, MESSAGEBOX, cookie),
+        is_reentrant_(is_reentrant) {
+  }
+
+  virtual void Run() {
+    RunStart();
+    if (is_reentrant_)
+      MessageLoop::current()->SetNestableTasksAllowed(true);
+    MessageBox(NULL, L"Please wait...", kMessageBoxTitle, MB_OK);
+    RunEnd();
+  }
+
+ private:
+  bool is_reentrant_;
+};
+
+// Will end the MessageBox.
+class EndDialogTask : public OrderedTasks {
+ public:
+  EndDialogTask(TaskList* order, int cookie)
+      : OrderedTasks(order, ENDDIALOG, cookie) {
+  }
+
+  virtual void Run() {
+    RunStart();
+    HWND window = GetActiveWindow();
+    if (window != NULL) {
+      EXPECT_NE(EndDialog(window, IDCONTINUE), 0);
+      // Cheap way to signal that the window wasn't found if RunEnd() isn't
+      // called.
+      RunEnd();
+    }
+  }
+};
+
+class RecursiveTask : public OrderedTasks {
+ public:
+  RecursiveTask(int depth, TaskList* order, int cookie, bool is_reentrant)
+      : OrderedTasks(order, RECURSIVE, cookie),
+        depth_(depth),
+        is_reentrant_(is_reentrant) {
+  }
+
+  virtual void Run() {
+    RunStart();
+    if (depth_ > 0) {
+      if (is_reentrant_)
+        MessageLoop::current()->SetNestableTasksAllowed(true);
+      MessageLoop::current()->PostTask(FROM_HERE,
+          new RecursiveTask(depth_ - 1, order(), cookie(), is_reentrant_));
+    }
+    RunEnd();
+  }
+
+ private:
+  int depth_;
+  bool is_reentrant_;
+};
+
+class QuitTask : public OrderedTasks {
+ public:
+  QuitTask(TaskList* order, int cookie)
+      : OrderedTasks(order, QUITMESSAGELOOP, cookie) {
+  }
+
+  virtual void Run() {
+    RunStart();
+    MessageLoop::current()->Quit();
+    RunEnd();
+  }
+};
+
+class Recursive2Tasks : public Task {
+ public:
+  Recursive2Tasks(MessageLoop* target,
+                  HANDLE event,
+                  bool expect_window,
+                  TaskList* order,
+                  bool is_reentrant)
+      : target_(target),
+        event_(event),
+        expect_window_(expect_window),
+        order_(order),
+        is_reentrant_(is_reentrant) {
+  }
+
+  virtual void Run() {
+    target_->PostTask(FROM_HERE,
+                      new RecursiveTask(2, order_, 1, is_reentrant_));
+    target_->PostTask(FROM_HERE,
+                      new MessageBoxTask(order_, 2, is_reentrant_));
+    target_->PostTask(FROM_HERE,
+                      new RecursiveTask(2, order_, 3, is_reentrant_));
+    // The trick here is that for recursive task processing, this task will be
+    // ran _inside_ the MessageBox message loop, dismissing the MessageBox
+    // without a chance.
+    // For non-recursive task processing, this will be executed _after_ the
+    // MessageBox will have been dismissed by the code below, where
+    // expect_window_ is true.
+    target_->PostTask(FROM_HERE, new EndDialogTask(order_, 4));
+    target_->PostTask(FROM_HERE, new QuitTask(order_, 5));
+
+    // Enforce that every tasks are sent before starting to run the main thread
+    // message loop.
+    ASSERT_TRUE(SetEvent(event_));
+
+    // Poll for the MessageBox. Don't do this at home! At the speed we do it,
+    // you will never realize one MessageBox was shown.
+    for (; expect_window_;) {
+      HWND window = FindWindow(L"#32770", kMessageBoxTitle);
+      if (window) {
+        // Dismiss it.
+        for (;;) {
+          HWND button = FindWindowEx(window, NULL, L"Button", NULL);
+          if (button != NULL) {
+            EXPECT_TRUE(0 == SendMessage(button, WM_LBUTTONDOWN, 0, 0));
+            EXPECT_TRUE(0 == SendMessage(button, WM_LBUTTONUP, 0, 0));
+            break;
+          }
+        }
+        break;
+      }
+    }
+  }
+
+ private:
+  MessageLoop* target_;
+  HANDLE event_;
+  TaskList* order_;
+  bool expect_window_;
+  bool is_reentrant_;
+};
+
+}  // namespace
+
+TEST(MessageLoop, RecursiveDenial1) {
+  EXPECT_TRUE(MessageLoop::current()->NestableTasksAllowed());
+  TaskList order;
+  MessageLoop::current()->PostTask(FROM_HERE,
+                                   new RecursiveTask(2, &order, 1, false));
+  MessageLoop::current()->PostTask(FROM_HERE,
+                                   new RecursiveTask(2, &order, 2, false));
+  MessageLoop::current()->PostTask(FROM_HERE, new QuitTask(&order, 3));
+
+  MessageLoop::current()->Run();
+
+  // FIFO order.
+  ASSERT_EQ(order.size(), 14);
+  EXPECT_EQ(order[ 0], TaskItem(RECURSIVE, 1, true));
+  EXPECT_EQ(order[ 1], TaskItem(RECURSIVE, 1, false));
+  EXPECT_EQ(order[ 2], TaskItem(RECURSIVE, 2, true));
+  EXPECT_EQ(order[ 3], TaskItem(RECURSIVE, 2, false));
+  EXPECT_EQ(order[ 4], TaskItem(QUITMESSAGELOOP, 3, true));
+  EXPECT_EQ(order[ 5], TaskItem(QUITMESSAGELOOP, 3, false));
+  EXPECT_EQ(order[ 6], TaskItem(RECURSIVE, 1, true));
+  EXPECT_EQ(order[ 7], TaskItem(RECURSIVE, 1, false));
+  EXPECT_EQ(order[ 8], TaskItem(RECURSIVE, 2, true));
+  EXPECT_EQ(order[ 9], TaskItem(RECURSIVE, 2, false));
+  EXPECT_EQ(order[10], TaskItem(RECURSIVE, 1, true));
+  EXPECT_EQ(order[11], TaskItem(RECURSIVE, 1, false));
+  EXPECT_EQ(order[12], TaskItem(RECURSIVE, 2, true));
+  EXPECT_EQ(order[13], TaskItem(RECURSIVE, 2, false));
+}
+
+
+TEST(MessageLoop, RecursiveSupport1) {
+  TaskList order;
+  MessageLoop::current()->PostTask(FROM_HERE,
+                                   new RecursiveTask(2, &order, 1, true));
+  MessageLoop::current()->PostTask(FROM_HERE,
+                                   new RecursiveTask(2, &order, 2, true));
+  MessageLoop::current()->PostTask(FROM_HERE,
+                                   new QuitTask(&order, 3));
+
+  MessageLoop::current()->Run();
+
+  // FIFO order.
+  ASSERT_EQ(order.size(), 14);
+  EXPECT_EQ(order[ 0], TaskItem(RECURSIVE, 1, true));
+  EXPECT_EQ(order[ 1], TaskItem(RECURSIVE, 1, false));
+  EXPECT_EQ(order[ 2], TaskItem(RECURSIVE, 2, true));
+  EXPECT_EQ(order[ 3], TaskItem(RECURSIVE, 2, false));
+  EXPECT_EQ(order[ 4], TaskItem(QUITMESSAGELOOP, 3, true));
+  EXPECT_EQ(order[ 5], TaskItem(QUITMESSAGELOOP, 3, false));
+  EXPECT_EQ(order[ 6], TaskItem(RECURSIVE, 1, true));
+  EXPECT_EQ(order[ 7], TaskItem(RECURSIVE, 1, false));
+  EXPECT_EQ(order[ 8], TaskItem(RECURSIVE, 2, true));
+  EXPECT_EQ(order[ 9], TaskItem(RECURSIVE, 2, false));
+  EXPECT_EQ(order[10], TaskItem(RECURSIVE, 1, true));
+  EXPECT_EQ(order[11], TaskItem(RECURSIVE, 1, false));
+  EXPECT_EQ(order[12], TaskItem(RECURSIVE, 2, true));
+  EXPECT_EQ(order[13], TaskItem(RECURSIVE, 2, false));
+}
+
+// A side effect of this test is the generation a beep. Sorry.
+TEST(MessageLoop, RecursiveDenial2) {
+  Thread worker("RecursiveDenial2_worker");
+  ASSERT_EQ(true, worker.Start());
+  TaskList order;
+  ScopedHandle event(CreateEvent(NULL, FALSE, FALSE, NULL));
+  worker.message_loop()->PostTask(FROM_HERE,
+                                  new Recursive2Tasks(MessageLoop::current(),
+                                                      event,
+                                                      true,
+                                                      &order,
+                                                      false));
+  // Let the other thread execute.
+  WaitForSingleObject(event, INFINITE);
+  MessageLoop::current()->Run();
+
+  ASSERT_EQ(order.size(), 17);
+  EXPECT_EQ(order[ 0], TaskItem(RECURSIVE, 1, true));
+  EXPECT_EQ(order[ 1], TaskItem(RECURSIVE, 1, false));
+  EXPECT_EQ(order[ 2], TaskItem(MESSAGEBOX, 2, true));
+  EXPECT_EQ(order[ 3], TaskItem(MESSAGEBOX, 2, false));
+  EXPECT_EQ(order[ 4], TaskItem(RECURSIVE, 3, true));
+  EXPECT_EQ(order[ 5], TaskItem(RECURSIVE, 3, false));
+  // When EndDialogTask is processed, the window is already dismissed, hence no
+  // "end" entry.
+  EXPECT_EQ(order[ 6], TaskItem(ENDDIALOG, 4, true));
+  EXPECT_EQ(order[ 7], TaskItem(QUITMESSAGELOOP, 5, true));
+  EXPECT_EQ(order[ 8], TaskItem(QUITMESSAGELOOP, 5, false));
+  EXPECT_EQ(order[ 9], TaskItem(RECURSIVE, 1, true));
+  EXPECT_EQ(order[10], TaskItem(RECURSIVE, 1, false));
+  EXPECT_EQ(order[11], TaskItem(RECURSIVE, 3, true));
+  EXPECT_EQ(order[12], TaskItem(RECURSIVE, 3, false));
+  EXPECT_EQ(order[13], TaskItem(RECURSIVE, 1, true));
+  EXPECT_EQ(order[14], TaskItem(RECURSIVE, 1, false));
+  EXPECT_EQ(order[15], TaskItem(RECURSIVE, 3, true));
+  EXPECT_EQ(order[16], TaskItem(RECURSIVE, 3, false));
+}
+
+// A side effect of this test is the generation a beep. Sorry.
+TEST(MessageLoop, RecursiveSupport2) {
+  Thread worker("RecursiveSupport2_worker");
+  ASSERT_EQ(true, worker.Start());
+  TaskList order;
+  ScopedHandle event(CreateEvent(NULL, FALSE, FALSE, NULL));
+  worker.message_loop()->PostTask(FROM_HERE,
+                                  new Recursive2Tasks(MessageLoop::current(),
+                                                      event,
+                                                      false,
+                                                      &order,
+                                                      true));
+  // Let the other thread execute.
+  WaitForSingleObject(event, INFINITE);
+  MessageLoop::current()->Run();
+
+  ASSERT_EQ(order.size(), 18);
+  EXPECT_EQ(order[ 0], TaskItem(RECURSIVE, 1, true));
+  EXPECT_EQ(order[ 1], TaskItem(RECURSIVE, 1, false));
+  EXPECT_EQ(order[ 2], TaskItem(MESSAGEBOX, 2, true));
+  // Note that this executes in the MessageBox modal loop.
+  EXPECT_EQ(order[ 3], TaskItem(RECURSIVE, 3, true));
+  EXPECT_EQ(order[ 4], TaskItem(RECURSIVE, 3, false));
+  EXPECT_EQ(order[ 5], TaskItem(ENDDIALOG, 4, true));
+  EXPECT_EQ(order[ 6], TaskItem(ENDDIALOG, 4, false));
+  EXPECT_EQ(order[ 7], TaskItem(MESSAGEBOX, 2, false));
+  /* The order can subtly change here. The reason is that when RecursiveTask(1)
+     is called in the main thread, if it is faster than getting to the
+     PostTask(FROM_HERE, QuitTask) execution, the order of task execution can
+     change. We don't care anyway that the order isn't correct.
+  EXPECT_EQ(order[ 8], TaskItem(QUITMESSAGELOOP, 5, true));
+  EXPECT_EQ(order[ 9], TaskItem(QUITMESSAGELOOP, 5, false));
+  EXPECT_EQ(order[10], TaskItem(RECURSIVE, 1, true));
+  EXPECT_EQ(order[11], TaskItem(RECURSIVE, 1, false));
+  */
+  EXPECT_EQ(order[12], TaskItem(RECURSIVE, 3, true));
+  EXPECT_EQ(order[13], TaskItem(RECURSIVE, 3, false));
+  EXPECT_EQ(order[14], TaskItem(RECURSIVE, 1, true));
+  EXPECT_EQ(order[15], TaskItem(RECURSIVE, 1, false));
+  EXPECT_EQ(order[16], TaskItem(RECURSIVE, 3, true));
+  EXPECT_EQ(order[17], TaskItem(RECURSIVE, 3, false));
+}
+
+class TaskThatPumps : public OrderedTasks {
+ public:
+  TaskThatPumps(TaskList* order, int cookie)
+      : OrderedTasks(order, PUMPS, cookie) {
+  }
+
+  virtual void Run() {
+    RunStart();
+    bool old_state = MessageLoop::current()->NestableTasksAllowed();
+    MessageLoop::current()->Quit();
+    MessageLoop::current()->SetNestableTasksAllowed(true);
+    MessageLoop::current()->Run();
+    MessageLoop::current()->SetNestableTasksAllowed(old_state);
+    RunEnd();
+  }
+
+ private:
+};
+
+
+// Tests that non nestable tasks run in FIFO if there are no nested loops.
+TEST(MessageLoop, NonNestableWithNoNesting) {
+  TaskList order;
+
+  Task* task = new OrderedTasks(&order, 1);
+  task->set_nestable(false);
+  MessageLoop::current()->PostTask(FROM_HERE, task);
+  MessageLoop::current()->PostTask(FROM_HERE, new OrderedTasks(&order, 2));
+  MessageLoop::current()->PostTask(FROM_HERE, new QuitTask(&order, 3));
+  MessageLoop::current()->Run();
+
+  // FIFO order.
+  ASSERT_EQ(order.size(), 6);
+  EXPECT_EQ(order[ 0], TaskItem(ORDERERD, 1, true));
+  EXPECT_EQ(order[ 1], TaskItem(ORDERERD, 1, false));
+  EXPECT_EQ(order[ 2], TaskItem(ORDERERD, 2, true));
+  EXPECT_EQ(order[ 3], TaskItem(ORDERERD, 2, false));
+  EXPECT_EQ(order[ 4], TaskItem(QUITMESSAGELOOP, 3, true));
+  EXPECT_EQ(order[ 5], TaskItem(QUITMESSAGELOOP, 3, false));
+}
+
+// Tests that non nestable tasks don't run when there's code in the call stack.
+TEST(MessageLoop, NonNestableInNestedLoop) {
+  TaskList order;
+
+  MessageLoop::current()->PostTask(FROM_HERE,
+                                   new TaskThatPumps(&order, 1));
+  Task* task = new OrderedTasks(&order, 2);
+  task->set_nestable(false);
+  MessageLoop::current()->PostTask(FROM_HERE, task);
+  MessageLoop::current()->PostTask(FROM_HERE, new OrderedTasks(&order, 3));
+  MessageLoop::current()->PostTask(FROM_HERE, new QuitTask(&order, 4));
+  Task* non_nestable_quit = new QuitTask(&order, 5);
+  non_nestable_quit->set_nestable(false);
+  MessageLoop::current()->PostTask(FROM_HERE, non_nestable_quit);
+
+
+  MessageLoop::current()->Run();
+
+  // FIFO order.
+  ASSERT_EQ(order.size(), 10);
+  EXPECT_EQ(order[ 0], TaskItem(PUMPS, 1, true));
+  EXPECT_EQ(order[ 1], TaskItem(ORDERERD, 3, true));
+  EXPECT_EQ(order[ 2], TaskItem(ORDERERD, 3, false));
+  EXPECT_EQ(order[ 3], TaskItem(QUITMESSAGELOOP, 4, true));
+  EXPECT_EQ(order[ 4], TaskItem(QUITMESSAGELOOP, 4, false));
+  EXPECT_EQ(order[ 5], TaskItem(PUMPS, 1, false));
+  EXPECT_EQ(order[ 6], TaskItem(ORDERERD, 2, true));
+  EXPECT_EQ(order[ 7], TaskItem(ORDERERD, 2, false));
+  EXPECT_EQ(order[ 8], TaskItem(QUITMESSAGELOOP, 5, true));
+  EXPECT_EQ(order[ 9], TaskItem(QUITMESSAGELOOP, 5, false));
+}
+
+
+namespace {
+
+class AutoresetWatcher : public MessageLoop::Watcher {
+ public:
+  AutoresetWatcher(HANDLE signal, MessageLoop* message_loop)
+      : signal_(signal), message_loop_(message_loop) {}
+  virtual void OnObjectSignaled(HANDLE object);
+ private:
+  HANDLE signal_;
+  MessageLoop* message_loop_;
+};
+
+void AutoresetWatcher::OnObjectSignaled(HANDLE object) {
+  message_loop_->WatchObject(object, NULL);
+  ASSERT_TRUE(SetEvent(signal_));
+}
+
+class AutoresetTask : public Task {
+ public:
+  AutoresetTask(HANDLE object, MessageLoop::Watcher* watcher)
+    : object_(object), watcher_(watcher) {}
+  virtual void Run() {
+    MessageLoop::current()->WatchObject(object_, watcher_);
+  }
+
+ private:
+  HANDLE object_;
+  MessageLoop::Watcher* watcher_;
+};
+
+}  // namespace
+
+TEST(MessageLoop, AutoresetEvents) {
+  SECURITY_ATTRIBUTES attributes;
+  attributes.nLength = sizeof(attributes);
+  attributes.bInheritHandle = false;
+  attributes.lpSecurityDescriptor = NULL;
+
+  // Init an autoreset and a manual reset events.
+  HANDLE autoreset = CreateEvent(&attributes, FALSE, FALSE, NULL);
+  HANDLE callback_called = CreateEvent(&attributes, TRUE, FALSE, NULL);
+  ASSERT_TRUE(NULL != autoreset);
+  ASSERT_TRUE(NULL != callback_called);
+
+  Thread thread("Autoreset test");
+  ASSERT_TRUE(thread.Start());
+
+  MessageLoop* message_loop = thread.message_loop();
+  ASSERT_TRUE(NULL != message_loop);
+
+  AutoresetWatcher watcher(callback_called, message_loop);
+  AutoresetTask* task = new AutoresetTask(autoreset, &watcher);
+  message_loop->PostTask(FROM_HERE, task);
+  Sleep(100);  // Make sure the thread runs and sleeps for lack of work.
+
+  ASSERT_TRUE(SetEvent(autoreset));
+
+  DWORD result = WaitForSingleObject(callback_called, 1000);
+  EXPECT_EQ(WAIT_OBJECT_0, result);
+
+  thread.Stop();
+}
+
+namespace {
+
+class DispatcherImpl : public MessageLoop::Dispatcher {
+ public:
+  DispatcherImpl() : dispatch_count_(0) {}
+
+  virtual bool Dispatch(const MSG& msg) {
+    ::TranslateMessage(&msg);
+    ::DispatchMessage(&msg);
+    return (++dispatch_count_ != 2);
+  }
+
+  int dispatch_count_;
+};
+
+}  // namespace
+
+TEST(MessageLoop, Dispatcher) {
+  class MyTask : public Task {
+  public:
+    virtual void Run() {
+      PostMessage(NULL, WM_LBUTTONDOWN, 0, 0);
+      PostMessage(NULL, WM_LBUTTONUP, 'A', 0);
+    }
+  };
+  Task* task = new MyTask();
+  MessageLoop::current()->PostDelayedTask(FROM_HERE, task, 100);
+  DispatcherImpl dispatcher;
+  MessageLoop::current()->Run(&dispatcher);
+  ASSERT_EQ(2, dispatcher.dispatch_count_);
+}
diff --git a/base/multiprocess_test.h b/base/multiprocess_test.h
new file mode 100644
index 0000000..21127f3
--- /dev/null
+++ b/base/multiprocess_test.h
@@ -0,0 +1,83 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_MULTIPROCESS_TEST_H__
+#define BASE_MULTIPROCESS_TEST_H__
+
+#include "base/command_line.h"
+#include "base/process_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Command line switch to invoke a child process rather than
+// to run the normal test suite.
+static const wchar_t kRunClientProcess[] = L"client";
+
+// A MultiProcessTest is a test class which makes it easier to
+// write a test which requires code running out of process.
+//
+// To create a multiprocess test simply follow these steps:
+//
+// 1) Derive your test from MultiProcessTest.
+// 2) Modify your mainline so that if it sees the
+//    kRuNClientProcess switch, it will deal with it.
+// 3) Create a mainline function for the child processes
+// 4) Call SpawnChild("foo"), where "foo" is the name of
+//    the function you wish to run in the child processes.
+// That's it!
+//
+class MultiProcessTest : public testing::Test {
+ public:
+  // Prototype function for a client function.  Multi-process
+  // clients must provide a callback with this signature to run.
+  typedef int (__cdecl *ChildFunctionPtr)();
+
+ protected:
+  // Run a child process.
+  // 'procname' is the name of a function which the child will
+  // execute.  It must be exported from this library in order to
+  // run.
+  //
+  // Example signature:
+  //    extern "C" int __declspec(dllexport) FooBar() {
+  //         // do client work here
+  //    }
+  //
+  // Returns the handle to the child, or NULL on failure
+  HANDLE SpawnChild(const std::wstring& procname) {
+    std::wstring cl(GetCommandLineW());
+    CommandLine::AppendSwitchWithValue(&cl, kRunClientProcess, procname);
+    // TODO(darin): re-enable this once we have base/debug_util.h
+    //ProcessDebugFlags(&cl, DebugUtil::UNKNOWN, false);
+    HANDLE handle = NULL;
+    process_util::LaunchApp(cl, false, true, &handle);
+    return handle;
+  }
+};
+
+#endif  // BASE_MULTIPROCESS_TEST_H__
diff --git a/base/no_windows2000_unittest.h b/base/no_windows2000_unittest.h
new file mode 100644
index 0000000..5bef8b8
--- /dev/null
+++ b/base/no_windows2000_unittest.h
@@ -0,0 +1,46 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_NO_WINDOWS2000_UNITTEST_H__
+#define BASE_NO_WINDOWS2000_UNITTEST_H__
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "base/win_util.h"
+
+// Disable the whole test case when executing on Windows 2000 or lower.
+// Note: Parent should be testing::Test or UITest.
+template<typename Parent>
+class NoWindows2000Test : public Parent {
+ public:
+  static bool IsTestCaseDisabled() {
+    return win_util::GetWinVersion() <= win_util::WINVERSION_2000;
+  }
+};
+
+#endif  // BASE_NO_WINDOWS2000_UNITTEST_H__op
diff --git a/base/non_thread_safe.cc b/base/non_thread_safe.cc
new file mode 100644
index 0000000..eefc9a5
--- /dev/null
+++ b/base/non_thread_safe.cc
@@ -0,0 +1,48 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/non_thread_safe.h"
+
+#include "base/message_loop.h"
+
+// These checks are only done in release builds.
+#ifndef NDEBUG
+
+NonThreadSafe::NonThreadSafe() : valid_thread_id_(GetCurrentThreadId()) {
+}
+
+bool NonThreadSafe::CalledOnValidThread() const {
+  return valid_thread_id_ == GetCurrentThreadId();
+}
+
+NonThreadSafe::~NonThreadSafe() {
+  DCHECK(CalledOnValidThread());
+}
+
+#endif
diff --git a/base/non_thread_safe.h b/base/non_thread_safe.h
new file mode 100644
index 0000000..e3f4e97
--- /dev/null
+++ b/base/non_thread_safe.h
@@ -0,0 +1,79 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_NON_THREAD_SAFE_H__
+#define BASE_NON_THREAD_SAFE_H__
+
+#include "base/logging.h"
+
+// A helper class used to help verify that methods of a class are
+// called from the same thread.  One can inherit from this class and use
+// CalledOnValidThread() to verify.
+//
+// This is intended to be used with classes that appear to be thread safe, but
+// aren't.  For example, a service or a singleton like the preferences system.
+//
+// Example:
+// class MyClass : public NonThreadSafe {
+//  public:
+//   void Foo() {
+//     DCHECK(CalledOnValidThread());
+//     ... (do stuff) ...
+//   }
+// }
+//
+// In Release mode, CalledOnValidThread will always return true.
+//
+#ifndef NDEBUG
+class NonThreadSafe {
+ public:
+  NonThreadSafe();
+  ~NonThreadSafe();
+
+ protected:
+  bool CalledOnValidThread() const;
+
+ private:
+  int32 valid_thread_id_;
+};
+#else
+// Do nothing in release mode.
+class NonThreadSafe {
+ public:
+  NonThreadSafe() {}
+  ~NonThreadSafe() {}
+
+ protected:
+  bool CalledOnValidThread() const {
+    return true;
+  }
+};
+#endif  // NDEBUG
+
+#endif  // BASE_NON_THREAD_SAFE_H__
diff --git a/base/observer_list.h b/base/observer_list.h
new file mode 100644
index 0000000..a64808d
--- /dev/null
+++ b/base/observer_list.h
@@ -0,0 +1,170 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_OBSERVER_LIST_H__
+#define BASE_OBSERVER_LIST_H__
+
+#include <vector>
+#include <algorithm>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// OVERVIEW:
+//
+//   A container for a list of observers.  Unlike a normal STL vector or list,
+//   this container can be modified during iteration without invalidating the
+//   iterator.  So, it safely handles the case of an observer removing itself
+//   or other observers from the list while observers are being notified.
+//
+// TYPICAL USAGE:
+//
+//   class MyWidget {
+//    public:
+//     ...
+//
+//     class Observer {
+//      public:
+//       virtual void OnFoo(MyWidget* w) = 0;
+//       virtual void OnBar(MyWidget* w, int x, int y) = 0;
+//     };
+//
+//     void AddObserver(Observer* obs) {
+//       observer_list_.AddObserver(obs);
+//     }
+//
+//     void RemoveObserver(Observer* obs) {
+//       observer_list_.RemoveObserver(obs);
+//     }
+//
+//     void NotifyFoo() {
+//       FOR_EACH_OBSERVER(Observer, observer_list_, OnFoo(this));
+//     }
+//
+//     void NotifyBar(int x, int y) {
+//       FOR_EACH_OBSERVER(Observer, observer_list_, OnBar(this, x, y));
+//     }
+//
+//    private:
+//     ObserverList<Observer> observer_list_;
+//   };
+//
+///////////////////////////////////////////////////////////////////////////////
+
+template <class ObserverType, bool check_empty = false>
+class ObserverList {
+ public:
+  ObserverList() : notify_depth_(0) {}
+  ~ObserverList() {
+    // When check_empty is true, assert that the list is empty on destruction.
+    if (check_empty) {
+      Compact();
+      DCHECK_EQ(observers_.size(), 0);
+    }
+  }
+
+  // Add an observer to the list.
+  void AddObserver(ObserverType* obs) {
+    DCHECK(find(observers_.begin(), observers_.end(), obs) == observers_.end())
+        << "Observers can only be added once!";
+    observers_.push_back(obs);
+  }
+
+  // Remove an observer from the list.
+  void RemoveObserver(ObserverType* obs) {
+    ListType::iterator it = find(observers_.begin(), observers_.end(), obs);
+    if (it != observers_.end()) {
+      if (notify_depth_) {
+        *it = 0;
+      } else {
+        observers_.erase(it);
+      }
+    }
+  }
+
+  // An iterator class that can be used to access the list of observers.  See
+  // also the FOREACH_OBSERVER macro defined below.
+  class Iterator {
+   public:
+    Iterator(const ObserverList<ObserverType>& list) : list_(list), index_(0) {
+      ++list_.notify_depth_;
+    }
+
+    ~Iterator() {
+      if (--list_.notify_depth_ == 0)
+        list_.Compact();
+    }
+
+    ObserverType* GetNext() {
+      ListType& observers = list_.observers_;
+      // Advance if the current element is null
+      while (index_ < observers.size() && !observers[index_])
+        ++index_;
+      return index_ < observers.size() ? observers[index_++] : NULL;
+    }
+
+   private:
+    const ObserverList<ObserverType>& list_;
+    size_t index_;
+  };
+
+ private:
+  typedef std::vector<ObserverType*> ListType;
+
+  void Compact() const {
+    ListType::iterator it = observers_.begin();
+    while (it != observers_.end()) {
+      if (*it) {
+        ++it;
+      } else {
+        it = observers_.erase(it);
+      }
+    }
+  }
+
+  // These are marked mutable to facilitate having NotifyAll be const.
+  mutable ListType observers_;
+  mutable int notify_depth_;
+
+  friend class ObserverList::Iterator;
+
+  DISALLOW_EVIL_CONSTRUCTORS(ObserverList);
+};
+
+#define FOR_EACH_OBSERVER(ObserverType, observer_list, func)  \
+  do {                                                        \
+    ObserverList<ObserverType>::Iterator it(observer_list);   \
+    ObserverType* obs;                                        \
+    while (obs = it.GetNext())                                \
+      obs->func;                                              \
+  } while (0)
+
+#endif  // BASE_OBSERVER_LIST_H__
diff --git a/base/observer_list_unittest.cc b/base/observer_list_unittest.cc
new file mode 100644
index 0000000..6bb1d63
--- /dev/null
+++ b/base/observer_list_unittest.cc
@@ -0,0 +1,88 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/observer_list.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class ObserverListTest : public testing::Test {
+};
+
+class Foo {
+ public:
+  virtual void Observe(int x) = 0;
+};
+
+class Adder : public Foo {
+ public:
+  Adder(int scaler) : scaler_(scaler), total(0) {}
+  virtual void Observe(int x) {
+    total += x * scaler_;
+  }
+  int total;
+ private:
+  int scaler_;
+};
+
+class Disrupter : public Foo {
+ public:
+  Disrupter(ObserverList<Foo>& list, Foo* doomed) : list_(list), doomed_(doomed) {
+  }
+  virtual void Observe(int x) {
+    list_.RemoveObserver(doomed_);
+  }
+ private:
+  ObserverList<Foo>& list_;
+  Foo* doomed_;
+};
+
+}  // namespace
+
+TEST(ObserverListTest, BasicTest) {
+  ObserverList<Foo> observer_list;
+  Adder a(1), b(-1), c(1), d(-1);
+  Disrupter evil(observer_list, &c);
+
+  observer_list.AddObserver(&a);
+  observer_list.AddObserver(&b);
+
+  FOR_EACH_OBSERVER(Foo, observer_list, Observe(10));
+
+  observer_list.AddObserver(&evil);
+  observer_list.AddObserver(&c);
+  observer_list.AddObserver(&d);
+
+  FOR_EACH_OBSERVER(Foo, observer_list, Observe(10));
+
+  EXPECT_EQ(a.total, 20);
+  EXPECT_EQ(b.total, -20);
+  EXPECT_EQ(c.total, 0);
+  EXPECT_EQ(d.total, -10);
+}
diff --git a/base/path_service.cc b/base/path_service.cc
new file mode 100644
index 0000000..f4b3a83
--- /dev/null
+++ b/base/path_service.cc
@@ -0,0 +1,202 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+#include <shellapi.h>
+#include <shlobj.h>
+
+#include <hash_map>
+#include <hash_set>
+
+#include "base/path_service.h"
+
+#include "base/lock.h"
+#include "base/logging.h"
+#include "base/file_util.h"
+
+namespace base {
+  bool PathProvider(int key, std::wstring* result);
+}
+
+namespace {
+
+typedef stdext::hash_map<int,std::wstring> PathMap;
+typedef stdext::hash_set<int> PathSet;
+
+// We keep a linked list of providers.  In a debug build we ensure that no two
+// providers claim overlapping keys.
+struct Provider {
+  PathService::ProviderFunc func;
+  struct Provider* next;
+#ifndef NDEBUG
+  int key_start;
+  int key_end;
+#endif
+};
+
+static Provider base_provider = {
+  base::PathProvider,
+  NULL,
+#ifndef NDEBUG
+  base::PATH_START,
+  base::PATH_END
+#endif
+};
+
+struct PathData {
+  Lock      lock;
+  PathMap   cache;      // Track mappings from path key to path value.
+  PathSet   overrides;  // Track whether a path has been overridden.
+  Provider* providers;  // Linked list of path service providers.
+
+  PathData() : providers(&base_provider) {
+  }
+};
+
+// We rely on the path service not being used prior to 'main' execution, and
+// we are happy to let this data structure leak at process exit.
+PathData* path_data = new PathData();
+
+}  // namespace
+
+// TODO(brettw): this function does not handle long paths (filename > MAX_PATH)
+// characters). This isn't supported very well by Windows right now, so it is
+// moot, but we should keep this in mind for the future.
+// static
+bool PathService::Get(int key, std::wstring* result) {
+  DCHECK(path_data);
+  DCHECK(result);
+  DCHECK(key >= base::DIR_CURRENT);
+
+  // special case the current directory because it can never be cached
+  if (key == base::DIR_CURRENT) {
+    wchar_t system_buffer[MAX_PATH];
+    system_buffer[0] = 0;
+    DWORD len = GetCurrentDirectory(MAX_PATH, system_buffer);
+    if (len == 0 || len > MAX_PATH)
+      return false;
+    *result = system_buffer;
+    file_util::TrimTrailingSeparator(result);
+    return true;
+  }
+
+  // TODO(darin): it would be nice to avoid holding this lock while calling out
+  // to the path providers.
+  AutoLock scoped_lock(path_data->lock);
+
+  // check for a cached version
+  PathMap::const_iterator it = path_data->cache.find(key);
+  if (it != path_data->cache.end()) {
+    *result = it->second;
+    return true;
+  }
+
+  std::wstring path;
+
+  // search providers for the requested path
+  Provider* provider = path_data->providers;
+  while (provider) {
+    if (provider->func(key, &path))
+      break;
+    DCHECK(path.empty()) << "provider should not have modified path";
+    provider = provider->next;
+  }
+
+  if (path.empty())
+    return false;
+
+  // Save the computed path in our cache.
+  path_data->cache[key] = path;
+
+  result->swap(path);
+  return true;
+}
+
+bool PathService::IsOverridden(int key) {
+  DCHECK(path_data);
+
+  AutoLock scoped_lock(path_data->lock);
+  return path_data->overrides.find(key) != path_data->overrides.end();
+}
+
+bool PathService::Override(int key, const std::wstring& path) {
+  DCHECK(path_data);
+  DCHECK(key > base::DIR_CURRENT) << "invalid path key";
+
+  wchar_t file_path_buf[MAX_PATH];
+  if (!_wfullpath(file_path_buf, path.c_str(), MAX_PATH))
+    return false;
+  std::wstring file_path(file_path_buf);
+
+  // make sure the directory exists:
+  if (!file_util::PathExists(file_path) &&
+      // TODO(darin): what if this path is not that of a directory?
+      !file_util::CreateDirectory(file_path))
+    return false;
+
+  file_util::TrimTrailingSeparator(&file_path);
+
+  AutoLock scoped_lock(path_data->lock);
+  path_data->cache[key] = file_path;
+  path_data->overrides.insert(key);
+  return true;
+}
+
+bool PathService::SetCurrentDirectory(const std::wstring& current_directory) {
+  BOOL ret = ::SetCurrentDirectory(current_directory.c_str());
+  return (ret ? true : false);
+}
+
+void PathService::RegisterProvider(ProviderFunc func, int key_start,
+                                   int key_end) {
+  DCHECK(path_data);
+  DCHECK(key_end > key_start);
+
+  AutoLock scoped_lock(path_data->lock);
+
+  Provider* p;
+
+#ifndef NDEBUG
+  p = path_data->providers;
+  while (p) {
+    DCHECK(key_start >= p->key_end || key_end <= p->key_start) <<
+      "path provider collision";
+    p = p->next;
+  }
+#endif
+
+  p = new Provider;
+  p->func = func;
+  p->next = path_data->providers;
+#ifndef NDEBUG
+  p->key_start = key_start;
+  p->key_end = key_end;
+#endif
+  path_data->providers = p;
+}
diff --git a/base/path_service.h b/base/path_service.h
new file mode 100644
index 0000000..861c9b5
--- /dev/null
+++ b/base/path_service.h
@@ -0,0 +1,85 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_PATH_SERVICE_H__
+#define BASE_PATH_SERVICE_H__
+
+#include <string>
+
+#include "base/base_paths.h"
+
+// The path service is a global table mapping keys to file system paths.  It is
+// OK to use this service from multiple threads.
+//
+class PathService {
+ public:
+  // Retrieves a path to a special directory or file and places it into the
+  // string pointed to by 'path'. If you ask for a directory it is guaranteed
+  // to NOT have a path separator at the end. For example, "c:\windows\temp"
+  // Directories are also guaranteed to exist when this function succeeds.
+  //
+  // Returns true if the directory or file was successfully retrieved. On
+  // failure, 'path' will not be changed.
+  static bool Get(int key, std::wstring* path);
+
+  // Overrides the path to a special directory or file.  This cannot be used to
+  // change the value of DIR_CURRENT, but that should be obvious.  Also, if the
+  // path specifies a directory that does not exist, the directory will be
+  // created by this method.  This method returns true if successful.
+  //
+  // If the given path is relative, then it will be resolved against DIR_CURRENT.
+  //
+  // WARNING: Consumers of PathService::Get may expect paths to be constant
+  // over the lifetime of the app, so this method should be used with caution.
+  static bool Override(int key, const std::wstring& path);
+
+  // Return whether a path was overridden.
+  static bool IsOverridden(int key);
+
+  // Sets the current directory.
+  static bool SetCurrentDirectory(const std::wstring& current_directory);
+
+  // To extend the set of supported keys, you can register a path provider,
+  // which is just a function mirroring PathService::Get.  The ProviderFunc
+  // returns false if it cannot provide a non-empty path for the given key.
+  // Otherwise, true is returned.
+  //
+  // WARNING: This function could be called on any thread from which the
+  // PathService is used, so a the ProviderFunc MUST BE THREADSAFE.
+  //
+  typedef bool (*ProviderFunc)(int, std::wstring*);
+
+  // Call to register a path provider.  You must specify the range "[key_start,
+  // key_end)" of supported path keys.
+  static void RegisterProvider(ProviderFunc provider,
+                               int key_start,
+                               int key_end);
+};
+
+#endif // BASE_PATH_SERVICE_H__
diff --git a/base/path_service_unittest.cc b/base/path_service_unittest.cc
new file mode 100644
index 0000000..8840650
--- /dev/null
+++ b/base/path_service_unittest.cc
@@ -0,0 +1,57 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/basictypes.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+  class PathServiceTest : public testing::Test {
+  };
+};
+
+// Returns true if PathService::Get returns true and sets the path parameter
+// to non-empty for the given PathService::DirType enumeration value.
+bool ReturnsValidPath(int dir_type) {
+  std::wstring path;
+  bool result = PathService::Get(dir_type, &path);
+  return result && !path.empty();
+}
+
+// Test that all PathService::Get calls return a value and a true result
+// in the development environment.  (This test was created because a few
+// later changes to Get broke the semantics of the function and yielded the
+// correct value while returning false.)
+TEST(PathServiceTest, Get) {
+  for (int key = base::DIR_CURRENT; key < base::PATH_END; ++key) {
+    EXPECT_PRED1(ReturnsValidPath, key);
+  }
+}
diff --git a/base/pe_image.cc b/base/pe_image.cc
new file mode 100644
index 0000000..b50bca5
--- /dev/null
+++ b/base/pe_image.cc
@@ -0,0 +1,560 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file implements PEImage, a generic class to manipulate PE files.
+// This file was adapted from GreenBorder's Code.
+
+#include "base/pe_image.h"
+
+// Structure to perform imports enumerations.
+struct EnumAllImportsStorage {
+  PEImage::EnumImportsFunction callback;
+  PVOID cookie;
+};
+
+// Callback used to enumerate imports. See EnumImportChunksFunction.
+bool ProcessImportChunk(const PEImage &image, LPCSTR module,
+                        PIMAGE_THUNK_DATA name_table,
+                        PIMAGE_THUNK_DATA iat, PVOID cookie) {
+  EnumAllImportsStorage &storage = *reinterpret_cast<EnumAllImportsStorage*>(
+                                       cookie);
+
+  return image.EnumOneImportChunk(storage.callback, module, name_table, iat,
+                                  storage.cookie);
+}
+
+// Callback used to enumerate delay imports. See EnumDelayImportChunksFunction.
+bool ProcessDelayImportChunk(const PEImage &image,
+                             PImgDelayDescr delay_descriptor,
+                             LPCSTR module, PIMAGE_THUNK_DATA name_table,
+                             PIMAGE_THUNK_DATA iat, PIMAGE_THUNK_DATA bound_iat,
+                             PIMAGE_THUNK_DATA unload_iat, PVOID cookie) {
+  EnumAllImportsStorage &storage = *reinterpret_cast<EnumAllImportsStorage*>(
+                                       cookie);
+
+  return image.EnumOneDelayImportChunk(storage.callback, delay_descriptor,
+                                       module, name_table, iat, bound_iat,
+                                       unload_iat, storage.cookie);
+}
+
+void PEImage::set_module(HMODULE module) {
+  module_ = module;
+}
+
+PIMAGE_DOS_HEADER PEImage::GetDosHeader() const {
+  return reinterpret_cast<PIMAGE_DOS_HEADER>(module_);
+}
+
+PIMAGE_NT_HEADERS PEImage::GetNTHeaders() const {
+  PIMAGE_DOS_HEADER dos_header = GetDosHeader();
+
+  return reinterpret_cast<PIMAGE_NT_HEADERS>(
+      reinterpret_cast<char*>(dos_header) + dos_header->e_lfanew);
+}
+
+PIMAGE_SECTION_HEADER PEImage::GetSectionHeader(UINT section) const {
+  PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
+  PIMAGE_SECTION_HEADER first_section = IMAGE_FIRST_SECTION(nt_headers);
+
+  if (section < nt_headers->FileHeader.NumberOfSections)
+    return first_section + section;
+  else
+    return NULL;
+}
+
+WORD PEImage::GetNumSections() const {
+  return GetNTHeaders()->FileHeader.NumberOfSections;
+}
+
+DWORD PEImage::GetImageDirectoryEntrySize(UINT directory) const {
+  PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
+
+  return nt_headers->OptionalHeader.DataDirectory[directory].Size;
+}
+
+PVOID PEImage::GetImageDirectoryEntryAddr(UINT directory) const {
+  PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
+
+  return RVAToAddr(
+      nt_headers->OptionalHeader.DataDirectory[directory].VirtualAddress);
+}
+
+PIMAGE_SECTION_HEADER PEImage::GetImageSectionFromAddr(PVOID address) const {
+  PBYTE target = reinterpret_cast<PBYTE>(address);
+  PIMAGE_SECTION_HEADER section;
+
+  for (UINT i = 0; NULL != (section = GetSectionHeader(i)); i++) {
+    // Don't use the virtual RVAToAddr.
+    PBYTE start = reinterpret_cast<PBYTE>(
+                      PEImage::RVAToAddr(section->VirtualAddress));
+
+    DWORD size = section->Misc.VirtualSize;
+
+    if ((start <= target) && (start + size > target))
+      return section;
+  }
+
+  return NULL;
+}
+
+PIMAGE_SECTION_HEADER PEImage::GetImageSectionHeaderByName(
+    LPCSTR section_name) const {
+  if (NULL == section_name)
+    return NULL;
+
+  PIMAGE_SECTION_HEADER ret = NULL;
+  int num_sections = GetNumSections();
+
+  for (int i = 0; i < num_sections; i++) {
+    PIMAGE_SECTION_HEADER section = GetSectionHeader(i);
+    if (0 == _strnicmp(reinterpret_cast<LPCSTR>(section->Name), section_name,
+                       sizeof(section->Name))) {
+      ret = section;
+      break;
+    }
+  }
+
+  return ret;
+}
+
+PDWORD PEImage::GetExportEntry(LPCSTR name) const {
+  PIMAGE_EXPORT_DIRECTORY exports = GetExportDirectory();
+
+  if (NULL == exports)
+    return NULL;
+
+  WORD ordinal = 0;
+  if (!GetProcOrdinal(name, &ordinal))
+    return NULL;
+
+  PDWORD functions = reinterpret_cast<PDWORD>(
+                         RVAToAddr(exports->AddressOfFunctions));
+
+  return functions + ordinal - exports->Base;
+}
+
+FARPROC PEImage::GetProcAddress(LPCSTR function_name) const {
+  PDWORD export_entry = GetExportEntry(function_name);
+  if (NULL == export_entry)
+    return NULL;
+
+  PBYTE function = reinterpret_cast<PBYTE>(RVAToAddr(*export_entry));
+
+  PBYTE exports = reinterpret_cast<PBYTE>(
+      GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT));
+  DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT);
+
+  // Check for forwarded exports as a special case.
+  if (exports <= function && exports + size > function)
+#pragma warning(push)
+#pragma warning(disable: 4312)
+    // This cast generates a warning because it is 32 bit specific.
+    return reinterpret_cast<FARPROC>(0xFFFFFFFF);
+#pragma warning(pop)
+
+  return reinterpret_cast<FARPROC>(function);
+}
+
+bool PEImage::GetProcOrdinal(LPCSTR function_name, WORD *ordinal) const {
+  if (NULL == ordinal)
+    return false;
+
+  PIMAGE_EXPORT_DIRECTORY exports = GetExportDirectory();
+
+  if (NULL == exports)
+    return false;
+
+  if (IsOrdinal(function_name)) {
+    *ordinal = ToOrdinal(function_name);
+  } else {
+    PDWORD names = reinterpret_cast<PDWORD>(RVAToAddr(exports->AddressOfNames));
+    PDWORD lower = names;
+    PDWORD upper = names + exports->NumberOfNames;
+    int cmp = -1;
+
+    // Binary Search for the name.
+    while (lower != upper) {
+      PDWORD middle = lower + (upper - lower) / 2;
+      LPCSTR name = reinterpret_cast<LPCSTR>(RVAToAddr(*middle));
+
+      cmp = strcmp(function_name, name);
+
+      if (cmp == 0) {
+        lower = middle;
+        break;
+      }
+
+      if (cmp > 0)
+        lower = middle + 1;
+      else
+        upper = middle;
+    }
+
+    if (cmp != 0)
+      return false;
+
+
+    PWORD ordinals = reinterpret_cast<PWORD>(
+                         RVAToAddr(exports->AddressOfNameOrdinals));
+
+    *ordinal = ordinals[lower - names] + static_cast<WORD>(exports->Base);
+  }
+
+  return true;
+}
+
+bool PEImage::EnumSections(EnumSectionsFunction callback, PVOID cookie) const {
+  PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
+  UINT num_sections = nt_headers->FileHeader.NumberOfSections;
+  PIMAGE_SECTION_HEADER section = GetSectionHeader(0);
+
+  for (UINT i = 0; i < num_sections; i++, section++) {
+    PVOID section_start = RVAToAddr(section->VirtualAddress);
+    DWORD size = section->Misc.VirtualSize;
+
+    if (!callback(*this, section, section_start, size, cookie))
+      return false;
+  }
+
+  return true;
+}
+
+bool PEImage::EnumExports(EnumExportsFunction callback, PVOID cookie) const {
+  PVOID directory = GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT);
+  DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT);
+
+  // Check if there are any exports at all.
+  if (NULL == directory || 0 == size)
+    return true;
+
+  PIMAGE_EXPORT_DIRECTORY exports = reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(
+                                        directory);
+  UINT ordinal_base = exports->Base;
+  UINT num_funcs = exports->NumberOfFunctions;
+  UINT num_names = exports->NumberOfNames;
+  PDWORD functions  = reinterpret_cast<PDWORD>(RVAToAddr(
+                          exports->AddressOfFunctions));
+  PDWORD names = reinterpret_cast<PDWORD>(RVAToAddr(exports->AddressOfNames));
+  PWORD ordinals = reinterpret_cast<PWORD>(RVAToAddr(
+                       exports->AddressOfNameOrdinals));
+
+  for (UINT count = 0; count < num_funcs; count++) {
+    PVOID func = RVAToAddr(functions[count]);
+    if (NULL == func)
+      continue;
+
+    // Check for a name.
+    LPCSTR name = NULL;
+    UINT hint;
+    for (hint = 0; hint < num_names; hint++) {
+      if (ordinals[hint] == count) {
+        name = reinterpret_cast<LPCSTR>(RVAToAddr(names[hint]));
+        break;
+      }
+    }
+
+    if (name == NULL)
+      hint = 0;
+
+    // Check for forwarded exports.
+    LPCSTR forward = NULL;
+    if (reinterpret_cast<char*>(func) >= reinterpret_cast<char*>(directory) &&
+        reinterpret_cast<char*>(func) <= reinterpret_cast<char*>(directory) +
+            size) {
+      forward = reinterpret_cast<LPCSTR>(func);
+      func = 0;
+    }
+
+    if (!callback(*this, ordinal_base + count, hint, name, func, forward,
+                  cookie))
+      return false;
+  }
+
+  return true;
+}
+
+bool PEImage::EnumRelocs(EnumRelocsFunction callback, PVOID cookie) const {
+  PVOID directory = GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_BASERELOC);
+  DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_BASERELOC);
+  PIMAGE_BASE_RELOCATION base = reinterpret_cast<PIMAGE_BASE_RELOCATION>(
+      directory);
+
+  if (directory == NULL || size < sizeof(IMAGE_BASE_RELOCATION))
+    return true;
+
+  while (base->SizeOfBlock) {
+    PWORD reloc = reinterpret_cast<PWORD>(base + 1);
+    UINT num_relocs = (base->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) /
+        sizeof(WORD);
+
+    for (UINT i = 0; i < num_relocs; i++, reloc++) {
+      WORD type = *reloc >> 12;
+      PVOID address = RVAToAddr(base->VirtualAddress + (*reloc & 0x0FFF));
+
+      if (!callback(*this, type, address, cookie))
+        return false;
+    }
+
+    base = reinterpret_cast<PIMAGE_BASE_RELOCATION>(
+               reinterpret_cast<char*>(base) + base->SizeOfBlock);
+  }
+
+  return true;
+}
+
+bool PEImage::EnumImportChunks(EnumImportChunksFunction callback,
+                               PVOID cookie) const {
+  DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_IMPORT);
+  PIMAGE_IMPORT_DESCRIPTOR import = GetFirstImportChunk();
+
+  if (import == NULL || size < sizeof(IMAGE_IMPORT_DESCRIPTOR))
+    return true;
+
+  for (; import->FirstThunk; import++) {
+    LPCSTR module_name = reinterpret_cast<LPCSTR>(RVAToAddr(import->Name));
+    PIMAGE_THUNK_DATA name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
+                                       RVAToAddr(import->OriginalFirstThunk));
+    PIMAGE_THUNK_DATA iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
+                                RVAToAddr(import->FirstThunk));
+
+    if (!callback(*this, module_name, name_table, iat, cookie))
+      return false;
+  }
+
+  return true;
+}
+
+bool PEImage::EnumOneImportChunk(EnumImportsFunction callback,
+                                 LPCSTR module_name,
+                                 PIMAGE_THUNK_DATA name_table,
+                                 PIMAGE_THUNK_DATA iat, PVOID cookie) const {
+  if (NULL == name_table)
+    return false;
+
+  for (; name_table && name_table->u1.Ordinal; name_table++, iat++) {
+    LPCSTR name = NULL;
+    WORD ordinal = 0;
+    WORD hint = 0;
+
+    if (IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) {
+      ordinal = static_cast<WORD>(IMAGE_ORDINAL32(name_table->u1.Ordinal));
+    } else {
+      PIMAGE_IMPORT_BY_NAME import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
+          RVAToAddr(name_table->u1.ForwarderString));
+
+      hint = import->Hint;
+      name = reinterpret_cast<LPCSTR>(&import->Name);
+    }
+
+    if (!callback(*this, module_name, ordinal, name, hint, iat, cookie))
+      return false;
+  }
+
+  return true;
+}
+
+bool PEImage::EnumAllImports(EnumImportsFunction callback, PVOID cookie) const {
+  EnumAllImportsStorage temp = { callback, cookie };
+  return EnumImportChunks(ProcessImportChunk, &temp);
+}
+
+bool PEImage::EnumDelayImportChunks(EnumDelayImportChunksFunction callback,
+                                    PVOID cookie) const {
+  PVOID directory = GetImageDirectoryEntryAddr(
+                        IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
+  DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
+  PImgDelayDescr delay_descriptor = reinterpret_cast<PImgDelayDescr>(directory);
+
+  if (directory == NULL || size == 0)
+    return true;
+
+  for (; delay_descriptor->rvaHmod; delay_descriptor++) {
+    PIMAGE_THUNK_DATA name_table;
+    PIMAGE_THUNK_DATA iat;
+    PIMAGE_THUNK_DATA bound_iat;    // address of the optional bound IAT
+    PIMAGE_THUNK_DATA unload_iat;   // address of optional copy of original IAT
+    LPCSTR module_name;
+
+    // check if VC7-style imports, using RVAs instead of
+    // VC6-style addresses.
+    bool rvas = (delay_descriptor->grAttrs & dlattrRva) != 0;
+
+    if (rvas) {
+      module_name = reinterpret_cast<LPCSTR>(
+                        RVAToAddr(delay_descriptor->rvaDLLName));
+      name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
+                       RVAToAddr(delay_descriptor->rvaINT));
+      iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
+                RVAToAddr(delay_descriptor->rvaIAT));
+      bound_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
+                      RVAToAddr(delay_descriptor->rvaBoundIAT));
+      unload_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
+                       RVAToAddr(delay_descriptor->rvaUnloadIAT));
+    } else {
+#pragma warning(push)
+#pragma warning(disable: 4312)
+      // These casts generate warnings because they are 32 bit specific.
+      module_name = reinterpret_cast<LPCSTR>(delay_descriptor->rvaDLLName);
+      name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
+                       delay_descriptor->rvaINT);
+      iat = reinterpret_cast<PIMAGE_THUNK_DATA>(delay_descriptor->rvaIAT);
+      bound_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
+                      delay_descriptor->rvaBoundIAT);
+      unload_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
+                       delay_descriptor->rvaUnloadIAT);
+#pragma warning(pop)
+    }
+
+    if (!callback(*this, delay_descriptor, module_name, name_table, iat,
+                  bound_iat, unload_iat, cookie))
+      return false;
+  }
+
+  return true;
+}
+
+bool PEImage::EnumOneDelayImportChunk(EnumImportsFunction callback,
+                                      PImgDelayDescr delay_descriptor,
+                                      LPCSTR module_name,
+                                      PIMAGE_THUNK_DATA name_table,
+                                      PIMAGE_THUNK_DATA iat,
+                                      PIMAGE_THUNK_DATA bound_iat,
+                                      PIMAGE_THUNK_DATA unload_iat,
+                                      PVOID cookie) const {
+  UNREFERENCED_PARAMETER(bound_iat);
+  UNREFERENCED_PARAMETER(unload_iat);
+
+  for (; name_table->u1.Ordinal; name_table++, iat++) {
+    LPCSTR name = NULL;
+    WORD ordinal = 0;
+    WORD hint = 0;
+
+    if (IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) {
+      ordinal = static_cast<WORD>(IMAGE_ORDINAL32(name_table->u1.Ordinal));
+    } else {
+      PIMAGE_IMPORT_BY_NAME import;
+      bool rvas = (delay_descriptor->grAttrs & dlattrRva) != 0;
+
+      if (rvas) {
+        import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
+                     RVAToAddr(name_table->u1.ForwarderString));
+      } else {
+#pragma warning(push)
+#pragma warning(disable: 4312)
+        // This cast generates a warning because it is 32 bit specific.
+        import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
+                     name_table->u1.ForwarderString);
+#pragma warning(pop)
+      }
+
+      hint = import->Hint;
+      name = reinterpret_cast<LPCSTR>(&import->Name);
+    }
+
+    if (!callback(*this, module_name, ordinal, name, hint, iat, cookie))
+      return false;
+  }
+
+  return true;
+}
+
+bool PEImage::EnumAllDelayImports(EnumImportsFunction callback,
+                                  PVOID cookie) const {
+  EnumAllImportsStorage temp = { callback, cookie };
+  return EnumDelayImportChunks(ProcessDelayImportChunk, &temp);
+}
+
+bool PEImage::VerifyMagic() const {
+  PIMAGE_DOS_HEADER dos_header = GetDosHeader();
+
+  if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
+    return false;
+
+  PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
+
+  if (nt_headers->Signature != IMAGE_NT_SIGNATURE)
+    return false;
+
+  if (nt_headers->FileHeader.SizeOfOptionalHeader !=
+      sizeof(IMAGE_OPTIONAL_HEADER))
+    return false;
+
+  if (nt_headers->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)
+    return false;
+
+  return true;
+}
+
+bool PEImage::ImageRVAToOnDiskOffset(DWORD rva, DWORD *on_disk_offset) const {
+  LPVOID address = RVAToAddr(rva);
+  return ImageAddrToOnDiskOffset(address, on_disk_offset);
+}
+
+bool PEImage::ImageAddrToOnDiskOffset(LPVOID address,
+                                      DWORD *on_disk_offset) const {
+  if (NULL == address)
+    return false;
+
+  // Get the section that this address belongs to.
+  PIMAGE_SECTION_HEADER section_header = GetImageSectionFromAddr(address);
+  if (NULL == section_header)
+    return false;
+
+#pragma warning(push)
+#pragma warning(disable: 4311)
+  // These casts generate warnings because they are 32 bit specific.
+  // Don't follow the virtual RVAToAddr, use the one on the base.
+  DWORD offset_within_section = reinterpret_cast<DWORD>(address) -
+                                    reinterpret_cast<DWORD>(PEImage::RVAToAddr(
+                                        section_header->VirtualAddress));
+#pragma warning(pop)
+
+  *on_disk_offset = section_header->PointerToRawData + offset_within_section;
+  return true;
+}
+
+PVOID PEImage::RVAToAddr(DWORD rva) const {
+  if (rva == 0)
+    return NULL;
+
+  return reinterpret_cast<char*>(module_) + rva;
+}
+
+PVOID PEImageAsData::RVAToAddr(DWORD rva) const {
+  if (rva == 0)
+    return NULL;
+
+  PVOID in_memory = PEImage::RVAToAddr(rva);
+  DWORD dummy;
+
+  if (!ImageAddrToOnDiskOffset(in_memory, &dummy))
+    return NULL;
+
+  return in_memory;
+}
diff --git a/base/pe_image.h b/base/pe_image.h
new file mode 100644
index 0000000..a8ff941
--- /dev/null
+++ b/base/pe_image.h
@@ -0,0 +1,282 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file was adapted from GreenBorder's Code.
+// To understand what this class is about (for other than well known functions
+// as GetProcAddress), a good starting point is "An In-Depth Look into the
+// Win32 Portable Executable File Format" by Matt Pietrek:
+// http://msdn.microsoft.com/msdnmag/issues/02/02/PE/default.aspx
+
+#ifndef BASE_SRC_PE_IMAGE_H__
+#define BASE_SRC_PE_IMAGE_H__
+
+#include <windows.h>
+#include <DelayIMP.h>
+
+// This class is a wrapper for the Portable Executable File Format (PE).
+// It's main purpose is to provide an easy way to work with imports and exports
+// from a file, mapped in memory as image.
+class PEImage {
+ public:
+  // Callback to enumerate sections.
+  // cookie is the value passed to the enumerate method.
+  // Returns true to continue the enumeration.
+  typedef bool (*EnumSectionsFunction)(const PEImage &image,
+                                       PIMAGE_SECTION_HEADER header,
+                                       PVOID section_start, DWORD section_size,
+                                       PVOID cookie);
+
+  // Callback to enumerate exports.
+  // function is the actual address of the symbol. If forward is not null, it
+  // contains the dll and symbol to forward this export to. cookie is the value
+  // passed to the enumerate method.
+  // Returns true to continue the enumeration.
+  typedef bool (*EnumExportsFunction)(const PEImage &image, DWORD ordinal,
+                                      DWORD hint, LPCSTR name, PVOID function,
+                                      LPCSTR forward, PVOID cookie);
+
+  // Callback to enumerate import blocks.
+  // name_table and iat point to the imports name table and address table for
+  // this block. cookie is the value passed to the enumerate method.
+  // Returns true to continue the enumeration.
+  typedef bool (*EnumImportChunksFunction)(const PEImage &image, LPCSTR module,
+                                           PIMAGE_THUNK_DATA name_table,
+                                           PIMAGE_THUNK_DATA iat, PVOID cookie);
+
+  // Callback to enumerate imports.
+  // module is the dll that exports this symbol. cookie is the value passed to
+  // the enumerate method.
+  // Returns true to continue the enumeration.
+  typedef bool (*EnumImportsFunction)(const PEImage &image, LPCSTR module,
+                                      DWORD ordinal, LPCSTR name, DWORD hint,
+                                      PIMAGE_THUNK_DATA iat, PVOID cookie);
+
+  // Callback to enumerate dalayed import blocks.
+  // module is the dll that exports this block of symbols. cookie is the value
+  // passed to the enumerate method.
+  // Returns true to continue the enumeration.
+  typedef bool (*EnumDelayImportChunksFunction)(const PEImage &image,
+                                                PImgDelayDescr delay_descriptor,
+                                                LPCSTR module,
+                                                PIMAGE_THUNK_DATA name_table,
+                                                PIMAGE_THUNK_DATA iat,
+                                                PIMAGE_THUNK_DATA bound_iat,
+                                                PIMAGE_THUNK_DATA unload_iat,
+                                                PVOID cookie);
+
+  // Callback to enumerate relocations.
+  // cookie is the value passed to the enumerate method.
+  // Returns true to continue the enumeration.
+  typedef bool (*EnumRelocsFunction)(const PEImage &image, WORD type,
+                                     PVOID address, PVOID cookie);
+
+  explicit PEImage(HMODULE module) : module_(module) {}
+  explicit PEImage(const void* module) {
+    module_ = reinterpret_cast<HMODULE>(const_cast<void*>(module));
+  }
+
+  // Gets the HMODULE for this object.
+  HMODULE module() const;
+
+  // Sets this object's HMODULE.
+  void set_module(HMODULE module);
+
+  // Checks if this symbol is actually an ordinal.
+  static bool IsOrdinal(LPCSTR name);
+
+  // Converts a named symbol to the corresponding ordinal.
+  static WORD ToOrdinal(LPCSTR name);
+
+  // Returns the DOS_HEADER for this PE.
+  PIMAGE_DOS_HEADER GetDosHeader() const;
+
+  // Returns the NT_HEADER for this PE.
+  PIMAGE_NT_HEADERS GetNTHeaders() const;
+
+  // Returns number of sections of this PE.
+  WORD GetNumSections() const;
+
+  // Returns the header for a given section.
+  // returns NULL if there is no such section.
+  PIMAGE_SECTION_HEADER GetSectionHeader(UINT section) const;
+
+  // Returns the size of a given directory entry.
+  DWORD GetImageDirectoryEntrySize(UINT directory) const;
+
+  // Returns the address of a given directory entry.
+  PVOID GetImageDirectoryEntryAddr(UINT directory) const;
+
+  // Returns the section header for a given address.
+  // Use: s = image.GetImageSectionFromAddr(a);
+  // Post: 's' is the section header of the section that contains 'a'
+  //       or NULL if there is no such section.
+  PIMAGE_SECTION_HEADER GetImageSectionFromAddr(PVOID address) const;
+
+  // Returns the section header for a given section.
+  PIMAGE_SECTION_HEADER GetImageSectionHeaderByName(LPCSTR section_name) const;
+
+  // Returns the first block of imports.
+  PIMAGE_IMPORT_DESCRIPTOR GetFirstImportChunk() const;
+
+  // Returns the exports directory.
+  PIMAGE_EXPORT_DIRECTORY GetExportDirectory() const;
+
+  // Returns a given export entry.
+  // Use: e = image.GetExportEntry(f);
+  // Pre: 'f' is either a zero terminated string or ordinal
+  // Post: 'e' is a pointer to the export directory entry
+  //       that contains 'f's export RVA, or NULL if 'f'
+  //       is not exported from this image
+  PDWORD GetExportEntry(LPCSTR name) const;
+
+  // Returns the address for a given exported symbol.
+  // Use: p = image.GetProcAddress(f);
+  // Pre: 'f' is either a zero terminated string or ordinal.
+  // Post: if 'f' is a non-forwarded export from image, 'p' is
+  //       the exported function. If 'f' is a forwarded export
+  //       then p is the special value 0xFFFFFFFF. In this case
+  //       RVAToAddr(*GetExportEntry) can be used to resolve
+  //       the string that describes the forward.
+  FARPROC GetProcAddress(LPCSTR function_name) const;
+
+  // Retrieves the ordinal for a given exported symbol.
+  // Returns true if the symbol was found.
+  bool GetProcOrdinal(LPCSTR function_name, WORD *ordinal) const;
+
+  // Enumerates PE sections.
+  // cookie is a generic cookie to pass to the callback.
+  // Returns true on success.
+  bool EnumSections(EnumSectionsFunction callback, PVOID cookie) const;
+
+  // Enumerates PE exports.
+  // cookie is a generic cookie to pass to the callback.
+  // Returns true on success.
+  bool EnumExports(EnumExportsFunction callback, PVOID cookie) const;
+
+  // Enumerates PE imports.
+  // cookie is a generic cookie to pass to the callback.
+  // Returns true on success.
+  bool EnumAllImports(EnumImportsFunction callback, PVOID cookie) const;
+
+  // Enumerates PE import blocks.
+  // cookie is a generic cookie to pass to the callback.
+  // Returns true on success.
+  bool EnumImportChunks(EnumImportChunksFunction callback, PVOID cookie) const;
+
+  // Enumerates the imports from a single PE import block.
+  // cookie is a generic cookie to pass to the callback.
+  // Returns true on success.
+  bool EnumOneImportChunk(EnumImportsFunction callback, LPCSTR module_name,
+                          PIMAGE_THUNK_DATA name_table, PIMAGE_THUNK_DATA iat,
+                          PVOID cookie) const;
+
+
+  // Enumerates PE delay imports.
+  // cookie is a generic cookie to pass to the callback.
+  // Returns true on success.
+  bool EnumAllDelayImports(EnumImportsFunction callback, PVOID cookie) const;
+
+  // Enumerates PE delay import blocks.
+  // cookie is a generic cookie to pass to the callback.
+  // Returns true on success.
+  bool EnumDelayImportChunks(EnumDelayImportChunksFunction callback,
+                             PVOID cookie) const;
+
+  // Enumerates imports from a single PE delay import block.
+  // cookie is a generic cookie to pass to the callback.
+  // Returns true on success.
+  bool EnumOneDelayImportChunk(EnumImportsFunction callback,
+                               PImgDelayDescr delay_descriptor,
+                               LPCSTR module_name,
+                               PIMAGE_THUNK_DATA name_table,
+                               PIMAGE_THUNK_DATA iat,
+                               PIMAGE_THUNK_DATA bound_iat,
+                               PIMAGE_THUNK_DATA unload_iat,
+                               PVOID cookie) const;
+
+  // Enumerates PE relocation entries.
+  // cookie is a generic cookie to pass to the callback.
+  // Returns true on success.
+  bool EnumRelocs(EnumRelocsFunction callback, PVOID cookie) const;
+
+  // Verifies the magic values on the PE file.
+  // Returns true if all values are correct.
+  bool VerifyMagic() const;
+
+  // Converts an rva value to the appropriate address.
+  virtual PVOID RVAToAddr(DWORD rva) const;
+
+  // Converts an rva value to an offset on disk.
+  // Returns true on success.
+  bool ImageRVAToOnDiskOffset(DWORD rva, DWORD *on_disk_offset) const;
+
+  // Converts an address to an offset on disk.
+  // Returns true on success.
+  bool ImageAddrToOnDiskOffset(LPVOID address, DWORD *on_disk_offset) const;
+
+ private:
+  HMODULE module_;
+};
+
+// This class is an extension to the PEImage class that allows working with PE
+// files mapped as data instead of as image file.
+class PEImageAsData : public PEImage {
+ public:
+  explicit PEImageAsData(HMODULE hModule) : PEImage(hModule) {}
+
+  virtual PVOID RVAToAddr(DWORD rva) const;
+};
+
+inline bool PEImage::IsOrdinal(LPCSTR name) {
+#pragma warning(push)
+#pragma warning(disable: 4311)
+  // This cast generates a warning because it is 32 bit specific.
+  return reinterpret_cast<DWORD>(name) <= 0xFFFF;
+#pragma warning(pop)
+}
+
+inline WORD PEImage::ToOrdinal(LPCSTR name) {
+  return reinterpret_cast<WORD>(name);
+}
+
+inline HMODULE PEImage::module() const {
+  return module_;
+}
+
+inline PIMAGE_IMPORT_DESCRIPTOR PEImage::GetFirstImportChunk() const {
+  return reinterpret_cast<PIMAGE_IMPORT_DESCRIPTOR>(
+             GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_IMPORT));
+}
+
+inline PIMAGE_EXPORT_DIRECTORY PEImage::GetExportDirectory() const {
+  return reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(
+             GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT));
+}
+
+#endif  // BASE_SRC_PE_IMAGE_H__
diff --git a/base/pe_image_unittest.cc b/base/pe_image_unittest.cc
new file mode 100644
index 0000000..31c99a5
--- /dev/null
+++ b/base/pe_image_unittest.cc
@@ -0,0 +1,226 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file contains unit tests for PEImage.
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "base/pe_image.h"
+
+// Just counts the number of invocations.
+bool ExportsCallback(const PEImage &image,
+                     DWORD ordinal,
+                     DWORD hint,
+                     LPCSTR name,
+                     PVOID function,
+                     LPCSTR forward,
+                     PVOID cookie) {
+  int* count = reinterpret_cast<int*>(cookie);
+  (*count)++;
+  return true;
+}
+
+// Just counts the number of invocations.
+bool ImportsCallback(const PEImage &image,
+                     LPCSTR module,
+                     DWORD ordinal,
+                     LPCSTR name,
+                     DWORD hint,
+                     PIMAGE_THUNK_DATA iat,
+                     PVOID cookie) {
+  int* count = reinterpret_cast<int*>(cookie);
+  (*count)++;
+  return true;
+}
+
+// Just counts the number of invocations.
+bool SectionsCallback(const PEImage &image,
+                       PIMAGE_SECTION_HEADER header,
+                       PVOID section_start,
+                       DWORD section_size,
+                       PVOID cookie) {
+  int* count = reinterpret_cast<int*>(cookie);
+  (*count)++;
+  return true;
+}
+
+// Just counts the number of invocations.
+bool RelocsCallback(const PEImage &image,
+                    WORD type,
+                    PVOID address,
+                    PVOID cookie) {
+  int* count = reinterpret_cast<int*>(cookie);
+  (*count)++;
+  return true;
+}
+
+// Just counts the number of invocations.
+bool ImportChunksCallback(const PEImage &image,
+                          LPCSTR module,
+                          PIMAGE_THUNK_DATA name_table,
+                          PIMAGE_THUNK_DATA iat,
+                          PVOID cookie) {
+  int* count = reinterpret_cast<int*>(cookie);
+  (*count)++;
+  return true;
+}
+
+// Just counts the number of invocations.
+bool DelayImportChunksCallback(const PEImage &image,
+                               PImgDelayDescr delay_descriptor,
+                               LPCSTR module,
+                               PIMAGE_THUNK_DATA name_table,
+                               PIMAGE_THUNK_DATA iat,
+                               PIMAGE_THUNK_DATA bound_iat,
+                               PIMAGE_THUNK_DATA unload_iat,
+                               PVOID cookie) {
+  int* count = reinterpret_cast<int*>(cookie);
+  (*count)++;
+  return true;
+}
+
+// We'll be using some known values for the tests.
+enum Value {
+  sections = 0,
+  imports_dlls,
+  delay_dlls,
+  exports,
+  imports,
+  delay_imports,
+  relocs
+};
+
+// Retrieves the expected value from advapi32.dll based on the OS.
+int GetExpectedValue(Value value, DWORD os) {
+  const int xp_delay_dlls = 2;
+  const int xp_exports = 675;
+  const int xp_imports = 422;
+  const int xp_delay_imports = 8;
+  const int xp_relocs = 9180;
+  const int vista_delay_dlls = 4;
+  const int vista_exports = 799;
+  const int vista_imports = 476;
+  const int vista_delay_imports = 24;
+  const int vista_relocs = 10188;
+  const int w2k_delay_dlls = 0;
+  const int w2k_exports = 566;
+  const int w2k_imports = 357;
+  const int w2k_delay_imports = 0;
+  const int w2k_relocs = 7388;
+
+  // Contains the expected value, for each enumerated property (Value), and the
+  // OS version: [Value][os_version]
+  const int expected[][3] = {
+    {4, 4, 4},
+    {3, 3, 3},
+    {w2k_delay_dlls, xp_delay_dlls, vista_delay_dlls},
+    {w2k_exports, xp_exports, vista_exports},
+    {w2k_imports, xp_imports, vista_imports},
+    {w2k_delay_imports, xp_delay_imports, vista_delay_imports},
+    {w2k_relocs, xp_relocs, vista_relocs}
+  };
+
+  if (value > relocs)
+    return 0;
+  if (50 == os)
+    os = 0;  // 5.0
+  else if (51 == os || 52 == os)
+    os = 1;
+  else if (os >= 60)
+    os = 2;  // 6.x
+  else
+    return 0;
+
+  return expected[value][os];
+}
+
+// Tests that we are able to enumerate stuff from a PE file, and that
+// the actual number of items found is within the expected range.
+TEST(PEImageTest, EnumeratesPE) {
+  HMODULE module = LoadLibrary(L"advapi32.dll");
+  ASSERT_TRUE(NULL != module);
+
+  PEImage pe(module);
+  int count = 0;
+  EXPECT_TRUE(pe.VerifyMagic());
+
+  DWORD os = pe.GetNTHeaders()->OptionalHeader.MajorOperatingSystemVersion;
+  os = os * 10 + pe.GetNTHeaders()->OptionalHeader.MinorOperatingSystemVersion;
+
+  pe.EnumSections(SectionsCallback, &count);
+  EXPECT_EQ(GetExpectedValue(sections, os), count);
+
+  count = 0;
+  pe.EnumImportChunks(ImportChunksCallback, &count);
+  EXPECT_EQ(GetExpectedValue(imports_dlls, os), count);
+
+  count = 0;
+  pe.EnumDelayImportChunks(DelayImportChunksCallback, &count);
+  EXPECT_EQ(GetExpectedValue(delay_dlls, os), count);
+
+  count = 0;
+  pe.EnumExports(ExportsCallback, &count);
+  EXPECT_GT(count, GetExpectedValue(exports, os) - 20);
+  EXPECT_LT(count, GetExpectedValue(exports, os) + 100);
+
+  count = 0;
+  pe.EnumAllImports(ImportsCallback, &count);
+  EXPECT_GT(count, GetExpectedValue(imports, os) - 20);
+  EXPECT_LT(count, GetExpectedValue(imports, os) + 100);
+
+  count = 0;
+  pe.EnumAllDelayImports(ImportsCallback, &count);
+  EXPECT_GT(count, GetExpectedValue(delay_imports, os) - 2);
+  EXPECT_LT(count, GetExpectedValue(delay_imports, os) + 8);
+
+  count = 0;
+  pe.EnumRelocs(RelocsCallback, &count);
+  EXPECT_GT(count, GetExpectedValue(relocs, os) - 150);
+  EXPECT_LT(count, GetExpectedValue(relocs, os) + 1500);
+
+  FreeLibrary(module);
+}
+
+// Tests that we can locate an specific exported symbol, by name and by ordinal.
+TEST(PEImageTest, RetrievesExports) {
+  HMODULE module = LoadLibrary(L"advapi32.dll");
+  ASSERT_TRUE(NULL != module);
+
+  PEImage pe(module);
+  WORD ordinal;
+
+  EXPECT_TRUE(pe.GetProcOrdinal("RegEnumKeyExW", &ordinal));
+
+  FARPROC address1 = pe.GetProcAddress("RegEnumKeyExW");
+  FARPROC address2 = pe.GetProcAddress(reinterpret_cast<char*>(ordinal));
+  EXPECT_TRUE(address1 != NULL);
+  EXPECT_TRUE(address2 != NULL);
+  EXPECT_TRUE(address1 == address2);
+
+  FreeLibrary(module);
+}
diff --git a/base/perftimer.cc b/base/perftimer.cc
new file mode 100644
index 0000000..442c4d3
--- /dev/null
+++ b/base/perftimer.cc
@@ -0,0 +1,69 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <stdio.h>
+#include <shlwapi.h>
+#include <windows.h>
+
+#include "base/perftimer.h"
+
+#include "base/logging.h"
+#include "base/basictypes.h"
+
+static FILE* perf_log_file = NULL;
+
+bool InitPerfLog(const char* log_file) {
+  if (perf_log_file) {
+    // trying to initialize twice
+    NOTREACHED();
+    return false;
+  }
+
+  return fopen_s(&perf_log_file, log_file, "w") == 0;
+}
+
+void FinalizePerfLog() {
+  if (!perf_log_file) {
+    // trying to cleanup without initializing
+    NOTREACHED();
+    return;
+  }
+  fclose(perf_log_file);
+}
+
+void LogPerfResult(const char* test_name, double value, const char* units) {
+  if (!perf_log_file) {
+    NOTREACHED();
+    return;
+  }
+
+  fprintf(perf_log_file, "%s\t%g\t%s\n", test_name, value, units);
+  printf("%s\t%g\t%s\n", test_name, value, units);
+}
+
diff --git a/base/perftimer.h b/base/perftimer.h
new file mode 100644
index 0000000..03a7713
--- /dev/null
+++ b/base/perftimer.h
@@ -0,0 +1,105 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_PERFTTIMER_H__
+#define BASE_PERFTTIMER_H__
+
+#include <string>
+#include "base/basictypes.h"
+#include "base/time.h"
+
+// ----------------------------------------------------------------------
+// Initializes and finalizes the perf log. These functions should be
+// called at the beginning and end (respectively) of running all the
+// performance tests. The init function returns true on success.
+// ----------------------------------------------------------------------
+bool InitPerfLog(const char* log_file);
+void FinalizePerfLog();
+
+// ----------------------------------------------------------------------
+// LogPerfResult
+//   Writes to the perf result log the given 'value' resulting from the
+//   named 'test'. The units are to aid in reading the log by people.
+// ----------------------------------------------------------------------
+void LogPerfResult(const char* test_name, double value, const char* units);
+
+// ----------------------------------------------------------------------
+// PerfTimer
+//   A simple wrapper around Now()
+// ----------------------------------------------------------------------
+class PerfTimer {
+ public:
+  PerfTimer() {
+    begin_ = TimeTicks::Now();
+  }
+
+  // Returns the time elapsed since object construction
+  TimeDelta Elapsed() const {
+    return TimeTicks::Now() - begin_;
+  }
+
+ private:
+  TimeTicks begin_;
+};
+
+// ----------------------------------------------------------------------
+// PerfTimeLogger
+//   Automates calling LogPerfResult for the common case where you want
+//   to measure the time that something took. Call Done() when the test
+//   is complete if you do extra work after the test or there are stack
+//   objects with potentially expensive constructors. Otherwise, this
+//   class with automatically log on destruction.
+// ----------------------------------------------------------------------
+class PerfTimeLogger {
+ public:
+  explicit PerfTimeLogger(const char* test_name)
+      : logged_(false),
+        test_name_(test_name) {
+  }
+
+  ~PerfTimeLogger() {
+    if (!logged_)
+      Done();
+  }
+
+  void Done() {
+    // we use a floating-point millisecond value because it is more
+    // intuitive than microseconds and we want more precision than
+    // integer milliseconds
+    LogPerfResult(test_name_.c_str(), timer_.Elapsed().InMillisecondsF(), "ms");
+    logged_ = true;
+  }
+
+ private:
+  bool logged_;
+  std::string test_name_;
+  PerfTimer timer_;
+};
+
+#endif  // BASE_PERFTTIMER_H__
diff --git a/base/pickle.cc b/base/pickle.cc
new file mode 100644
index 0000000..a16368b
--- /dev/null
+++ b/base/pickle.cc
@@ -0,0 +1,348 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <stdlib.h>
+#include <string>
+
+#include "base/pickle.h"
+
+//------------------------------------------------------------------------------
+
+// static
+const int Pickle::kPayloadUnit = 64;
+
+// Payload is uint32 aligned.
+
+Pickle::Pickle()
+    : header_(NULL),
+      header_size_(sizeof(Header)),
+      capacity_(0),
+      variable_buffer_offset_(0) {
+  Resize(kPayloadUnit);
+  header_->payload_size = 0;
+}
+
+Pickle::Pickle(int header_size)
+    : header_(NULL),
+      header_size_(AlignInt(header_size, sizeof(uint32))),
+      capacity_(0),
+      variable_buffer_offset_(0) {
+  DCHECK(header_size >= sizeof(Header));
+  DCHECK(header_size <= kPayloadUnit);
+  Resize(kPayloadUnit);
+  header_->payload_size = 0;
+}
+
+Pickle::Pickle(const char* data, int data_len)
+    : header_(reinterpret_cast<Header*>(const_cast<char*>(data))),
+      header_size_(data_len - header_->payload_size),
+      capacity_(-1),
+      variable_buffer_offset_(0) {
+  DCHECK(header_size_ >= sizeof(Header));
+  DCHECK(header_size_ == AlignInt(header_size_, sizeof(uint32)));
+}
+
+Pickle::Pickle(const Pickle& other)
+    : header_(NULL),
+      header_size_(other.header_size_),
+      capacity_(0),
+      variable_buffer_offset_(other.variable_buffer_offset_) {
+  size_t payload_size = header_size_ + other.header_->payload_size;
+  bool resized = Resize(payload_size);
+  CHECK(resized);  // Realloc failed.
+  memcpy(header_, other.header_, payload_size);
+}
+
+Pickle::~Pickle() {
+  if (capacity_ != -1)
+    free(header_);
+}
+
+Pickle& Pickle::operator=(const Pickle& other) {
+  if (header_size_ != other.header_size_ && capacity_ != -1) {
+    free(header_);
+    header_ = NULL;
+    header_size_ = other.header_size_;
+  }
+  bool resized = Resize(other.header_size_ + other.header_->payload_size);
+  CHECK(resized);  // Realloc failed.
+  memcpy(header_, other.header_, header_size_ + other.header_->payload_size);
+  variable_buffer_offset_ = other.variable_buffer_offset_;
+  return *this;
+}
+
+bool Pickle::ReadBool(void** iter, bool* result) const {
+  DCHECK(iter);
+
+  int tmp;
+  if (!ReadInt(iter, &tmp))
+    return false;
+  DCHECK(0 == tmp || 1 == tmp);
+  *result = tmp ? true : false;
+  return true;
+}
+
+bool Pickle::ReadInt(void** iter, int* result) const {
+  DCHECK(iter);
+  if (!*iter)
+    *iter = const_cast<char*>(payload());
+
+  if (!IteratorHasRoomFor(*iter, sizeof(*result)))
+    return false;
+
+  // TODO(jar) bug 1129285: Pickle should be cleaned up, and not dependent on
+  // alignment.
+  // Next line is otherwise the same as: memcpy(result, *iter, sizeof(*result));
+  *result = *reinterpret_cast<int*>(*iter);
+
+  UpdateIter(iter, sizeof(*result));
+  return true;
+}
+
+bool Pickle::ReadLength(void** iter, int* result) const {
+  if (!ReadInt(iter, result))
+    return false;
+  return ((*result) >= 0);
+}
+
+bool Pickle::ReadSize(void** iter, size_t* result) const {
+  DCHECK(iter);
+  if (!*iter)
+    *iter = const_cast<char*>(payload());
+
+  if (!IteratorHasRoomFor(*iter, sizeof(*result)))
+    return false;
+
+  // TODO(jar) bug 1129285: Pickle should be cleaned up, and not dependent on
+  // alignment.
+  // Next line is otherwise the same as: memcpy(result, *iter, sizeof(*result));
+  *result = *reinterpret_cast<size_t*>(*iter);
+
+  UpdateIter(iter, sizeof(*result));
+  return true;
+}
+
+bool Pickle::ReadInt64(void** iter, int64* result) const {
+  DCHECK(iter);
+  if (!*iter)
+    *iter = const_cast<char*>(payload());
+
+  if (!IteratorHasRoomFor(*iter, sizeof(*result)))
+    return false;
+
+  memcpy(result, *iter, sizeof(*result));
+
+  UpdateIter(iter, sizeof(*result));
+  return true;
+}
+
+bool Pickle::ReadIntPtr(void** iter, intptr_t* result) const {
+  DCHECK(iter);
+  if (!*iter)
+    *iter = const_cast<char*>(payload());
+
+  if (!IteratorHasRoomFor(*iter, sizeof(*result)))
+    return false;
+
+  memcpy(result, *iter, sizeof(*result));
+
+  UpdateIter(iter, sizeof(*result));
+  return true;
+}
+
+bool Pickle::ReadString(void** iter, std::string* result) const {
+  DCHECK(iter);
+
+  int len;
+  if (!ReadLength(iter, &len))
+    return false;
+  if (!IteratorHasRoomFor(*iter, len))
+    return false;
+
+  char* chars = reinterpret_cast<char*>(*iter);
+  result->assign(chars, len);
+
+  UpdateIter(iter, len);
+  return true;
+}
+
+bool Pickle::ReadWString(void** iter, std::wstring* result) const {
+  DCHECK(iter);
+
+  int len;
+  if (!ReadLength(iter, &len))
+    return false;
+  if (!IteratorHasRoomFor(*iter, len * sizeof(wchar_t)))
+    return false;
+
+  wchar_t* chars = reinterpret_cast<wchar_t*>(*iter);
+  result->assign(chars, len);
+
+  UpdateIter(iter, len * sizeof(wchar_t));
+  return true;
+}
+
+bool Pickle::ReadBytes(void** iter, const char** data, int length) const {
+  DCHECK(iter);
+  DCHECK(data);
+
+  if (!IteratorHasRoomFor(*iter, length))
+    return false;
+
+  *data = reinterpret_cast<const char*>(*iter);
+
+  UpdateIter(iter, length);
+  return true;
+}
+
+bool Pickle::ReadData(void** iter, const char** data, int* length) const {
+  DCHECK(iter);
+  DCHECK(data);
+  DCHECK(length);
+
+  if (!ReadLength(iter, length))
+    return false;
+
+  return ReadBytes(iter, data, *length);
+}
+
+char* Pickle::BeginWrite(size_t length) {
+  // write at a uint32-aligned offset from the beginning of the header
+  size_t offset = AlignInt(header_->payload_size, sizeof(uint32));
+
+  size_t new_size = offset + length;
+  if (header_size_ + new_size > capacity_ && !Resize(header_size_ + new_size))
+    return NULL;
+
+  header_->payload_size = new_size;
+  return payload() + offset;
+}
+
+void Pickle::EndWrite(char* dest, int length) {
+  // Zero-pad to keep tools like purify from complaining about uninitialized
+  // memory.
+  if (length % sizeof(uint32))
+    memset(dest + length, 0, sizeof(uint32) - (length % sizeof(uint32)));
+}
+
+bool Pickle::WriteBytes(const void* data, int data_len) {
+  DCHECK(capacity_ != -1) << "oops: pickle is readonly";
+
+  char* dest = BeginWrite(data_len);
+  if (!dest)
+    return false;
+
+  memcpy(dest, data, data_len);
+
+  EndWrite(dest, data_len);
+  return true;
+}
+
+bool Pickle::WriteString(const std::string& value) {
+  if (!WriteInt(static_cast<int>(value.size())))
+    return false;
+
+  return WriteBytes(value.data(), static_cast<int>(value.size()));
+}
+
+bool Pickle::WriteWString(const std::wstring& value) {
+  if (!WriteInt(static_cast<int>(value.size())))
+    return false;
+
+  return WriteBytes(value.data(),
+                    static_cast<int>(value.size() * sizeof(value.data()[0])));
+}
+
+bool Pickle::WriteData(const char* data, int length) {
+  return WriteInt(length) && WriteBytes(data, length);
+}
+
+char* Pickle::BeginWriteData(int length) {
+  DCHECK_EQ(variable_buffer_offset_, 0) <<
+    "There can only be one variable buffer in a Pickle";
+
+  if (!WriteInt(length))
+    return false;
+
+  char *data_ptr = BeginWrite(length);
+  if (!data_ptr)
+    return NULL;
+
+  variable_buffer_offset_ =
+      data_ptr - reinterpret_cast<char*>(header_) - sizeof(int);
+
+  // EndWrite doesn't necessarily have to be called after the write operation,
+  // so we call it here to pad out what the caller will eventually write.
+  EndWrite(data_ptr, length);
+  return data_ptr;
+}
+
+void Pickle::TrimWriteData(int length) {
+  DCHECK(variable_buffer_offset_ != 0);
+
+  VariableLengthBuffer *buffer = reinterpret_cast<VariableLengthBuffer*>(
+      reinterpret_cast<char*>(header_) + variable_buffer_offset_);
+
+  DCHECK_GE(buffer->length, length);
+
+  int old_length = buffer->length;
+  int trimmed_bytes = old_length - length;
+  if (trimmed_bytes > 0) {
+    header_->payload_size -= trimmed_bytes;
+    buffer->length = length;
+  }
+}
+
+bool Pickle::Resize(size_t new_capacity) {
+  new_capacity = AlignInt(new_capacity, kPayloadUnit);
+
+  void* p = realloc(header_, new_capacity);
+  if (!p)
+    return false;
+
+  header_ = reinterpret_cast<Header*>(p);
+  capacity_ = new_capacity;
+  return true;
+}
+
+// static
+const char* Pickle::FindNext(size_t header_size,
+                             const char* start,
+                             const char* end) {
+  DCHECK(header_size == AlignInt(header_size, sizeof(uint32)));
+  DCHECK(header_size <= kPayloadUnit);
+
+  const Header* hdr = reinterpret_cast<const Header*>(start);
+  const char* payload_base = start + header_size;
+  const char* payload_end = payload_base + hdr->payload_size;
+  if (payload_end < payload_base)
+    return NULL;
+
+  return (payload_end > end) ? NULL : payload_end;
+}
diff --git a/base/pickle.h b/base/pickle.h
new file mode 100644
index 0000000..5e97ff5
--- /dev/null
+++ b/base/pickle.h
@@ -0,0 +1,259 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_PICKLE_H__
+#define BASE_PICKLE_H__
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest_prod.h"
+
+// This class provides facilities for basic binary value packing and unpacking.
+//
+// The Pickle class supports appending primitive values (ints, strings, etc.)
+// to a pickle instance.  The Pickle instance grows its internal memory buffer
+// dynamically to hold the sequence of primitive values.   The internal memory
+// buffer is exposed as the "data" of the Pickle.  This "data" can be passed
+// to a Pickle object to initialize it for reading.
+//
+// When reading from a Pickle object, it is important for the consumer to know
+// what value types to read and in what order to read them as the Pickle does
+// not keep track of the type of data written to it.
+//
+// The Pickle's data has a header which contains the size of the Pickle's
+// payload.  It can optionally support additional space in the header.  That
+// space is controlled by the header_size parameter passed to the Pickle
+// constructor.
+//
+class Pickle {
+ public:
+  ~Pickle();
+
+  // Initialize a Pickle object using the default header size.
+  Pickle();
+
+  // Initialize a Pickle object with the specified header size in bytes, which
+  // must be greater-than-or-equal-to sizeof(Pickle::Header).  The header size
+  // will be rounded up to ensure that the header size is 32bit-aligned.
+  explicit Pickle(int header_size);
+
+  // Initializes a Pickle from a const block of data.  The data is not copied;
+  // instead the data is merely referenced by this Pickle.  Only const methods
+  // should be used on the Pickle when initialized this way.  The header
+  // padding size is deduced from the data length.
+  Pickle(const char* data, int data_len);
+
+  // Initializes a Pickle as a deep copy of another Pickle.
+  Pickle(const Pickle& other);
+
+  // Performs a deep copy.
+  Pickle& operator=(const Pickle& other);
+
+  // Returns the size of the Pickle's data.
+  int size() const { return static_cast<int>(header_size_ +
+                                             header_->payload_size); }
+
+  // Returns the data for this Pickle.
+  const void* data() const { return header_; }
+
+  // Methods for reading the payload of the Pickle.  To read from the start of
+  // the Pickle, initialize *iter to NULL.  If successful, these methods return
+  // true.  Otherwise, false is returned to indicate that the result could not
+  // be extracted.
+  bool ReadBool(void** iter, bool* result) const;
+  bool ReadInt(void** iter, int* result) const;
+  bool ReadSize(void** iter, size_t* result) const;
+  bool ReadInt64(void** iter, int64* result) const;
+  bool ReadIntPtr(void** iter, intptr_t* result) const;
+  bool ReadString(void** iter, std::string* result) const;
+  bool ReadWString(void** iter, std::wstring* result) const;
+  bool ReadData(void** iter, const char** data, int* length) const;
+  bool ReadBytes(void** iter, const char** data, int length) const;
+
+  // Safer version of ReadInt() checks for the result not being negative.
+  // Use it for reading the object sizes.
+  bool Pickle::ReadLength(void** iter, int* result) const;
+
+  // Methods for adding to the payload of the Pickle.  These values are
+  // appended to the end of the Pickle's payload.  When reading values from a
+  // Pickle, it is important to read them in the order in which they were added
+  // to the Pickle.
+  bool WriteBool(bool value) {
+    return WriteInt(value ? 1 : 0);
+  }
+  bool WriteInt(int value) {
+    return WriteBytes(&value, sizeof(value));
+  }
+  bool WriteSize(size_t value) {
+    return WriteBytes(&value, sizeof(value));
+  }
+  bool WriteInt64(int64 value) {
+    return WriteBytes(&value, sizeof(value));
+  }
+  bool WriteIntPtr(intptr_t value) {
+    return WriteBytes(&value, sizeof(value));
+  }
+  bool WriteString(const std::string& value);
+  bool WriteWString(const std::wstring& value);
+  bool WriteData(const char* data, int length);
+  bool WriteBytes(const void* data, int data_len);
+
+  // Same as WriteData, but allows the caller to write directly into the
+  // Pickle. This saves a copy in cases where the data is not already
+  // available in a buffer. The caller should take care to not write more
+  // than the length it declares it will. Use ReadData to get the data.
+  // Returns NULL on failure.
+  //
+  // The returned pointer will only be valid until the next write operation
+  // on this Pickle.
+  char* BeginWriteData(int length);
+
+  // For Pickles which contain variable length buffers (e.g. those created
+  // with BeginWriteData), the Pickle can
+  // be 'trimmed' if the amount of data required is less than originally
+  // requested.  For example, you may have created a buffer with 10K of data,
+  // but decided to only fill 10 bytes of that data.  Use this function
+  // to trim the buffer so that we don't send 9990 bytes of unused data.
+  // You cannot increase the size of the variable buffer; only shrink it.
+  // This function assumes that the length of the variable buffer has
+  // not been changed.
+  void TrimWriteData(int length);
+
+  // payload follows after allocation of Header (header size is customizable)
+  struct Header {
+    size_t payload_size;  // specifies the size of the payload
+  };
+
+  // Returns the header, cast to a user-specified type T.  The type T must be a
+  // subclass of Header and its size must correspond to the header_size passed
+  // to the Pickle constructor.
+  template <class T>
+  T* headerT() {
+    DCHECK(sizeof(T) == header_size_);
+    return static_cast<T*>(header_);
+  }
+  template <class T>
+  const T* headerT() const {
+    DCHECK(sizeof(T) == header_size_);
+    return static_cast<const T*>(header_);
+  }
+
+  // Returns true if the given iterator could point to data with the given
+  // length. If there is no room for the given data before the end of the
+  // payload, returns false.
+  bool IteratorHasRoomFor(const void* iter, int len) const {
+    if ((len < 0) || (iter < header_) || iter > end_of_payload())
+      return false;
+    const char* end_of_region = reinterpret_cast<const char*>(iter) + len;
+    // Watch out for overflow in pointer calculation, which wraps.
+    return (iter <= end_of_region) && (end_of_region <= end_of_payload());
+  }
+
+ protected:
+  size_t payload_size() const { return header_->payload_size; }
+
+  char* payload() {
+    return reinterpret_cast<char*>(header_) + header_size_;
+  }
+  const char* payload() const {
+    return reinterpret_cast<const char*>(header_) + header_size_;
+  }
+
+  // Returns the address of the byte immediately following the currently valid
+  // header + payload.
+  char* end_of_payload() {
+    return payload() + payload_size();
+  }
+  const char* end_of_payload() const {
+    return payload() + payload_size();
+  }
+
+  size_t capacity() const {
+    return capacity_;
+  }
+
+  // Resizes the buffer for use when writing the specified amount of data. The
+  // location that the data should be written at is returned, or NULL if there
+  // was an error. Call EndWrite with the returned offset and the given length
+  // to pad out for the next write.
+  char* BeginWrite(size_t length);
+
+  // Completes the write operation by padding the data with NULL bytes until it
+  // is padded. Should be paired with BeginWrite, but it does not necessarily
+  // have to be called after the data is written.
+  void EndWrite(char* dest, int length);
+
+  // Resize the capacity, note that the input value should include the size of
+  // the header: new_capacity = sizeof(Header) + desired_payload_capacity.
+  // A realloc() failure will cause a Resize failure... and caller should check
+  // the return result for true (i.e., successful resizing).
+  bool Resize(size_t new_capacity);
+
+  // Aligns 'i' by rounding it up to the next multiple of 'alignment'
+  static inline size_t AlignInt(size_t i, int alignment) {
+    return i + (alignment - (i % alignment)) % alignment;
+  }
+
+  // Moves the iterator by the given number of bytes, making sure it is aligned.
+  // Pointer (iterator) is NOT aligned, but the change in the pointer
+  // is guaranteed to be a multiple of sizeof(uint32).
+  static inline void UpdateIter(void** iter, int bytes) {
+    *iter = static_cast<char*>(*iter) + AlignInt(bytes, sizeof(uint32));
+  }
+
+  // Find the end of the pickled data that starts at range_start.  Returns NULL
+  // if the entire Pickle is not found in the given data range.
+  static const char* FindNext(size_t header_size,
+                              const char* range_start,
+                              const char* range_end);
+
+  // The allocation granularity of the payload.
+  static const int kPayloadUnit;
+
+ private:
+  // A buffer of variable length; used internally
+  struct VariableLengthBuffer {
+    int length;
+    char data;   // This is variable length.
+  };
+
+  Header* header_;
+  size_t header_size_;  // Supports extra data between header and payload.
+  // Allocation size of payload (or -1 if allocation is const).
+  size_t capacity_;
+  size_t variable_buffer_offset_;  // IF non-zero, then offset to a buffer.
+
+  FRIEND_TEST(PickleTest, Resize);
+  FRIEND_TEST(PickleTest, FindNext);
+  FRIEND_TEST(PickleTest, IteratorHasRoom);
+};
+
+#endif  // BASE_PICKLE_H__
diff --git a/base/pickle_unittest.cc b/base/pickle_unittest.cc
new file mode 100644
index 0000000..6780e21
--- /dev/null
+++ b/base/pickle_unittest.cc
@@ -0,0 +1,238 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#include <windows.h>
+#include <string.h>
+
+#include "base/basictypes.h"
+#include "base/check_handler.h"
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "base/pickle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const int testint = 2093847192;
+const std::string teststr("Hello world"); // note non-aligned string length
+const std::wstring testwstr(L"Hello, world");
+const char testdata[] = "AAA\0BBB\0";
+const int testdatalen = arraysize(testdata) - 1;
+const bool testbool1 = false;
+const bool testbool2 = true;
+
+// checks that the result
+void VerifyResult(const Pickle& pickle) {
+  void* iter = NULL;
+
+  int outint;
+  EXPECT_TRUE(pickle.ReadInt(&iter, &outint));
+  EXPECT_EQ(testint, outint);
+
+  std::string outstr;
+  EXPECT_TRUE(pickle.ReadString(&iter, &outstr));
+  EXPECT_EQ(teststr, outstr);
+
+  std::wstring outwstr;
+  EXPECT_TRUE(pickle.ReadWString(&iter, &outwstr));
+  EXPECT_EQ(testwstr, outwstr);
+
+  bool outbool;
+  EXPECT_TRUE(pickle.ReadBool(&iter, &outbool));
+  EXPECT_EQ(testbool1, outbool);
+  EXPECT_TRUE(pickle.ReadBool(&iter, &outbool));
+  EXPECT_EQ(testbool2, outbool);
+
+  const char* outdata;
+  int outdatalen;
+  EXPECT_TRUE(pickle.ReadData(&iter, &outdata, &outdatalen));
+  EXPECT_EQ(testdatalen, outdatalen);
+  EXPECT_EQ(memcmp(testdata, outdata, outdatalen), 0);
+
+  EXPECT_TRUE(pickle.ReadData(&iter, &outdata, &outdatalen));
+  EXPECT_EQ(testdatalen, outdatalen);
+  EXPECT_EQ(memcmp(testdata, outdata, outdatalen), 0);
+
+  // reads past the end should fail
+  EXPECT_FALSE(pickle.ReadInt(&iter, &outint));
+}
+
+}  // namespace
+
+TEST(PickleTest, EncodeDecode) {
+  Pickle pickle;
+
+  EXPECT_TRUE(pickle.WriteInt(testint));
+  EXPECT_TRUE(pickle.WriteString(teststr));
+  EXPECT_TRUE(pickle.WriteWString(testwstr));
+  EXPECT_TRUE(pickle.WriteBool(testbool1));
+  EXPECT_TRUE(pickle.WriteBool(testbool2));
+  EXPECT_TRUE(pickle.WriteData(testdata, testdatalen));
+
+  char* dest = pickle.BeginWriteData(testdatalen);
+  EXPECT_TRUE(dest);
+  memcpy(dest, testdata, testdatalen);
+
+  VerifyResult(pickle);
+
+  // test copy constructor
+  Pickle pickle2(pickle);
+  VerifyResult(pickle2);
+
+  // test operator=
+  Pickle pickle3;
+  pickle3 = pickle;
+  VerifyResult(pickle3);
+}
+
+TEST(PickleTest, ZeroLenStr) {
+  Pickle pickle;
+  EXPECT_TRUE(pickle.WriteString(""));
+
+  void* iter = NULL;
+  std::string outstr;
+  EXPECT_TRUE(pickle.ReadString(&iter, &outstr));
+  EXPECT_EQ("", outstr);
+}
+
+TEST(PickleTest, ZeroLenWStr) {
+  Pickle pickle;
+  EXPECT_TRUE(pickle.WriteWString(L""));
+
+  void* iter = NULL;
+  std::string outstr;
+  EXPECT_TRUE(pickle.ReadString(&iter, &outstr));
+  EXPECT_EQ("", outstr);
+}
+
+TEST(PickleTest, BadLenStr) {
+  Pickle pickle;
+  EXPECT_TRUE(pickle.WriteInt(-2));
+
+  void* iter = NULL;
+  std::string outstr;
+  EXPECT_FALSE(pickle.ReadString(&iter, &outstr));
+}
+
+TEST(PickleTest, BadLenWStr) {
+  Pickle pickle;
+  EXPECT_TRUE(pickle.WriteInt(-1));
+
+  void* iter = NULL;
+  std::wstring woutstr;
+  EXPECT_FALSE(pickle.ReadWString(&iter, &woutstr));
+}
+
+TEST(PickleTest, FindNext) {
+  Pickle pickle;
+  EXPECT_TRUE(pickle.WriteInt(1));
+  EXPECT_TRUE(pickle.WriteString("Domo"));
+
+  const char* start = reinterpret_cast<const char*>(pickle.data());
+  const char* end = start + pickle.size();
+
+  EXPECT_TRUE(end == Pickle::FindNext(pickle.header_size_, start, end));
+  EXPECT_TRUE(NULL == Pickle::FindNext(pickle.header_size_, start, end - 1));
+  EXPECT_TRUE(end == Pickle::FindNext(pickle.header_size_, start, end + 1));
+}
+
+TEST(PickleTest, IteratorHasRoom) {
+  Pickle pickle;
+  EXPECT_TRUE(pickle.WriteInt(1));
+  EXPECT_TRUE(pickle.WriteInt(2));
+
+  const void* iter = 0;
+  EXPECT_FALSE(pickle.IteratorHasRoomFor(iter, 1));
+  iter = pickle.payload();
+  EXPECT_TRUE(pickle.IteratorHasRoomFor(iter, 0));
+  EXPECT_TRUE(pickle.IteratorHasRoomFor(iter, 1));
+  EXPECT_FALSE(pickle.IteratorHasRoomFor(iter, -1));
+  EXPECT_TRUE(pickle.IteratorHasRoomFor(iter, sizeof(int) * 2));
+  EXPECT_FALSE(pickle.IteratorHasRoomFor(iter, (sizeof(int) * 2) + 1));
+}
+
+TEST(PickleTest, Resize) {
+  int unit = Pickle::kPayloadUnit;
+  scoped_array<char> data(new char[unit]);
+  char* data_ptr = data.get();
+  for (int i = 0; i < unit; i++)
+    data_ptr[i] = 'G';
+
+  // construct a message that will be exactly the size of one payload unit,
+  // note that any data will have a 4-byte header indicating the size
+  const int payload_size_after_header = unit - sizeof(uint32);
+  Pickle pickle;
+  pickle.WriteData(data_ptr, payload_size_after_header - sizeof(uint32));
+  int cur_payload = payload_size_after_header;
+
+  EXPECT_EQ(pickle.capacity(), unit);
+  EXPECT_EQ(pickle.payload_size(), payload_size_after_header);
+
+  // fill out a full page (noting data header)
+  pickle.WriteData(data_ptr, unit - sizeof(uint32));
+  cur_payload += unit;
+  EXPECT_EQ(unit*2, pickle.capacity());
+  EXPECT_EQ(cur_payload, pickle.payload_size());
+
+  // one more byte should expand the capacity by one unit
+  pickle.WriteData(data_ptr, 1);
+  cur_payload += 5;
+  EXPECT_EQ(unit * 3, pickle.capacity());
+  EXPECT_EQ(cur_payload, pickle.payload_size());
+}
+
+TEST(PickleTest, HeaderPadding) {
+  struct CustomHeader : Pickle::Header {
+    int blah;
+  };
+
+  const uint32 kMagic = 0x12345678;
+
+  Pickle pickle(sizeof(CustomHeader));
+  pickle.WriteInt(kMagic);
+
+  // this should not overwrite the 'int' payload
+  pickle.headerT<CustomHeader>()->blah = 10;
+
+  void* iter = NULL;
+  int result;
+  ASSERT_TRUE(pickle.ReadInt(&iter, &result));
+
+  EXPECT_EQ(result, kMagic);
+}
+
+TEST(PickleTest, EqualsOperator) {
+  Pickle source;
+  source.WriteInt(1);
+
+  Pickle copy_refs_source_buffer(static_cast<const char*>(source.data()),
+                                 source.size());
+  Pickle copy;
+  copy = copy_refs_source_buffer;
+  ASSERT_EQ(source.size(), copy.size());
+}
\ No newline at end of file
diff --git a/base/platform_thread.cc b/base/platform_thread.cc
new file mode 100644
index 0000000..1966ab2
--- /dev/null
+++ b/base/platform_thread.cc
@@ -0,0 +1,51 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/platform_thread.h"
+
+// static
+PlatformThread PlatformThread::Current() {
+  PlatformThread thread;
+
+#ifdef WIN32
+  thread.thread_ = GetCurrentThread();
+#else
+  thread.thread_ = pthread_self();
+#endif
+
+  return thread;
+}
+
+bool PlatformThread::operator==(const PlatformThread& other_thread) {
+#ifdef WIN32
+  return thread_ == other_thread.thread_;
+#else
+  return pthread_equal(thread_, other_thread.thread_);
+#endif
+}
diff --git a/base/platform_thread.h b/base/platform_thread.h
new file mode 100644
index 0000000..b6fb45d
--- /dev/null
+++ b/base/platform_thread.h
@@ -0,0 +1,53 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_PLATFORM_THREAD_H__
+#define BASE_PLATFORM_THREAD_H__
+
+#ifdef WIN32
+#include <windows.h>
+typedef HANDLE PlatformThreadHandle;
+#else
+#include <pthread.h>
+typedef pthread_t PlatformThreadHandle;
+#endif
+
+class PlatformThread {
+ public:
+  // Gets the current thread.
+  static PlatformThread Current();
+
+  bool operator==(const PlatformThread& other_thread);
+
+ private:
+
+  PlatformThreadHandle thread_;
+};
+
+#endif  // BASE_PLATFORM_THREAD_H__
diff --git a/base/port.h b/base/port.h
new file mode 100644
index 0000000..db688dc
--- /dev/null
+++ b/base/port.h
@@ -0,0 +1,57 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_PORT_H__
+#define BASE_PORT_H__
+
+#ifdef MSC_VER
+#define GG_LONGLONG(x) x##I64
+#define GG_ULONGLONG(x) x##UI64
+#else
+#define GG_LONGLONG(x) x##LL
+#define GG_ULONGLONG(x) x##ULL
+#endif
+
+// Per C99 7.8.14, define __STDC_CONSTANT_MACROS before including <stdint.h>
+// to get the INTn_C and UINTn_C macros for integer constants.  It's difficult
+// to guarantee any specific ordering of header includes, so it's difficult to
+// guarantee that the INTn_C macros can be defined by including <stdint.h> at
+// any specific point.  Provide GG_INTn_C macros instead.
+
+#define GG_INT8_C(x)    (x)
+#define GG_INT16_C(x)   (x)
+#define GG_INT32_C(x)   (x)
+#define GG_INT64_C(x)   GG_LONGLONG(x)
+
+#define GG_UINT8_C(x)   (x ## U)
+#define GG_UINT16_C(x)  (x ## U)
+#define GG_UINT32_C(x)  (x ## U)
+#define GG_UINT64_C(x)  GG_ULONGLONG(x)
+
+#endif // BASE_PORT_H__
diff --git a/base/pr_time_test.cc b/base/pr_time_test.cc
new file mode 100644
index 0000000..e054164
--- /dev/null
+++ b/base/pr_time_test.cc
@@ -0,0 +1,179 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/logging.h"
+#include "base/third_party/nspr/prtime.h"
+#include "base/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include <time.h>
+
+namespace {
+
+const int64 kMicrosecondsPerSecond = 1000000i64;
+
+// time_t representation of 15th Oct 2007 12:45:00 PDT
+PRTime comparison_time_pdt = 1192477500 * kMicrosecondsPerSecond;
+
+// Specialized test fixture allowing time strings without timezones to be
+// tested by comparing them to a known time in the local zone.
+class PRTimeTest : public testing::Test {
+ protected:
+  virtual void SetUp() {
+    // Use mktime to get a time_t, and turn it into a PRTime by converting
+    // seconds to microseconds.  Use 15th Oct 2007 12:45:00 local.  This
+    // must be a time guaranteed to be outside of a DST fallback hour in
+    // any timezone.
+    struct tm local_comparison_tm = {
+      0,            // second
+      45,           // minute
+      12,           // hour
+      15,           // day of month
+      10 - 1,       // month
+      2007 - 1900,  // year
+      0,            // day of week (ignored, output only)
+      0,            // day of year (ignored, output only)
+      -1            // DST in effect, -1 tells mktime to figure it out
+    };
+    comparison_time_local_ = mktime(&local_comparison_tm) *
+                             kMicrosecondsPerSecond;
+    ASSERT_GT(comparison_time_local_, 0);
+  }
+
+  PRTime comparison_time_local_;
+};
+
+}  // namespace
+
+// Tests the PR_ParseTimeString nspr helper function for
+// a variety of time strings.
+TEST_F(PRTimeTest, ParseTimeTest1) {
+  //time_t current_time = 0;
+  PRTime current_time = 0;
+  time(&current_time);
+
+  tm local_time = {0};
+  localtime_s(&local_time,&current_time);
+
+  char time_buf[MAX_PATH] = {0};
+  asctime_s(time_buf, arraysize(time_buf), &local_time);
+  current_time *= PR_USEC_PER_SEC;
+
+  PRTime parsed_time = 0;
+  PRStatus result = PR_ParseTimeString(time_buf, PR_FALSE, &parsed_time);
+  EXPECT_EQ(PR_SUCCESS, result);
+  EXPECT_EQ(current_time,parsed_time);
+}
+
+TEST_F(PRTimeTest, ParseTimeTest2) {
+  PRTime parsed_time = 0;
+  PRStatus result = PR_ParseTimeString("Mon, 15 Oct 2007 19:45:00 GMT",
+                                       PR_FALSE, &parsed_time);
+  EXPECT_EQ(PR_SUCCESS, result);
+  EXPECT_EQ(parsed_time, comparison_time_pdt);
+}
+
+TEST_F(PRTimeTest, ParseTimeTest3) {
+  PRTime parsed_time = 0;
+  PRStatus result = PR_ParseTimeString("15 Oct 07 12:45:00", PR_FALSE,
+                                       &parsed_time);
+  EXPECT_EQ(PR_SUCCESS, result);
+  EXPECT_EQ(parsed_time, comparison_time_local_);
+}
+
+TEST_F(PRTimeTest, ParseTimeTest4) {
+  PRTime parsed_time = 0;
+  PRStatus result = PR_ParseTimeString("15 Oct 07 19:45 GMT", PR_FALSE,
+                                       &parsed_time);
+  EXPECT_EQ(PR_SUCCESS, result);
+  EXPECT_EQ(parsed_time, comparison_time_pdt);
+}
+
+TEST_F(PRTimeTest, ParseTimeTest5) {
+  PRTime parsed_time = 0;
+  PRStatus result = PR_ParseTimeString("Mon Oct 15 12:45 PDT 2007",
+                                       PR_FALSE, &parsed_time);
+  EXPECT_EQ(PR_SUCCESS, result);
+  EXPECT_EQ(parsed_time, comparison_time_pdt);
+}
+
+TEST_F(PRTimeTest, ParseTimeTest6) {
+  PRTime parsed_time = 0;
+  PRStatus result = PR_ParseTimeString("Monday, Oct 15, 2007 12:45 PM",
+                                       PR_FALSE, &parsed_time);
+  EXPECT_EQ(PR_SUCCESS, result);
+  EXPECT_EQ(parsed_time, comparison_time_local_);
+}
+
+TEST_F(PRTimeTest, ParseTimeTest7) {
+  PRTime parsed_time = 0;
+  PRStatus result = PR_ParseTimeString("10/15/07 12:45:00 PM", PR_FALSE,
+                                       &parsed_time);
+  EXPECT_EQ(PR_SUCCESS, result);
+  EXPECT_EQ(parsed_time, comparison_time_local_);
+}
+
+TEST_F(PRTimeTest, ParseTimeTest8) {
+  PRTime parsed_time = 0;
+  PRStatus result = PR_ParseTimeString("15-OCT-2007 12:45pm", PR_FALSE,
+                                       &parsed_time);
+  EXPECT_EQ(PR_SUCCESS, result);
+  EXPECT_EQ(parsed_time, comparison_time_local_);
+}
+
+TEST_F(PRTimeTest, ParseTimeTest9) {
+  PRTime parsed_time = 0;
+  PRStatus result = PR_ParseTimeString("16 Oct 2007 4:45-JST (Tuesday)",
+                                       PR_FALSE, &parsed_time);
+  EXPECT_EQ(PR_SUCCESS, result);
+  EXPECT_EQ(parsed_time, comparison_time_pdt);
+}
+
+// This tests the Time::FromString wrapper over PR_ParseTimeString
+TEST_F(PRTimeTest, ParseTimeTest10) {
+  Time parsed_time;
+  bool result = Time::FromString(L"15/10/07 12:45",&parsed_time);
+  EXPECT_EQ(true, result);
+
+  time_t computed_time = parsed_time.ToTimeT();
+  time_t time_to_compare = comparison_time_local_ / kMicrosecondsPerSecond;
+  EXPECT_EQ(computed_time, time_to_compare);
+}
+
+// This tests the Time::FromString wrapper over PR_ParseTimeString
+TEST_F(PRTimeTest, ParseTimeTest11) {
+  Time parsed_time;
+  bool result = Time::FromString(L"Mon, 15 Oct 2007 19:45:00 GMT",
+                                 &parsed_time);
+  EXPECT_EQ(true, result);
+
+  time_t computed_time = parsed_time.ToTimeT();
+  time_t time_to_compare = comparison_time_pdt / kMicrosecondsPerSecond;
+  EXPECT_EQ(computed_time, time_to_compare);
+}
diff --git a/base/process.cc b/base/process.cc
new file mode 100644
index 0000000..462c84b
--- /dev/null
+++ b/base/process.cc
@@ -0,0 +1,134 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/process.h"
+#include "base/logging.h"
+#include "base/process_util.h"
+#include "base/scoped_ptr.h"
+
+bool Process::IsProcessBackgrounded() {
+  DCHECK(process_);
+  DWORD priority = GetPriorityClass(process_);
+  if (priority == 0)
+    return false;  // Failure case.
+  return priority == BELOW_NORMAL_PRIORITY_CLASS;
+}
+
+bool Process::SetProcessBackgrounded(bool value) {
+  DCHECK(process_);
+  DWORD priority = value ? BELOW_NORMAL_PRIORITY_CLASS : NORMAL_PRIORITY_CLASS;
+  return (SetPriorityClass(process_, priority) != 0);
+}
+
+// According to MSDN, these are the default values which XP
+// uses to govern working set soft limits.
+// http://msdn.microsoft.com/en-us/library/ms686234.aspx
+static const int kWinDefaultMinSet = 50 * 4096;
+static const int kWinDefaultMaxSet = 345 * 4096;
+static const int kDampingFactor = 2;
+
+bool Process::ReduceWorkingSet() {
+  if (!process_)
+    return false;
+  // The idea here is that when we the process' working set has gone
+  // down, we want to release those pages to the OS quickly.  However,
+  // when it is not going down, we want to be careful not to release
+  // too much back to the OS, as it could cause additional paging.
+
+  // We use a damping function to lessen the working set over time.
+  // As the process grows/shrinks, this algorithm will lag with
+  // working set reduction.
+  //
+  // The intended algorithm is:
+  //   TargetWorkingSetSize = (LastWorkingSet/2 + CurrentWorkingSet) /2
+
+  scoped_ptr<process_util::ProcessMetrics> metrics(
+    process_util::ProcessMetrics::CreateProcessMetrics(process_));
+  process_util::WorkingSetKBytes working_set;
+  if (!metrics->GetWorkingSetKBytes(&working_set))
+    return false;
+
+
+  // We want to compute the amount of working set that the process
+  // needs to keep in memory.  Since other processes contain the
+  // pages which are shared, we don't need to reserve them in our
+  // process, the system already has them tagged.  Keep in mind, we
+  // don't get to control *which* pages get released, but if we
+  // assume reasonable distribution of pages, this should generally
+  // be the right value.
+  size_t current_working_set_size  = working_set.priv +
+                                     working_set.shareable;
+
+  size_t max_size = current_working_set_size;
+  if (last_working_set_size_)
+    max_size = (max_size + last_working_set_size_) / 2;  // Average.
+  max_size *= 1024;  // Convert to KBytes.
+  last_working_set_size_ = current_working_set_size / kDampingFactor;
+
+  BOOL rv = SetProcessWorkingSetSize(process_, kWinDefaultMinSet, max_size);
+  return rv == TRUE;
+}
+
+bool Process::UnReduceWorkingSet() {
+  if (!process_)
+    return false;
+
+  if (!last_working_set_size_)
+    return true;  // There was nothing to undo.
+
+  // We've had a reduced working set.  Make sure we have lots of
+  // headroom now that we're active again.
+  size_t limit = last_working_set_size_ * kDampingFactor * 2 * 1024;
+  BOOL rv = SetProcessWorkingSetSize(process_, kWinDefaultMinSet, limit);
+  return rv == TRUE;
+}
+
+bool Process::EmptyWorkingSet() {
+  if (!process_)
+    return false;
+
+  BOOL rv = SetProcessWorkingSetSize(process_, -1, -1);
+  return rv == TRUE;
+}
+
+int32 Process::pid() const {
+  if (process_ == 0)
+    return 0;
+
+  return process_util::GetProcId(process_);
+}
+
+bool Process::is_current() const {
+  return process_ == GetCurrentProcess();
+}
+
+// static
+Process Process::Current() {
+  return Process(GetCurrentProcess());
+}
diff --git a/base/process.h b/base/process.h
new file mode 100644
index 0000000..b8ea94f
--- /dev/null
+++ b/base/process.h
@@ -0,0 +1,106 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_PROCESS_H__
+#define BASE_PROCESS_H__
+
+#include <windows.h>
+#include "base/basictypes.h"
+
+// ProcessHandle is a platform specific type which represents the underlying OS
+// handle to a process.
+#ifdef WIN32
+typedef HANDLE ProcessHandle;
+#else
+typedef int ProcessHandle;
+#endif
+
+// A Process
+// TODO(mbelshe): Replace existing code which uses the ProcessHandle w/ the
+//                Process object where relevant.
+class Process {
+ public:
+  Process() : process_(0), last_working_set_size_(0) {}
+  explicit Process(ProcessHandle handle) :
+    process_(handle), last_working_set_size_(0) {}
+
+  // A handle to the current process.
+  static Process Current();
+
+  // Get/Set the handle for this process.
+  ProcessHandle handle() { return process_; }
+  void set_handle(ProcessHandle handle) { process_ = handle; }
+
+  // Get the PID for this process.
+  int32 pid() const;
+
+  // Is the this process the current process.
+  bool is_current() const;
+
+  // Close the Process Handle.
+  void Close() {
+    CloseHandle(process_);
+    process_ = 0;
+  }
+
+  // A process is backgrounded when it's priority is lower than normal.
+  // Return true if this process is backgrounded, false otherwise.
+  bool IsProcessBackgrounded();
+
+  // Set a prcess as backgrounded.  If value is true, the priority
+  // of the process will be lowered.  If value is false, the priority
+  // of the process will be made "normal" - equivalent to default
+  // process priority.
+  // Returns true if the priority was changed, false otherwise.
+  bool SetProcessBackgrounded(bool value);
+
+  // Reduces the working set of memory used by the process.
+  // The algorithm used by this function is intentionally vague.  Repeated calls
+  // to this function consider the process' previous required Working Set sizes
+  // to determine a reasonable reduction.  This helps give memory back to the OS
+  // in increments without over releasing memory.
+  // When the WorkingSet is reduced, it is permanent, until the caller calls
+  // UnReduceWorkingSet.
+  // Returns true if successful, false otherwise.
+  bool ReduceWorkingSet();
+
+  // Undoes the effects of prior calls to ReduceWorkingSet().
+  // Returns true if successful, false otherwise.
+  bool UnReduceWorkingSet();
+
+  // Releases as much of the working set back to the OS as possible.
+  // Returns true if successful, false otherwise.
+  bool EmptyWorkingSet();
+
+ private:
+  ProcessHandle process_;
+  size_t last_working_set_size_;
+};
+
+#endif  // BASE_PROCESS_H__
diff --git a/base/process_util.cc b/base/process_util.cc
new file mode 100644
index 0000000..09631ac
--- /dev/null
+++ b/base/process_util.cc
@@ -0,0 +1,581 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/process_util.h"
+
+#include <windows.h>
+#include <winternl.h>
+#include <psapi.h>
+
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+
+namespace {
+
+// System pagesize. This value remains constant on x86/64 architectures.
+const int PAGESIZE_KB = 4;
+
+// HeapSetInformation function pointer.
+typedef BOOL (WINAPI* HeapSetFn)(HANDLE, HEAP_INFORMATION_CLASS, PVOID, SIZE_T);
+
+}  // namespace
+
+namespace process_util {
+
+int GetCurrentProcId() {
+  return ::GetCurrentProcessId();
+}
+
+// Helper for GetProcId()
+bool GetProcIdViaGetProcessId(ProcessHandle process, DWORD* id) {
+  // Dynamically get a pointer to GetProcessId().
+  typedef DWORD (WINAPI *GetProcessIdFunction)(HANDLE);
+  static GetProcessIdFunction GetProcessIdPtr = NULL;
+  static bool initialize_get_process_id = true;
+  if (initialize_get_process_id) {
+    initialize_get_process_id = false;
+    HMODULE kernel32_handle = GetModuleHandle(L"kernel32.dll");
+    if (!kernel32_handle) {
+      NOTREACHED();
+      return false;
+    }
+    GetProcessIdPtr = reinterpret_cast<GetProcessIdFunction>(GetProcAddress(
+        kernel32_handle, "GetProcessId"));
+  }
+  if (!GetProcessIdPtr)
+    return false;
+  // Ask for the process ID.
+  *id = (*GetProcessIdPtr)(process);
+  return true;
+}
+
+// Helper for GetProcId()
+bool GetProcIdViaNtQueryInformationProcess(ProcessHandle process, DWORD* id) {
+  // Dynamically get a pointer to NtQueryInformationProcess().
+  typedef NTSTATUS (WINAPI *NtQueryInformationProcessFunction)(
+      HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
+  static NtQueryInformationProcessFunction NtQueryInformationProcessPtr = NULL;
+  static bool initialize_query_information_process = true;
+  if (initialize_query_information_process) {
+    initialize_query_information_process = false;
+    // According to nsylvain, ntdll.dll is guaranteed to be loaded, even though
+    // the Windows docs seem to imply that you should LoadLibrary() it.
+    HMODULE ntdll_handle = GetModuleHandle(L"ntdll.dll");
+    if (!ntdll_handle) {
+      NOTREACHED();
+      return false;
+    }
+    NtQueryInformationProcessPtr =
+        reinterpret_cast<NtQueryInformationProcessFunction>(GetProcAddress(
+            ntdll_handle, "NtQueryInformationProcess"));
+  }
+  if (!NtQueryInformationProcessPtr)
+    return false;
+  // Ask for the process ID.
+  PROCESS_BASIC_INFORMATION info;
+  ULONG bytes_returned;
+  NTSTATUS status = (*NtQueryInformationProcessPtr)(process,
+                                                    ProcessBasicInformation,
+                                                    &info, sizeof info,
+                                                    &bytes_returned);
+  if (!SUCCEEDED(status) || (bytes_returned != (sizeof info)))
+    return false;
+
+  *id = static_cast<DWORD>(info.UniqueProcessId);
+  return true;
+}
+
+int GetProcId(ProcessHandle process) {
+  // Get a handle to |process| that has PROCESS_QUERY_INFORMATION rights.
+  HANDLE current_process = GetCurrentProcess();
+  HANDLE process_with_query_rights;
+  if (DuplicateHandle(current_process, process, current_process,
+                      &process_with_query_rights, PROCESS_QUERY_INFORMATION,
+                      false, 0)) {
+    // Try to use GetProcessId(), if it exists.  Fall back on
+    // NtQueryInformationProcess() otherwise (< Win XP SP1).
+    DWORD id;
+    bool success =
+        GetProcIdViaGetProcessId(process_with_query_rights, &id) ||
+        GetProcIdViaNtQueryInformationProcess(process_with_query_rights, &id);
+    CloseHandle(process_with_query_rights);
+    if (success)
+      return id;
+  }
+
+  // We're screwed.
+  NOTREACHED();
+  return 0;
+}
+
+bool LaunchApp(const std::wstring& cmdline,
+               bool wait, bool start_hidden, ProcessHandle* process_handle) {
+  STARTUPINFO startup_info = {0};
+  startup_info.cb = sizeof(startup_info);
+  if (start_hidden) {
+    startup_info.dwFlags = STARTF_USESHOWWINDOW;
+    startup_info.wShowWindow = SW_HIDE;
+  }
+  PROCESS_INFORMATION process_info;
+  if (!CreateProcess(NULL,
+                     const_cast<wchar_t*>(cmdline.c_str()), NULL, NULL,
+                     FALSE, 0, NULL, NULL,
+                     &startup_info, &process_info))
+    return false;
+
+  // Handles must be closed or they will leak
+  CloseHandle(process_info.hThread);
+
+  if (wait)
+    WaitForSingleObject(process_info.hProcess, INFINITE);
+
+  // If the caller wants the process handle, we won't close it.
+  if (process_handle) {
+    *process_handle = process_info.hProcess;
+  } else {
+    CloseHandle(process_info.hProcess);
+  }
+  return true;
+}
+
+// Attempts to kill the process identified by the given process
+// entry structure, giving it the specified exit code.
+// Returns true if this is successful, false otherwise.
+bool KillProcess(int process_id, int exit_code, bool wait) {
+  bool result = false;
+  HANDLE process = OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE,
+                               FALSE,  // Don't inherit handle
+                               process_id);
+  if (process) {
+    result = !!TerminateProcess(process, exit_code);
+    if (result && wait) {
+      // The process may not end immediately due to pending I/O
+      if (WAIT_OBJECT_0 != WaitForSingleObject(process, 60 * 1000))
+        DLOG(ERROR) << "Error waiting for process exit: " << GetLastError();
+    } else {
+      DLOG(ERROR) << "Unable to terminate process: " << GetLastError();
+    }
+    CloseHandle(process);
+  }
+  return result;
+}
+
+bool DidProcessCrash(ProcessHandle handle) {
+  DWORD exitcode = 0;
+  BOOL success = ::GetExitCodeProcess(handle, &exitcode);
+  DCHECK(success);
+  DCHECK(exitcode != STILL_ACTIVE);
+
+  if (exitcode == 0 ||           // Normal termination.
+      exitcode == 1 ||           // Killed by task manager.
+      exitcode == 0xC0000354 ||  // STATUS_DEBUGGER_INACTIVE
+      exitcode == 0xC000013A ||  // Control-C/end session.
+      exitcode == 0x40010004) {  // Debugger terminated process/end session.
+    return false;
+  }
+
+  // All other exit codes indicate crashes.
+  return true;
+}
+
+NamedProcessIterator::NamedProcessIterator(const std::wstring& executable_name,
+                                           const ProcessFilter* filter) :
+  started_iteration_(false),
+  executable_name_(executable_name),
+  filter_(filter) {
+    snapshot_ = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+  }
+
+NamedProcessIterator::~NamedProcessIterator() {
+  CloseHandle(snapshot_);
+}
+
+
+const ProcessEntry* NamedProcessIterator::NextProcessEntry() {
+  bool result = false;
+  do {
+    result = CheckForNextProcess();
+  } while (result && !IncludeEntry());
+
+  if (result) {
+    return &entry_;
+  }
+
+  return NULL;
+}
+
+bool NamedProcessIterator::CheckForNextProcess() {
+  InitProcessEntry(&entry_);
+
+  if (!started_iteration_) {
+    started_iteration_ = true;
+    return !!Process32First(snapshot_, &entry_);
+  }
+
+  return !!Process32Next(snapshot_, &entry_);
+}
+
+bool NamedProcessIterator::IncludeEntry() {
+  return _wcsicmp(executable_name_.c_str(), entry_.szExeFile) == 0 &&
+                  (!filter_ || filter_->Includes(entry_.th32ProcessID,
+                                                 entry_.th32ParentProcessID));
+}
+
+void NamedProcessIterator::InitProcessEntry(ProcessEntry* entry) {
+  memset(entry, 0, sizeof(*entry));
+  entry->dwSize = sizeof(*entry);
+}
+
+int GetProcessCount(const std::wstring& executable_name,
+                    const ProcessFilter* filter) {
+  int count = 0;
+
+  NamedProcessIterator iter(executable_name, filter);
+  while (iter.NextProcessEntry())
+    ++count;
+  return count;
+}
+
+bool KillProcesses(const std::wstring& executable_name, int exit_code,
+                   const ProcessFilter* filter) {
+  bool result = true;
+  const ProcessEntry* entry;
+
+  NamedProcessIterator iter(executable_name, filter);
+  while (entry = iter.NextProcessEntry())
+    result = KillProcess((*entry).th32ProcessID, exit_code, true) && result;
+
+  return result;
+}
+
+bool WaitForProcessesToExit(const std::wstring& executable_name,
+                            int wait_milliseconds,
+                            const ProcessFilter* filter) {
+  const ProcessEntry* entry;
+  bool result = true;
+  DWORD start_time = GetTickCount();
+
+  NamedProcessIterator iter(executable_name, filter);
+  while (entry = iter.NextProcessEntry()) {
+    DWORD remaining_wait =
+      std::max(0, wait_milliseconds -
+          static_cast<int>(GetTickCount() - start_time));
+    HANDLE process = OpenProcess(SYNCHRONIZE,
+                                 FALSE,
+                                 entry->th32ProcessID);
+    DWORD wait_result = WaitForSingleObject(process, remaining_wait);
+    CloseHandle(process);
+    result = result && (wait_result == WAIT_OBJECT_0);
+  }
+
+  return result;
+}
+
+bool CleanupProcesses(const std::wstring& executable_name,
+                      int wait_milliseconds,
+                      int exit_code,
+                      const ProcessFilter* filter) {
+  bool exited_cleanly =
+    process_util::WaitForProcessesToExit(executable_name, wait_milliseconds,
+                                         filter);
+  if (!exited_cleanly)
+    process_util::KillProcesses(executable_name, exit_code, filter);
+  return exited_cleanly;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// ProcesMetrics
+
+ProcessMetrics::ProcessMetrics(ProcessHandle process) : process_(process),
+                                                        last_time_(0),
+                                                        last_system_time_(0) {
+  SYSTEM_INFO system_info;
+  GetSystemInfo(&system_info);
+  processor_count_ = system_info.dwNumberOfProcessors;
+}
+
+// static
+ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) {
+  return new ProcessMetrics(process);
+}
+
+ProcessMetrics::~ProcessMetrics() { }
+
+size_t ProcessMetrics::GetPagefileUsage() {
+  PROCESS_MEMORY_COUNTERS pmc;
+  if (GetProcessMemoryInfo(process_, &pmc, sizeof(pmc))) {
+    return pmc.PagefileUsage;
+  }
+  return 0;
+}
+
+// Returns the peak space allocated for the pagefile, in bytes.
+size_t ProcessMetrics::GetPeakPagefileUsage() {
+  PROCESS_MEMORY_COUNTERS pmc;
+  if (GetProcessMemoryInfo(process_, &pmc, sizeof(pmc))) {
+    return pmc.PeakPagefileUsage;
+  }
+  return 0;
+}
+
+// Returns the current working set size, in bytes.
+size_t ProcessMetrics::GetWorkingSetSize() {
+  PROCESS_MEMORY_COUNTERS pmc;
+  if (GetProcessMemoryInfo(process_, &pmc, sizeof(pmc))) {
+    return pmc.WorkingSetSize;
+  }
+  return 0;
+}
+
+size_t ProcessMetrics::GetPrivateBytes() {
+  // PROCESS_MEMORY_COUNTERS_EX is not supported until XP SP2.
+  // GetProcessMemoryInfo() will simply fail on prior OS. So the requested
+  // information is simply not available. Hence, we will return 0 on unsupported
+  // OSes. Unlike most Win32 API, we don't need to initialize the "cb" member.
+  PROCESS_MEMORY_COUNTERS_EX pmcx;
+  if (GetProcessMemoryInfo(process_,
+                          reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&pmcx),
+                          sizeof(pmcx))) {
+      return pmcx.PrivateUsage;
+  }
+  return 0;
+}
+
+void ProcessMetrics::GetCommittedKBytes(CommittedKBytes* usage) {
+  MEMORY_BASIC_INFORMATION mbi = {0};
+  size_t committed_private = 0;
+  size_t committed_mapped = 0;
+  size_t committed_image = 0;
+  void* base_address = NULL;
+  while (VirtualQueryEx(process_, base_address, &mbi,
+         sizeof(MEMORY_BASIC_INFORMATION)) ==
+         sizeof(MEMORY_BASIC_INFORMATION)) {
+      if(mbi.State == MEM_COMMIT) {
+        if (mbi.Type == MEM_PRIVATE) {
+          committed_private += mbi.RegionSize;
+        } else if (mbi.Type == MEM_MAPPED) {
+          committed_mapped += mbi.RegionSize;
+        } else if (mbi.Type == MEM_IMAGE) {
+          committed_image += mbi.RegionSize;
+        } else {
+          NOTREACHED();
+        }
+      }
+      base_address = (static_cast<BYTE*>(mbi.BaseAddress)) + mbi.RegionSize;
+  }
+  usage->image = committed_image / 1024;
+  usage->mapped = committed_mapped / 1024;
+  usage->priv = committed_private / 1024;
+}
+
+bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) {
+  size_t ws_private = 0;
+  size_t ws_shareable = 0;
+  size_t ws_shared = 0;
+
+  DCHECK(ws_usage);
+  memset(ws_usage, 0, sizeof(*ws_usage));
+
+  DWORD number_of_entries = 4096;  // Just a guess.
+  PSAPI_WORKING_SET_INFORMATION* buffer = NULL;
+  int retries = 5;
+  for(;;) {
+    DWORD buffer_size = sizeof(PSAPI_WORKING_SET_INFORMATION) +
+                        (number_of_entries * sizeof(PSAPI_WORKING_SET_BLOCK));
+
+    // if we can't expand the buffer, don't leak the previous
+    // contents or pass a NULL pointer to QueryWorkingSet
+    PSAPI_WORKING_SET_INFORMATION* new_buffer = reinterpret_cast<PSAPI_WORKING_SET_INFORMATION*>(
+        realloc(buffer, buffer_size));
+    if (!new_buffer) {
+      free(buffer);
+      return false;
+    }
+    buffer = new_buffer;
+
+    // Call the function once to get number of items
+    if (QueryWorkingSet(process_, buffer, buffer_size))
+      break;  // Success
+
+    if (GetLastError() != ERROR_BAD_LENGTH) {
+      free(buffer);
+      return false;
+    }
+
+    number_of_entries = static_cast<DWORD>(buffer->NumberOfEntries);
+
+    // Maybe some entries are being added right now. Increase the buffer to
+    // take that into account.
+    number_of_entries = static_cast<DWORD>(number_of_entries * 1.25);
+
+    if (--retries == 0) {
+      free(buffer);  // If we're looping, eventually fail.
+      return false;
+    }
+  }
+
+  // On windows 2000 the function returns 1 even when the buffer is too small.
+  // The number of entries that we are going to parse is the minimum between the
+  // size we allocated and the real number of entries.
+  number_of_entries =
+      std::min(number_of_entries, static_cast<DWORD>(buffer->NumberOfEntries));
+  for (unsigned int i = 0; i < number_of_entries; i++) {
+    if (buffer->WorkingSetInfo[i].Shared) {
+      ws_shareable++;
+      if (buffer->WorkingSetInfo[i].ShareCount > 1)
+        ws_shared++;
+    } else {
+      ws_private++;
+    }
+  }
+
+  ws_usage->priv = ws_private * PAGESIZE_KB;
+  ws_usage->shareable = ws_shareable * PAGESIZE_KB;
+  ws_usage->shared = ws_shared * PAGESIZE_KB;
+  free(buffer);
+  return true;
+}
+
+static uint64 FileTimeToUTC(const FILETIME& ftime) {
+  LARGE_INTEGER li;
+  li.LowPart = ftime.dwLowDateTime;
+  li.HighPart = ftime.dwHighDateTime;
+  return li.QuadPart;
+}
+
+int ProcessMetrics::GetCPUUsage() {
+  FILETIME now;
+  FILETIME creation_time;
+  FILETIME exit_time;
+  FILETIME kernel_time;
+  FILETIME user_time;
+
+  GetSystemTimeAsFileTime(&now);
+
+  if (!GetProcessTimes(process_, &creation_time, &exit_time,
+                       &kernel_time, &user_time)) {
+    // We don't assert here because in some cases (such as in the Task Manager)
+    // we may call this function on a process that has just exited but we have
+    // not yet received the notification.
+    return 0;
+  }
+  int64 system_time = (FileTimeToUTC(kernel_time) + FileTimeToUTC(user_time)) /
+                        processor_count_;
+  int64 time = FileTimeToUTC(now);
+
+  if ((last_system_time_ == 0) || (last_time_ == 0)) {
+    // First call, just set the last values.
+    last_system_time_ = system_time;
+    last_time_ = time;
+    return 0;
+  }
+
+  int64 system_time_delta = system_time - last_system_time_;
+  int64 time_delta = time - last_time_;
+  DCHECK(time_delta != 0);
+  if (time_delta == 0)
+    return 0;
+
+  // We add time_delta / 2 so the result is rounded.
+  int cpu = static_cast<int>((system_time_delta * 100 + time_delta / 2) /
+                             time_delta);
+
+  last_system_time_ = system_time;
+  last_time_ = time;
+
+  return cpu;
+}
+
+bool ProcessMetrics::GetIOCounters(IO_COUNTERS* io_counters) {
+  return GetProcessIoCounters(process_, io_counters) != FALSE;
+}
+
+bool ProcessMetrics::CalculateFreeMemory(FreeMBytes* free) {
+  const SIZE_T kTopAdress = 0x7F000000;
+  const SIZE_T kMegabyte = 1024 * 1024;
+  SIZE_T accumulated = 0;
+
+  MEMORY_BASIC_INFORMATION largest = {0};
+  UINT_PTR scan = 0;
+  while (scan < kTopAdress) {
+    MEMORY_BASIC_INFORMATION info;
+    if (!::VirtualQueryEx(process_, reinterpret_cast<void*>(scan),
+                          &info, sizeof(info)))
+      return false;
+    if (info.State == MEM_FREE) {
+      accumulated += info.RegionSize;
+      UINT_PTR end = scan + info.RegionSize;
+      if (info.RegionSize > (largest.RegionSize))
+        largest = info;
+    }
+    scan += info.RegionSize;
+  }
+  free->largest = largest.RegionSize / kMegabyte;
+  free->largest_ptr = largest.BaseAddress;
+  free->total = accumulated / kMegabyte;
+  return true;
+}
+
+bool EnableLowFragmentationHeap() {
+  HMODULE kernel32 = GetModuleHandle(L"kernel32.dll");
+  HeapSetFn heap_set = reinterpret_cast<HeapSetFn>(GetProcAddress(
+      kernel32,
+      "HeapSetInformation"));
+
+  // On Windows 2000, the function is not exported. This is not a reason to
+  // fail.
+  if (!heap_set)
+    return true;
+
+  unsigned number_heaps = GetProcessHeaps(0, NULL);
+  if (!number_heaps)
+    return false;
+
+  // Gives us some extra space in the array in case a thread is creating heaps
+  // at the same time we're querying them.
+  static const int MARGIN = 8;
+  scoped_array<HANDLE> heaps(new HANDLE[number_heaps + MARGIN]);
+  number_heaps = GetProcessHeaps(number_heaps + MARGIN, heaps.get());
+  if (!number_heaps)
+    return false;
+
+  for (unsigned i = 0; i < number_heaps; ++i) {
+    ULONG lfh_flag = 2;
+    // Don't bother with the result code. It may fails on heaps that have the
+    // HEAP_NO_SERIALIZE flag. This is expected and not a problem at all.
+    heap_set(heaps[i],
+             HeapCompatibilityInformation,
+             &lfh_flag,
+             sizeof(lfh_flag));
+  }
+  return true;
+}
+
+}  // namespace process_util
diff --git a/base/process_util.h b/base/process_util.h
new file mode 100644
index 0000000..1bdd9e9
--- /dev/null
+++ b/base/process_util.h
@@ -0,0 +1,285 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file/namespace contains utility functions for enumerating, ending and
+// computing statistics of processes.
+
+#ifndef BASE_PROCESS_UTIL_H__
+#define BASE_PROCESS_UTIL_H__
+
+#include <string>
+#ifdef WIN32
+#include <windows.h>
+#include <tlhelp32.h>
+#endif // WIN32
+
+#include "base/basictypes.h"
+#include "base/process.h"
+
+// ProcessHandle is a platform specific type which represents the underlying OS
+// handle to a process.
+#ifdef WIN32
+typedef PROCESSENTRY32 ProcessEntry;
+typedef IO_COUNTERS IoCounters;
+#else
+typedef int ProcessEntry;
+typedef int IoCounters; //TODO(awalker): replace with struct when available
+#endif
+
+namespace process_util {
+
+// Returns the id of the current process.
+int GetCurrentProcId();
+
+// Returns the unique ID for the specified process.  This is functionally the
+// same as Windows' GetProcessId(), but works on versions of Windows before
+// Win XP SP1 as well.
+int GetProcId(ProcessHandle process);
+
+// Runs the given application name with the given command line. Normally, the
+// first command line argument should be the path to the process, and don't
+// forget to quote it.
+//
+// If wait is true, it will block and wait for the other process to finish,
+// otherwise, it will just continue asynchronously.
+//
+// Example (including literal quotes)
+//  cmdline = "c:\windows\explorer.exe" -foo "c:\bar\"
+//
+// If process_handle is non-NULL, the process handle of the launched app will be
+// stored there on a successful launch.
+// NOTE: In this case, the caller is responsible for closing the handle so
+//       that it doesn't leak!
+bool LaunchApp(const std::wstring& cmdline,
+               bool wait, bool start_hidden, ProcessHandle* process_handle);
+
+// Used to filter processes by process ID.
+class ProcessFilter {
+ public:
+  // Returns true to indicate set-inclusion and false otherwise.  This method
+  // should not have side-effects and should be idempotent.
+  virtual bool Includes(uint32 pid, uint32 parent_pid) const = 0;
+};
+
+// Returns the number of processes on the machine that are running from the
+// given executable name.  If filter is non-null, then only processes selected
+// by the filter will be counted.
+int GetProcessCount(const std::wstring& executable_name,
+                    const ProcessFilter* filter);
+
+// Attempts to kill all the processes on the current machine that were launched
+// from the given executable name, ending them with the given exit code.  If
+// filter is non-null, then only processes selected by the filter are killed.
+// Returns false if all processes were able to be killed off, false if at least
+// one couldn't be killed.
+bool KillProcesses(const std::wstring& executable_name, int exit_code,
+                   const ProcessFilter* filter);
+
+// Attempts to kill the process identified by the given process
+// entry structure, giving it the specified exit code. If |wait| is true, wait
+// for the process to be actually terminated before returning.
+// Returns true if this is successful, false otherwise.
+bool KillProcess(int process_id, int exit_code, bool wait);
+
+// Get the termination status (exit code) of the process and return true if the
+// status indicates the process crashed.  It is an error to call this if the
+// process hasn't terminated yet.
+bool DidProcessCrash(ProcessHandle handle);
+
+// Wait for all the processes based on the named executable to exit.  If filter
+// is non-null, then only processes selected by the filter are waited on.
+// Returns after all processes have exited or wait_milliseconds have expired.
+// Returns true if all the processes exited, false otherwise.
+bool WaitForProcessesToExit(const std::wstring& executable_name,
+                            int wait_milliseconds,
+                            const ProcessFilter* filter);
+
+// Waits a certain amount of time (can be 0) for all the processes with a given
+// executable name to exit, then kills off any of them that are still around.
+// If filter is non-null, then only processes selected by the filter are waited
+// on.  Killed processes are ended with the given exit code.  Returns false if
+// any processes needed to be killed, true if they all exited cleanly within
+// the wait_milliseconds delay.
+bool CleanupProcesses(const std::wstring& executable_name,
+                      int wait_milliseconds,
+                      int exit_code,
+                      const ProcessFilter* filter);
+
+// This class provides a way to iterate through the list of processes
+// on the current machine that were started from the given executable
+// name.  To use, create an instance and then call NextProcessEntry()
+// until it returns false.
+class NamedProcessIterator {
+ public:
+  NamedProcessIterator(const std::wstring& executable_name,
+                       const ProcessFilter* filter);
+  ~NamedProcessIterator();
+
+  // If there's another process that matches the given executable name,
+  // returns a const pointer to the corresponding PROCESSENTRY32.
+  // If there are no more matching processes, returns NULL.
+  // The returned pointer will remain valid until NextProcessEntry()
+  // is called again or this NamedProcessIterator goes out of scope.
+  const ProcessEntry* NextProcessEntry();
+
+ private:
+  // Determines whether there's another process (regardless of executable)
+  // left in the list of all processes.  Returns true and sets entry_ to
+  // that process's info if there is one, false otherwise.
+  bool CheckForNextProcess();
+
+  bool IncludeEntry();
+
+  // Initializes a PROCESSENTRY32 data structure so that it's ready for
+  // use with Process32First/Process32Next.
+  void InitProcessEntry(ProcessEntry* entry);
+
+  std::wstring executable_name_;
+#ifdef WIN32
+  HANDLE snapshot_;
+#endif WIN32
+  bool started_iteration_;
+  ProcessEntry entry_;
+  const ProcessFilter* filter_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(NamedProcessIterator);
+};
+
+// Working Set (resident) memory usage broken down by
+// priv (private): These pages (kbytes) cannot be shared with any other process.
+// shareable:      These pages (kbytes) can be shared with other processes under
+//                 the right circumstances.
+// shared :        These pages (kbytes) are currently shared with at least one
+//                 other process.
+struct WorkingSetKBytes {
+  size_t priv;
+  size_t shareable;
+  size_t shared;
+};
+
+// Committed (resident + paged) memory usage broken down by
+// private: These pages cannot be shared with any other process.
+// mapped:  These pages are mapped into the view of a section (backed by
+//          pagefile.sys)
+// image:   These pages are mapped into the view of an image section (backed by
+//          file system)
+struct CommittedKBytes {
+  size_t priv;
+  size_t mapped;
+  size_t image;
+};
+
+// Free memory (Megabytes marked as free) in the 2G process address space.
+// total : total amount in megabytes marked as free. Maximum value is 2048.
+// largest : size of the largest contiguous amount of memory found. It is
+//   always smaller or equal to FreeMBytes::total.
+// largest_ptr: starting address of the largest memory block.
+struct FreeMBytes {
+  size_t total;
+  size_t largest;
+  void* largest_ptr;
+};
+
+// Provides performance metrics for a specified process (CPU usage, memory and
+// IO counters). To use it, invoke CreateProcessMetrics() to get an instance
+// for a specific process, then access the information with the different get
+// methods.
+class ProcessMetrics {
+ public:
+  // Creates a ProcessMetrics for the specified process.
+  // The caller owns the returned object.
+  static ProcessMetrics* CreateProcessMetrics(ProcessHandle process);
+
+  ~ProcessMetrics();
+
+  // Returns the current space allocated for the pagefile, in bytes (these pages
+  // may or may not be in memory).
+  size_t GetPagefileUsage();
+  // Returns the peak space allocated for the pagefile, in bytes.
+  size_t GetPeakPagefileUsage();
+  // Returns the current working set size, in bytes.
+  size_t GetWorkingSetSize();
+  // Returns private usage, in bytes. Private bytes is the amount
+  // of memory currently allocated to a process that cannot be shared.
+  // Note: returns 0 on unsupported OSes: prior to XP SP2.
+  size_t GetPrivateBytes();
+  // Fills a CommittedKBytes with both resident and paged
+  // memory usage as per definition of CommittedBytes.
+  void GetCommittedKBytes(CommittedKBytes* usage);
+  // Fills a WorkingSetKBytes containing resident private and shared memory
+  // usage in bytes, as per definition of WorkingSetBytes.
+  bool GetWorkingSetKBytes(WorkingSetKBytes* ws_usage);
+
+  // Computes the current process available memory for allocation.
+  // It does a linear scan of the address space querying each memory region
+  // for its free (unallocated) status. It is useful for estimating the memory
+  // load and fragmentation.
+  bool CalculateFreeMemory(FreeMBytes* free);
+
+  // Returns the CPU usage in percent since the last time this method was
+  // called. The first time this method is called it returns 0 and will return
+  // the actual CPU info on subsequent calls.
+  // Note that on multi-processor machines, the CPU usage value is for all
+  // CPUs. So if you have 2 CPUs and your process is using all the cycles
+  // of 1 CPU and not the other CPU, this method returns 50.
+  int GetCPUUsage();
+
+  // Retrieves accounting information for all I/O operations performed by the
+  // process.
+  // If IO information is retrieved successfully, the function returns true
+  // and fills in the IO_COUNTERS passed in. The function returns false
+  // otherwise.
+  bool GetIOCounters(IoCounters* io_counters);
+
+ private:
+  explicit ProcessMetrics(ProcessHandle process);
+
+  ProcessHandle process_;
+
+  int processor_count_;
+
+  // Used to store the previous times so we can compute the CPU usage.
+  int64 last_time_;
+  int64 last_system_time_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(ProcessMetrics);
+};
+
+// Enables low fragmentation heap (LFH) for every heaps of this process. This
+// won't have any effect on heaps created after this function call. It will not
+// modify data allocated in the heaps before calling this function. So it is
+// better to call this function early in initialization and again before
+// entering the main loop.
+// Note: Returns true on Windows 2000 without doing anything.
+bool EnableLowFragmentationHeap();
+
+}  // namespace process_util
+
+
+#endif  // BASE_PROCESS_UTIL_H__
diff --git a/base/process_util_unittest.cc b/base/process_util_unittest.cc
new file mode 100644
index 0000000..d8bfb33
--- /dev/null
+++ b/base/process_util_unittest.cc
@@ -0,0 +1,99 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#define _CRT_SECURE_NO_WARNINGS
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "base/process_util.h"
+
+TEST(ProcessUtilTest, EnableLFH) {
+  ASSERT_TRUE(process_util::EnableLowFragmentationHeap());
+  if (IsDebuggerPresent()) {
+    // Under these conditions, LFH can't be enabled. There's no point to test
+    // anything.
+    const char* no_debug_env = getenv("_NO_DEBUG_HEAP");
+    if (!no_debug_env || strcmp(no_debug_env, "1"))
+      return;
+  }
+  HANDLE heaps[1024] = { 0 };
+  unsigned number_heaps = GetProcessHeaps(1024, heaps);
+  EXPECT_GT(number_heaps, 0u);
+  for (unsigned i = 0; i < number_heaps; ++i) {
+    ULONG flag = 0;
+    SIZE_T length;
+    ASSERT_NE(0, HeapQueryInformation(heaps[i],
+                                      HeapCompatibilityInformation,
+                                      &flag,
+                                      sizeof(flag),
+                                      &length));
+    // If flag is 0, the heap is a standard heap that does not support
+    // look-asides. If flag is 1, the heap supports look-asides. If flag is 2,
+    // the heap is a low-fragmentation heap (LFH). Note that look-asides are not
+    // supported on the LFH.
+
+    // We don't have any documented way of querying the HEAP_NO_SERIALIZE flag.
+    EXPECT_LE(flag, 2u);
+    EXPECT_NE(flag, 1u);
+  }
+}
+
+TEST(ProcessUtilTest, CalcFreeMemory) {
+  process_util::ProcessMetrics* metrics =
+    process_util::ProcessMetrics::CreateProcessMetrics(::GetCurrentProcess());
+  ASSERT_TRUE(NULL != metrics);
+
+  // Typical values here is ~1900 for total and ~1000 for largest. Obviously
+  // it depends in what other tests have done to this process.
+  process_util::FreeMBytes free_mem1 = {0};
+  EXPECT_TRUE(metrics->CalculateFreeMemory(&free_mem1));
+  EXPECT_LT(10u, free_mem1.total);
+  EXPECT_LT(10u, free_mem1.largest);
+  EXPECT_GT(2048u, free_mem1.total);
+  EXPECT_GT(2048u, free_mem1.largest);
+  EXPECT_GE(free_mem1.total, free_mem1.largest);
+  EXPECT_TRUE(NULL != free_mem1.largest_ptr);
+
+  // Allocate 20M and check again. It should have gone down.
+  const int kAllocMB = 20;
+  char* alloc = new char[kAllocMB * 1024 * 1024];
+  EXPECT_TRUE(NULL != alloc);
+
+  size_t expected_total = free_mem1.total - kAllocMB;
+  size_t expected_largest = free_mem1.largest;
+
+  process_util::FreeMBytes free_mem2 = {0};
+  EXPECT_TRUE(metrics->CalculateFreeMemory(&free_mem2));
+  EXPECT_GE(free_mem2.total, free_mem2.largest);
+  EXPECT_GE(expected_total, free_mem2.total);
+  EXPECT_GE(expected_largest, free_mem2.largest);
+  EXPECT_TRUE(NULL != free_mem2.largest_ptr);
+
+  delete[] alloc;
+  delete metrics;
+}
diff --git a/base/ref_counted.h b/base/ref_counted.h
new file mode 100644
index 0000000..36f4ab4
--- /dev/null
+++ b/base/ref_counted.h
@@ -0,0 +1,249 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_REF_COUNTED_H__
+#define BASE_REF_COUNTED_H__
+
+#include "base/atomic.h"
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+namespace base {
+
+//
+// A base class for reference counted classes.  Otherwise, known as a cheap
+// knock-off of WebKit's RefCounted<T> class.  To use this guy just extend your
+// class from it like so:
+//
+//   class MyFoo : public base::RefCounted<MyFoo> {
+//    ...
+//   };
+//
+template <class T>
+class RefCounted {
+ public:
+  RefCounted() : ref_count_(0) {
+#ifndef NDEBUG
+    in_dtor_ = false;
+#endif
+  }
+
+  ~RefCounted() {
+#ifndef NDEBUG
+    DCHECK(in_dtor_) << "RefCounted object deleted without calling Release()";
+#endif
+  }
+
+  void AddRef() {
+#ifndef NDEBUG
+    DCHECK(!in_dtor_);
+#endif
+    ++ref_count_;
+  }
+
+  void Release() {
+#ifndef NDEBUG
+    DCHECK(!in_dtor_);
+#endif
+    if (--ref_count_ == 0) {
+#ifndef NDEBUG
+      in_dtor_ = true;
+#endif
+      delete static_cast<T*>(this);
+    }
+  }
+
+ private:
+  int ref_count_;
+#ifndef NDEBUG
+  bool in_dtor_;
+#endif
+
+  DISALLOW_EVIL_CONSTRUCTORS(RefCounted<T>);
+};
+
+//
+// A thread-safe variant of RefCounted<T>
+//
+//   class MyFoo : public base::RefCountedThreadSafe<MyFoo> {
+//    ...
+//   };
+//
+template <class T>
+class RefCountedThreadSafe {
+ public:
+  RefCountedThreadSafe() : ref_count_(0) {
+#ifndef NDEBUG
+    in_dtor_ = false;
+#endif
+  }
+
+  ~RefCountedThreadSafe() {
+#ifndef NDEBUG
+    DCHECK(in_dtor_) << "RefCountedThreadSafe object deleted without " <<
+                        "calling Release()";
+#endif
+  }
+
+  void AddRef() {
+#ifndef NDEBUG
+    DCHECK(!in_dtor_);
+#endif
+    AtomicIncrement(&ref_count_);
+  }
+
+  void Release() {
+#ifndef NDEBUG
+    DCHECK(!in_dtor_);
+#endif
+    // We need to insert memory barriers to ensure that state written before
+    // the reference count became 0 will be visible to a thread that has just
+    // made the count 0.
+    // TODO(wtc): Bug 1112286: use the barrier variant of AtomicDecrement.
+    if (AtomicDecrement(&ref_count_) == 0) {
+#ifndef NDEBUG
+      in_dtor_ = true;
+#endif
+      delete static_cast<T*>(this);
+    }
+  }
+
+ private:
+  int32 ref_count_;
+#ifndef NDEBUG
+  bool in_dtor_;
+#endif
+
+  DISALLOW_EVIL_CONSTRUCTORS(RefCountedThreadSafe<T>);
+};
+
+} // namespace base
+
+//
+// A smart pointer class for reference counted objects.  Use this class instead
+// of calling AddRef and Release manually on a reference counted object to
+// avoid common memory leaks caused by forgetting to Release an object
+// reference.  Sample usage:
+//
+//   class MyFoo : public RefCounted<MyFoo> {
+//    ...
+//   };
+//
+//   void some_function() {
+//     scoped_refptr<MyFoo> foo = new MyFoo();
+//     foo->Method(param);
+//     // |foo| is released when this function returns
+//   }
+//
+//   void some_other_function() {
+//     scoped_refptr<MyFoo> foo = new MyFoo();
+//     ...
+//     foo = NULL;  // explicitly releases |foo|
+//     ...
+//     if (foo)
+//       foo->Method(param);
+//   }
+//
+// The above examples show how scoped_refptr<T> acts like a pointer to T.
+// Given two scoped_refptr<T> classes, it is also possible to exchange
+// references between the two objects, like so:
+//
+//   {
+//     scoped_refptr<MyFoo> a = new MyFoo();
+//     scoped_refptr<MyFoo> b;
+//
+//     b.swap(a);
+//     // now, |b| references the MyFoo object, and |a| references NULL.
+//   }
+//
+// To make both |a| and |b| in the above example reference the same MyFoo
+// object, simply use the assignment operator:
+//
+//   {
+//     scoped_refptr<MyFoo> a = new MyFoo();
+//     scoped_refptr<MyFoo> b;
+//
+//     b = a;
+//     // now, |a| and |b| each own a reference to the same MyFoo object.
+//   }
+//
+template <class T>
+class scoped_refptr {
+ public:
+  scoped_refptr() : ptr_(NULL) {
+  }
+
+  scoped_refptr(T* p) : ptr_(p) {
+    if (ptr_)
+      ptr_->AddRef();
+  }
+
+  scoped_refptr(const scoped_refptr<T>& r) : ptr_(r.ptr_) {
+    if (ptr_)
+      ptr_->AddRef();
+  }
+
+  ~scoped_refptr() {
+    if (ptr_)
+      ptr_->Release();
+  }
+
+  T* get() const { return ptr_; }
+  operator T*() const { return ptr_; }
+  T* operator->() const { return ptr_; }
+
+  scoped_refptr<T>& operator=(T* p) {
+    // AddRef first so that self assignment should work
+    if (p)
+      p->AddRef();
+    if (ptr_ )
+      ptr_ ->Release();
+    ptr_ = p;
+    return *this;
+  }
+
+  scoped_refptr<T>& operator=(const scoped_refptr<T>& r) {
+    return *this = r.ptr_;
+  }
+
+  void swap(T** pp) {
+    T* p = ptr_;
+    ptr_ = *pp;
+    *pp = p;
+  }
+
+  void swap(scoped_refptr<T>& r) {
+    swap(&r.ptr_);
+  }
+
+ private:
+  T* ptr_;
+};
+
+#endif  // BASE_REF_COUNTED_H__
diff --git a/base/ref_counted_unittest.cc b/base/ref_counted_unittest.cc
new file mode 100644
index 0000000..ec0186c
--- /dev/null
+++ b/base/ref_counted_unittest.cc
@@ -0,0 +1,41 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "base/ref_counted.h"
+
+class SelfAssign : public base::RefCounted<SelfAssign> {
+};
+
+TEST(RefCountedUnitTest, TestSelfAssignment) {
+  SelfAssign* p = new SelfAssign;
+  scoped_refptr<SelfAssign> var = p;
+  var = var;
+  EXPECT_EQ(var.get(), p);
+}
diff --git a/base/registry.cc b/base/registry.cc
new file mode 100644
index 0000000..642973f
--- /dev/null
+++ b/base/registry.cc
@@ -0,0 +1,481 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// All Rights Reserved.
+
+#include <assert.h>
+#include <shlwapi.h>
+#include <windows.h>
+
+#include "base/registry.h"
+
+#pragma comment(lib, "shlwapi.lib")  // for SHDeleteKey
+
+// local types (see the same declarations in the header file)
+#define tchar TCHAR
+#define CTP const tchar*
+#define tstr std::basic_string<tchar>
+
+//
+// RegistryValueIterator
+//
+
+
+RegistryValueIterator::RegistryValueIterator(HKEY root_key,
+                                             LPCTSTR folder_key) {
+  LONG result = RegOpenKeyEx(root_key, folder_key, 0, KEY_READ, &key_);
+  if (result != ERROR_SUCCESS) {
+    key_ = NULL;
+  } else {
+    DWORD count = 0;
+    result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count,
+                               NULL, NULL, NULL, NULL);
+
+    if (result != ERROR_SUCCESS) {
+      ::RegCloseKey(key_);
+      key_ = NULL;
+    } else {
+      index_ = count - 1;
+    }
+  }
+
+  Read();
+}
+
+RegistryValueIterator::~RegistryValueIterator() {
+  if (key_)
+    ::RegCloseKey(key_);
+}
+
+bool RegistryValueIterator::Valid() const {
+  // true while the iterator is valid
+  return key_ != NULL && index_ >= 0;
+}
+
+
+void RegistryValueIterator::operator ++ () {
+  // advance to the next entry in the folder
+  --index_;
+  Read();
+}
+
+
+bool RegistryValueIterator::Read() {
+  if (Valid()) {
+    DWORD ncount = sizeof(name_)/sizeof(*name_);
+    value_size_ = sizeof(value_);
+    LRESULT r = ::RegEnumValue(key_, index_, name_, &ncount, NULL, &type_,
+                               reinterpret_cast<BYTE*>(value_), &value_size_);
+    if (ERROR_SUCCESS == r)
+      return true;
+  }
+
+  name_[0] = '\0';
+  value_[0] = '\0';
+  value_size_ = 0;
+  return false;
+}
+
+
+DWORD RegistryValueIterator::ValueCount() const {
+
+  DWORD count = 0;
+  HRESULT result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL);
+
+  if (result != ERROR_SUCCESS)
+    return 0;
+
+  return count;
+}
+
+
+//
+// RegistryKeyIterator
+//
+
+
+RegistryKeyIterator::RegistryKeyIterator(HKEY root_key,
+                                         LPCTSTR folder_key) {
+  LONG result = RegOpenKeyEx(root_key, folder_key, 0, KEY_READ, &key_);
+  if (result != ERROR_SUCCESS) {
+    key_ = NULL;
+  } else {
+    DWORD count = 0;
+    HRESULT result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL,
+                                       NULL, NULL, NULL, NULL, NULL);
+
+    if (result != ERROR_SUCCESS) {
+      ::RegCloseKey(key_);
+      key_ = NULL;
+    } else {
+      index_ = count - 1;
+    }
+  }
+
+  Read();
+}
+
+RegistryKeyIterator::~RegistryKeyIterator() {
+  if (key_)
+    ::RegCloseKey(key_);
+}
+
+bool RegistryKeyIterator::Valid() const {
+  // true while the iterator is valid
+  return key_ != NULL && index_ >= 0;
+}
+
+
+void RegistryKeyIterator::operator ++ () {
+  // advance to the next entry in the folder
+  --index_;
+  Read();
+}
+
+
+bool RegistryKeyIterator::Read() {
+  if (Valid()) {
+    DWORD ncount = sizeof(name_)/sizeof(*name_);
+    FILETIME written;
+    LRESULT r = ::RegEnumKeyEx(key_, index_, name_, &ncount, NULL, NULL,
+                               NULL, &written);
+    if (ERROR_SUCCESS == r)
+      return true;
+  }
+
+  name_[0] = '\0';
+  return false;
+}
+
+
+DWORD RegistryKeyIterator::SubkeyCount() const {
+
+  DWORD count = 0;
+  HRESULT result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL,
+                                     NULL, NULL, NULL, NULL, NULL);
+
+  if (result != ERROR_SUCCESS)
+    return 0;
+
+  return count;
+}
+
+
+//
+// RegKey
+//
+
+
+
+RegKey::RegKey(HKEY rootkey, const tchar* subkey, REGSAM access)
+  : key_(NULL), watch_event_(0) {
+  if (rootkey) {
+    if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK))
+      this->Create(rootkey, subkey, access);
+    else
+      this->Open(rootkey, subkey, access);
+  }
+  else assert(!subkey);
+}
+
+
+
+void RegKey::Close() {
+  StopWatching();
+  if (key_) {
+    ::RegCloseKey(key_);
+    key_ = NULL;
+  }
+}
+
+
+
+bool RegKey::Create(HKEY rootkey, const tchar* subkey, REGSAM access) {
+  DWORD disposition_value;
+  return CreateWithDisposition(rootkey, subkey, &disposition_value, access);
+}
+
+
+
+bool RegKey::CreateWithDisposition(HKEY rootkey, const tchar* subkey,
+                                   DWORD* disposition, REGSAM access) {
+  assert(rootkey && subkey && access && disposition);
+  this->Close();
+
+  LONG const result = RegCreateKeyEx(rootkey,
+                                     subkey,
+                                     0,
+                                     NULL,
+                                     REG_OPTION_NON_VOLATILE,
+                                     access,
+                                     NULL,
+                                     &key_,
+                                     disposition );
+  if (result != ERROR_SUCCESS) {
+    key_ = NULL;
+    return false;
+  }
+  else return true;
+}
+
+
+
+bool RegKey::Open(HKEY rootkey, const tchar* subkey, REGSAM access) {
+  assert(rootkey && subkey && access);
+  this->Close();
+
+  LONG const result = RegOpenKeyEx(rootkey, subkey, 0,
+                                   access, &key_ );
+  if (result != ERROR_SUCCESS) {
+    key_ = NULL;
+    return false;
+  }
+  else return true;
+}
+
+
+
+bool RegKey::CreateKey(const tchar* name, REGSAM access) {
+  assert(name && access);
+
+  HKEY subkey = NULL;
+  LONG const result = RegCreateKeyEx(key_, name, 0, NULL,
+                                     REG_OPTION_NON_VOLATILE,
+                                     access, NULL, &subkey, NULL);
+  this->Close();
+
+  key_ = subkey;
+  return (result == ERROR_SUCCESS);
+}
+
+
+
+bool RegKey::OpenKey(const tchar* name, REGSAM access) {
+  assert(name && access);
+
+  HKEY subkey = NULL;
+  LONG const result = RegOpenKeyEx(key_, name, 0, access, &subkey);
+
+  this->Close();
+
+  key_ = subkey;
+  return (result == ERROR_SUCCESS);
+}
+
+
+
+
+DWORD RegKey::ValueCount() {
+  DWORD count = 0;
+  HRESULT const result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL,
+                                     NULL, &count, NULL, NULL, NULL, NULL);
+  return (result != ERROR_SUCCESS) ? 0 : count;
+}
+
+
+bool RegKey::ReadName(int index, tstr* name) {
+  tchar buf[256];
+  DWORD bufsize = sizeof(buf)/sizeof(*buf);
+  LRESULT r = ::RegEnumValue(key_, index, buf, &bufsize, NULL, NULL,
+                             NULL, NULL);
+  if (r != ERROR_SUCCESS)
+    return false;
+  if (name)
+    *name = buf;
+  return true;
+}
+
+
+bool RegKey::ValueExists(const tchar* name) {
+  if (!key_) return false;
+  const HRESULT result = RegQueryValueEx(key_, name, 0, NULL, NULL, NULL);
+  return (result == ERROR_SUCCESS);
+}
+
+
+
+bool RegKey::ReadValue(const tchar* name, void* data,
+                       DWORD* dsize, DWORD* dtype) {
+  if (!key_) return false;
+  HRESULT const result = RegQueryValueEx(key_, name, 0, dtype,
+                                         reinterpret_cast<LPBYTE>(data),
+                                         dsize);
+  return (result == ERROR_SUCCESS);
+}
+
+
+
+bool RegKey::ReadValue(const tchar* name, tstr * value) {
+  assert(value);
+  static const size_t kMaxStringLength = 1024;  // This is after expansion.
+  // Use the one of the other forms of ReadValue if 1024 is too small for you.
+  TCHAR raw_value[kMaxStringLength];
+  DWORD type = REG_SZ, size = sizeof(raw_value);
+  if (this->ReadValue(name, raw_value, &size, &type)) {
+    if (type == REG_SZ) {
+      *value = raw_value;
+    } else if (type == REG_EXPAND_SZ) {
+      TCHAR expanded[kMaxStringLength];
+      size = ExpandEnvironmentStrings(raw_value, expanded, kMaxStringLength);
+      // Success: returns the number of TCHARs copied
+      // Fail: buffer too small, returns the size required
+      // Fail: other, returns 0
+      if (size == 0 || size > kMaxStringLength)
+        return false;
+      *value = expanded;
+    } else {
+      // Not a string. Oops.
+      return false;
+    }
+    return true;
+  }
+  else return false;
+}
+
+
+
+bool RegKey::ReadValueDW(const tchar* name, DWORD * value) {
+  assert(value);
+  DWORD type = REG_DWORD, size = sizeof(DWORD), result = 0;
+  if (this->ReadValue(name, &result, &size, &type)
+     && (type == REG_DWORD || type == REG_BINARY)
+     && size == sizeof(DWORD)) {
+    *value = result;
+    return true;
+  }
+  else return false;
+}
+
+
+
+bool RegKey::WriteValue(const tchar* name, const void * data, DWORD dsize, DWORD dtype) {
+  assert(data);
+  if (!key_) return false;
+  HRESULT const result = RegSetValueEx(key_, name, 0,
+                                       dtype,
+                                       reinterpret_cast<LPBYTE>(const_cast<void*>(data)),
+                                       dsize);
+  return (result == ERROR_SUCCESS);
+}
+
+
+
+bool RegKey::WriteValue(const tchar * name, const tchar * value) {
+  return this->WriteValue(name, value,
+    static_cast<DWORD>(sizeof(*value) * (_tcslen(value) + 1)), REG_SZ);
+}
+
+
+bool RegKey::WriteValue(const tchar * name, DWORD value) {
+  return this->WriteValue(name, &value,
+    static_cast<DWORD>(sizeof(value)), REG_DWORD);
+}
+
+
+
+bool RegKey::DeleteKey(const tchar * name) {
+  if (!key_) return false;
+  return (ERROR_SUCCESS == SHDeleteKey(key_, name));
+}
+
+
+bool RegKey::DeleteValue(const tchar * value_name) {
+  assert(value_name);
+  HRESULT const result = RegDeleteValue(key_, value_name);
+  return (result == ERROR_SUCCESS);
+}
+
+bool RegKey::StartWatching() {
+  assert(watch_event_ == 0);
+  watch_event_ = CreateEvent(NULL, TRUE, FALSE, NULL);
+  DWORD filter = REG_NOTIFY_CHANGE_NAME |
+                 REG_NOTIFY_CHANGE_ATTRIBUTES |
+                 REG_NOTIFY_CHANGE_LAST_SET |
+                 REG_NOTIFY_CHANGE_SECURITY;
+
+  // Watch the registry key for a change of value.
+  HRESULT result = RegNotifyChangeKeyValue(key_, TRUE, filter,
+                                           watch_event_, TRUE);
+  if (SUCCEEDED(result)) {
+    return true;
+  } else {
+    CloseHandle(watch_event_);
+    watch_event_ = 0;
+    return false;
+  }
+}
+
+bool RegKey::StopWatching() {
+  if (watch_event_) {
+    CloseHandle(watch_event_);
+    watch_event_ = 0;
+    return true;
+  }
+  return false;
+}
+
+bool RegKey::HasChanged() {
+  if (watch_event_) {
+    if (WaitForSingleObject(watch_event_, 0) == WAIT_OBJECT_0) {
+      // An event only gets signaled once, then it's done, so we have
+      // to set up another event to watch.
+      CloseHandle(watch_event_);
+      watch_event_ = 0;
+      StartWatching();
+      return true;
+    }
+  }
+  return false;
+}
+
+
+// Register a COM object with the most usual properties.
+bool RegisterCOMServer(const tchar* guid, const tchar* name, const tchar* path) {
+  RegKey key(HKEY_CLASSES_ROOT, _T("CLSID"), KEY_WRITE);
+  key.CreateKey(guid, KEY_WRITE);
+  key.WriteValue(NULL, name);
+  key.CreateKey(_T("InprocServer32"), KEY_WRITE);
+  key.WriteValue(NULL, path);
+  key.WriteValue(_T("ThreadingModel"), _T("Apartment"));
+  return true;
+};
+
+bool RegisterCOMServer(const tchar* guid, const tchar* name, HINSTANCE module) {
+  tchar module_path[MAX_PATH];
+  ::GetModuleFileName(module, module_path, MAX_PATH);
+  _tcslwr_s(module_path, MAX_PATH);
+  return RegisterCOMServer(guid, name, module_path);
+}
+
+bool UnregisterCOMServer(const tchar* guid) {
+  RegKey key(HKEY_CLASSES_ROOT, _T("CLSID"), KEY_WRITE);
+  key.DeleteKey(guid);
+  return true;
+}
+
+//  LocalWords:  RegKey
diff --git a/base/registry.h b/base/registry.h
new file mode 100644
index 0000000..6c70ba7
--- /dev/null
+++ b/base/registry.h
@@ -0,0 +1,249 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// All Rights Reserved.
+
+#ifndef BASE_REGISTRY_H__
+#define BASE_REGISTRY_H__
+
+#include <windows.h>
+#include <tchar.h>
+#include <shlwapi.h>
+#include <string>
+
+// The shared file uses a bunch of header files that define types that we don't.
+// To avoid changing much code from the standard version, and also to avoid
+// polluting our namespace with extra types we don't want, we define these types
+// here with the preprocessor and undefine them at the end of the file.
+#define tchar TCHAR
+#define CTP const tchar*
+#define tstr std::basic_string<tchar>
+
+// RegKey
+// Utility class to read from and manipulate the registry.
+// Registry vocabulary primer: a "key" is like a folder, in which there
+// are "values", which are <name,data> pairs, with an associated data type.
+
+class RegKey {
+ public:
+  RegKey(HKEY rootkey = NULL, CTP subkey = NULL, REGSAM access = KEY_READ);
+    // start there
+
+  ~RegKey() { this->Close(); }
+
+  bool Create(HKEY rootkey, CTP subkey, REGSAM access = KEY_READ);
+
+  bool CreateWithDisposition(HKEY rootkey, CTP subkey, DWORD* disposition,
+                             REGSAM access = KEY_READ);
+
+  bool Open(HKEY rootkey, CTP subkey, REGSAM access = KEY_READ);
+
+  // Create a subkey (or open if exists)
+  bool CreateKey(CTP name, REGSAM access);
+
+  // Open a subkey
+  bool OpenKey(CTP name, REGSAM access);
+
+  // all done, eh?
+  void Close();
+
+  DWORD ValueCount();  // Count of the number of value extant
+
+  bool ReadName(int index, tstr* name);  // Determine the Nth value's name
+
+  // True while the key is valid
+  bool Valid() const { return NULL != key_; }
+
+  // Kill key and everything that liveth below it; please be careful out there
+  bool DeleteKey(CTP name);
+
+  // Delete a single value within the key
+  bool DeleteValue(CTP name);
+
+  bool ValueExists(CTP name);
+  bool ReadValue(CTP name, void * data, DWORD * dsize, DWORD * dtype = NULL);
+  bool ReadValue(CTP name, tstr * value);
+  bool ReadValueDW(CTP name, DWORD * value);  // Named to differ from tstr*
+
+  bool WriteValue(CTP name, const void * data, DWORD dsize,
+                  DWORD dtype = REG_BINARY);
+  bool WriteValue(CTP name, CTP value);
+  bool WriteValue(CTP name, DWORD value);
+
+  // StartWatching()
+  //   Start watching the key to see if any of its values have changed.
+  //   The key must have been opened with the KEY_NOTIFY access
+  //   privelege.
+  bool StartWatching();
+
+  // HasChanged()
+  //   If StartWatching hasn't been called, always returns false.
+  //   Otherwise, returns true if anything under the key has changed.
+  //   This can't be const because the watch_event_ may be refreshed.
+  bool HasChanged();
+
+  // StopWatching()
+  //   Will automatically be called by destructor if not manually called
+  //   beforehand.  Returns true if it was watching, false otherwise.
+  bool StopWatching();
+
+  inline bool IsWatching() const { return watch_event_ != 0; }
+  HKEY Handle() const { return key_; }
+
+ private:
+  HKEY  key_;    // the registry key being iterated
+  HANDLE watch_event_;
+};
+
+
+// Standalone registry functions -- sorta deprecated, they now map to
+// using RegKey
+
+
+// Add a raw data to the registry -- you can pass NULL for the data if
+// you just want to create a key
+inline bool AddToRegistry(HKEY root_key, CTP key, CTP value_name,
+                          void const * data, DWORD dsize,
+                          DWORD dtype = REG_BINARY) {
+  return RegKey(root_key, key, KEY_WRITE).WriteValue(value_name, data, dsize,
+                                                     dtype);
+}
+
+// Convenience routine to add a string value to the registry
+inline bool AddToRegistry(HKEY root_key, CTP key, CTP value_name, CTP value) {
+  return AddToRegistry(root_key, key, value_name, value,
+                       sizeof(*value) * (lstrlen(value) + 1), REG_SZ);
+}
+
+// Read raw data from the registry -- pass something as the dtype
+// parameter if you care to learn what type the value was stored as
+inline bool ReadFromRegistry(HKEY root_key, CTP key, CTP value_name,
+                             void* data, DWORD* dsize, DWORD* dtype = NULL) {
+  return RegKey(root_key, key).ReadValue(value_name, data, dsize, dtype);
+}
+
+
+// Delete a value or a key from the registry
+inline bool DeleteFromRegistry(HKEY root_key, CTP subkey, CTP value_name) {
+  if (value_name)
+    return ERROR_SUCCESS == ::SHDeleteValue(root_key, subkey, value_name);
+  else
+    return ERROR_SUCCESS == ::SHDeleteKey(root_key, subkey);
+}
+
+
+
+// delete a key and all subkeys from the registry
+inline bool DeleteKeyFromRegistry(HKEY root_key, CTP key_path, CTP key_name) {
+  RegKey key;
+  return key.Open(root_key, key_path, KEY_WRITE)
+      && key.DeleteKey(key_name);
+}
+
+
+// Iterates the entries found in a particular folder on the registry.
+// For this application I happen to know I wont need data size larger
+// than MAX_PATH, but in real life this wouldn't neccessarily be
+// adequate.
+class RegistryValueIterator {
+ public:
+  // Specify a key in construction
+  RegistryValueIterator(HKEY root_key, LPCTSTR folder_key);
+
+  ~RegistryValueIterator();
+
+  DWORD ValueCount() const;  // count of the number of subkeys extant
+
+  bool Valid() const;  // true while the iterator is valid
+
+  void operator++();  // advance to the next entry in the folder
+
+  // The pointers returned by these functions are statics owned by the
+  // Name and Value functions
+  CTP Name() const { return name_; }
+  CTP Value() const { return value_; }
+  DWORD ValueSize() const { return value_size_; }
+  DWORD Type() const { return type_; }
+
+  int Index() const { return index_; }
+
+ private:
+  bool Read();   // read in the current values
+
+  HKEY  key_;    // the registry key being iterated
+  int   index_;  // current index of the iteration
+
+  // Current values
+  TCHAR name_[MAX_PATH];
+  TCHAR value_[MAX_PATH];
+  DWORD value_size_;
+  DWORD type_;
+};
+
+
+class RegistryKeyIterator {
+ public:
+  // Specify a parent key in construction
+  RegistryKeyIterator(HKEY root_key, LPCTSTR folder_key);
+
+  ~RegistryKeyIterator();
+
+  DWORD SubkeyCount() const;  // count of the number of subkeys extant
+
+  bool Valid() const;  // true while the iterator is valid
+
+  void operator++();  // advance to the next entry in the folder
+
+  // The pointer returned by Name() is a static owned by the function
+  CTP Name() const { return name_; }
+
+  int Index() const { return index_; }
+
+ private:
+  bool Read();   // read in the current values
+
+  HKEY  key_;    // the registry key being iterated
+  int   index_;  // current index of the iteration
+
+  // Current values
+  TCHAR name_[MAX_PATH];
+};
+
+
+// Register a COM object with the most usual properties.
+bool RegisterCOMServer(const tchar* guid, const tchar* name,
+                       const tchar* modulepath);
+bool RegisterCOMServer(const tchar* guid, const tchar* name, HINSTANCE module);
+bool UnregisterCOMServer(const tchar* guid);
+
+// undo the local types defined above
+#undef tchar
+#undef CTP
+#undef tstr
+
+#endif  // BASE_REGISTRY_H__
diff --git a/base/resource_util.cc b/base/resource_util.cc
new file mode 100644
index 0000000..e3b5504
--- /dev/null
+++ b/base/resource_util.cc
@@ -0,0 +1,57 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/resource_util.h"
+
+#include "base/logging.h"
+
+namespace base {
+bool GetDataResourceFromModule(HMODULE module, int resource_id,
+                               void** data, size_t* length) {
+  if (!module)
+    return false;
+
+  // Get a pointer to the data in the dll.
+  DCHECK(IS_INTRESOURCE(resource_id));
+  HRSRC hres_info = FindResource(module, MAKEINTRESOURCE(resource_id),
+                                 L"BINDATA");
+  if (NULL == hres_info)
+    return false;
+
+  DWORD data_size = SizeofResource(module, hres_info);
+
+  HGLOBAL hres = LoadResource(module, hres_info);
+  if (!hres_info)
+    return false;
+
+  *data = LockResource(hres);
+  *length = static_cast<size_t>(data_size);
+  return true;
+}
+}  // namespace
diff --git a/base/resource_util.h b/base/resource_util.h
new file mode 100644
index 0000000..b592cc2
--- /dev/null
+++ b/base/resource_util.h
@@ -0,0 +1,49 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file contains utility functions for accessing resources in external
+// files (DLLs) or embedded in the executable itself.
+
+#ifndef BASE_RESOURCE_UTIL_H__
+#define BASE_RESOURCE_UTIL_H__
+
+#include <windows.h>
+#include <string>
+
+#include "base/basictypes.h"
+
+namespace base {
+// Function for getting a data resource (BINDATA) from a dll.  Some
+// resources are optional, especially in unit tests, so this returns false
+// but doesn't raise an error if the resource can't be loaded.
+bool GetDataResourceFromModule(HMODULE module, int resource_id,
+                               void** data, size_t* length);
+}  // namespace
+
+#endif // BASE_RESOURCE_UTIL_H__
diff --git a/base/revocable_store.cc b/base/revocable_store.cc
new file mode 100644
index 0000000..2974792
--- /dev/null
+++ b/base/revocable_store.cc
@@ -0,0 +1,72 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/revocable_store.h"
+
+#include "base/logging.h"
+
+RevocableStore::Revocable::Revocable(RevocableStore* store)
+    : store_reference_(store->owning_reference_) {
+  // We AddRef() the owning reference.
+  DCHECK(store_reference_->store());
+  store_reference_->store()->Add(this);
+}
+
+RevocableStore::Revocable::~Revocable() {
+  if (!revoked()) {
+    // Notify the store of our destruction.
+    --(store_reference_->store()->count_);
+  }
+}
+
+RevocableStore::RevocableStore() : count_(0) {
+  // Create a new owning reference.
+  owning_reference_ = new StoreRef(this);
+}
+
+RevocableStore::~RevocableStore() {
+  // Revoke all the items in the store.
+  owning_reference_->set_store(NULL);
+}
+
+void RevocableStore::Add(Revocable* item) {
+  DCHECK(!item->revoked());
+  ++count_;
+}
+
+void RevocableStore::RevokeAll() {
+  // We revoke all the existing items in the store and reset our count.
+  owning_reference_->set_store(NULL);
+  count_ = 0;
+
+  // Then we create a new owning reference for new items that get added.
+  // This Release()s the old owning reference, allowing it to be freed after
+  // all the items that were in the store are eventually destroyed.
+  owning_reference_ = new StoreRef(this);
+}
diff --git a/base/revocable_store.h b/base/revocable_store.h
new file mode 100644
index 0000000..cee1c64
--- /dev/null
+++ b/base/revocable_store.h
@@ -0,0 +1,101 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_REVOCABLE_STORE_H__
+#define BASE_REVOCABLE_STORE_H__
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/ref_counted.h"
+
+// |RevocableStore| is a container of items that can be removed from the store.
+class RevocableStore {
+ public:
+  // A |StoreRef| is used to link the |RevocableStore| to its items.  There is
+  // one StoreRef per store, and each item holds a reference to it.  If the
+  // store wishes to revoke its items, it sets |store_| to null.  Items are
+  // permitted to release their reference to the |StoreRef| when they no longer
+  // require the store.
+ class StoreRef : public base::RefCounted<StoreRef> {
+   public:
+    StoreRef(RevocableStore* store) : store_(store) { }
+
+    void set_store(RevocableStore* store) { store_ = store; }
+    RevocableStore* store() const { return store_; }
+
+   private:
+    RevocableStore* store_;
+
+    DISALLOW_EVIL_CONSTRUCTORS(StoreRef);
+  };
+
+  // An item in the store.  On construction, the object adds itself to the
+  // store.
+  class Revocable {
+   public:
+    Revocable(RevocableStore* store);
+    ~Revocable();
+
+    // This item has been revoked if it no longer has a pointer to the store.
+    bool revoked() const { return !store_reference_->store(); }
+
+  private:
+    // We hold a reference to the store through this ref pointer.  We release
+    // this reference on destruction.
+    scoped_refptr<StoreRef> store_reference_;
+
+    DISALLOW_EVIL_CONSTRUCTORS(Revocable);
+  };
+
+  RevocableStore();
+  ~RevocableStore();
+
+  // Revokes all the items in the store.
+  void RevokeAll();
+
+  // Returns true if there are no items in the store.
+  bool empty() const { return count_ == 0; }
+
+ private:
+  friend class Revocable;
+
+  // Adds an item to the store.  To add an item to the store, construct it
+  // with a pointer to the store.
+  void Add(Revocable* item);
+
+  // This is the reference the unrevoked items in the store hold.
+  scoped_refptr<StoreRef> owning_reference_;
+
+  // The number of unrevoked items in the store.
+  int count_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(RevocableStore);
+};
+
+#endif  // BASE_REVOCABLE_STORE_H__
diff --git a/base/run_all_perftests.cc b/base/run_all_perftests.cc
new file mode 100644
index 0000000..4ef7494
--- /dev/null
+++ b/base/run_all_perftests.cc
@@ -0,0 +1,54 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+
+#include "base/command_line.h"
+#include "base/perftimer.h"
+#include "base/string_util.h"
+#include "base/test_suite.h"
+
+int main(int argc, char** argv) {
+  // Initialize the perf timer log
+  std::string log_file =
+      WideToUTF8(CommandLine().GetSwitchValue(L"log-file"));
+  if (log_file.empty())
+    log_file = "perf_test.log";
+  ASSERT_TRUE(InitPerfLog(log_file.c_str()));
+
+  // Raise to high priority to have more precise measurements. Since we don't
+  // aim at 1% precision, it is not necessary to run at realtime level.
+  if (!IsDebuggerPresent())
+    SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
+
+  int rv = TestSuite(argc, argv).Run();
+
+  FinalizePerfLog();
+  return rv;
+}
diff --git a/base/run_all_unittests.cc b/base/run_all_unittests.cc
new file mode 100644
index 0000000..69b2d3f
--- /dev/null
+++ b/base/run_all_unittests.cc
@@ -0,0 +1,34 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/test_suite.h"
+
+int main(int argc, char** argv) {
+  return TestSuite(argc, argv).Run();
+}
diff --git a/base/scoped_cftyperef.h b/base/scoped_cftyperef.h
new file mode 100644
index 0000000..f4623fe
--- /dev/null
+++ b/base/scoped_cftyperef.h
@@ -0,0 +1,97 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_SCOPED_CFTYPEREF_H__
+#define BASE_SCOPED_CFTYPEREF_H__
+
+#include <CoreFoundation/CoreFoundation.h>
+
+// scoped_cftyperef<> is patterned after scoped_ptr<>, but maintains ownership
+// of a CoreFoundation object: any object that can be represented as a
+// CFTypeRef.  Style deviations here are solely for compatibility with
+// scoped_ptr<>'s interface, with which everyone is already familiar.
+template<typename CFT>
+class scoped_cftyperef {
+ public:
+  typedef CFT element_type;
+
+  explicit scoped_cftyperef(CFT object = NULL)
+      : object_(object) {
+  }
+
+  ~scoped_cftyperef() {
+    if (object_)
+      CFRelease(object_);
+  }
+
+  void reset(CFT object = NULL) {
+    if (object_ && object_ != object) {
+      CFRelease(object_);
+      object_ = object;
+    }
+  }
+
+  bool operator==(CFT that) const {
+    return object_ == that;
+  }
+
+  bool operator!=(CFT that) const {
+    return object_ != that;
+  }
+
+  operator CFT() const {
+    return object_;
+  }
+
+  CFT get() const {
+    return object_;
+  }
+
+  void swap(scoped_cftyperef& that) {
+    CFT temp = that.object_;
+    that.object_ = object_;
+    object_ = temp;
+  }
+
+  // scoped_cftyperef<>::release() is like scoped_ptr<>::release.  It is NOT
+  // a wrapper for CFRelease().  To force a scoped_cftyperef<> object to call
+  // CFRelease(), use scoped_cftyperef<>::reset().
+  CFT release() {
+    CFT temp = object_;
+    object_ = NULL;
+    return temp;
+  }
+
+ private:
+  CFT object_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(scoped_cftyperef);
+};
+
+#endif  // BASE_SCOPED_CFTYPEREF_H__
diff --git a/base/scoped_handle.h b/base/scoped_handle.h
new file mode 100644
index 0000000..63f8d00
--- /dev/null
+++ b/base/scoped_handle.h
@@ -0,0 +1,213 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_SCOPED_HANDLE_H__
+#define BASE_SCOPED_HANDLE_H__
+
+#include <windows.h>
+
+#include "base/basictypes.h"
+
+// Used so we always remember to close the handle. Example:
+//   ScopedHandle hfile(CreateFile(...));
+//   if (!hfile.Get())
+//     ...process error
+//   ReadFile(hfile.Get(), ...);
+//
+// To sqirrel the handle away somewhere else:
+//   secret_handle_ = hfile.Take();
+//
+// To explicitly close the handle:
+//   CloseHandle(hfile.Take());
+class ScopedHandle {
+ public:
+  ScopedHandle() : handle_(NULL) {
+  }
+
+  explicit ScopedHandle(HANDLE h) : handle_(NULL) {
+    Set(h);
+  }
+
+  ~ScopedHandle() {
+    Close();
+  }
+
+  // Use this instead of comparing to INVALID_HANDLE_VALUE to pick up our NULL
+  // usage for errors.
+  bool IsValid() const {
+    return handle_ != NULL;
+  }
+
+  void Set(HANDLE new_handle) {
+    Close();
+
+    // Windows is inconsistent about invalid handles, so we always use NULL
+    if (handle_ != INVALID_HANDLE_VALUE)
+      handle_ = new_handle;
+  }
+
+  HANDLE Get() {
+    return handle_;
+  }
+
+  operator HANDLE() { return handle_; }
+
+  HANDLE Take() {
+    // transfers ownership away from this object
+    HANDLE h = handle_;
+    handle_ = NULL;
+    return h;
+  }
+
+ private:
+  void Close() {
+    if (handle_) {
+      CloseHandle(handle_);
+      handle_ = NULL;
+    }
+  }
+
+  HANDLE handle_;
+  DISALLOW_EVIL_CONSTRUCTORS(ScopedHandle);
+};
+
+// Like ScopedHandle, but for HANDLEs returned from FindFile().
+class ScopedFindFileHandle {
+ public:
+  explicit ScopedFindFileHandle(HANDLE handle) : handle_(handle) {
+    // Windows is inconsistent about invalid handles, so we always use NULL
+    if (handle_ == INVALID_HANDLE_VALUE)
+      handle_ = NULL;
+  }
+
+  ~ScopedFindFileHandle() {
+    if (handle_)
+      FindClose(handle_);
+  }
+
+  // Use this instead of comparing to INVALID_HANDLE_VALUE to pick up our NULL
+  // usage for errors.
+  bool IsValid() const { return handle_ != NULL; }
+
+  operator HANDLE() { return handle_; }
+
+ private:
+  HANDLE handle_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(ScopedFindFileHandle);
+};
+
+// Like ScopedHandle but for HDC.  Only use this on HDCs returned from
+// CreateCompatibleDC.  For an HDC returned by GetDC, use ReleaseDC instead.
+class ScopedHDC {
+ public:
+  explicit ScopedHDC(HDC h) : hdc_(h) { }
+
+  ~ScopedHDC() {
+    if (hdc_)
+      DeleteDC(hdc_);
+  }
+
+  operator HDC() { return hdc_; }
+
+ private:
+  HDC hdc_;
+  DISALLOW_EVIL_CONSTRUCTORS(ScopedHDC);
+};
+
+// Like ScopedHandle but for HBITMAP.
+class ScopedBitmap {
+ public:
+  explicit ScopedBitmap(HBITMAP h) : hbitmap_(h) { }
+
+  ~ScopedBitmap() {
+    if (hbitmap_)
+      DeleteObject(hbitmap_);
+  }
+
+  operator HBITMAP() { return hbitmap_; }
+
+ private:
+  HBITMAP hbitmap_;
+  DISALLOW_EVIL_CONSTRUCTORS(ScopedBitmap);
+};
+
+// Like ScopedHandle but for HRGN.
+class ScopedHRGN {
+ public:
+  explicit ScopedHRGN(HRGN h) : hrgn_(h) { }
+
+  ~ScopedHRGN() {
+    if (hrgn_)
+      DeleteObject(hrgn_);
+  }
+
+  operator HRGN() { return hrgn_; }
+
+  ScopedHRGN& operator=(HRGN hrgn) {
+    if (hrgn_ && hrgn != hrgn_)
+      DeleteObject(hrgn_);
+    hrgn_ = hrgn;
+    return *this;
+  }
+
+ private:
+  HRGN hrgn_;
+  DISALLOW_EVIL_CONSTRUCTORS(ScopedHRGN);
+};
+
+// Like ScopedHandle except for HGLOBAL.
+template<class T>
+class ScopedHGlobal {
+ public:
+  explicit ScopedHGlobal(HGLOBAL glob) : glob_(glob) {
+    data_ = static_cast<T*>(GlobalLock(glob_));
+  }
+  ~ScopedHGlobal() {
+    GlobalUnlock(glob_);
+  }
+
+  T* get() { return data_; }
+
+  size_t Size() const { return GlobalSize(glob_); }
+
+  T* operator->() const  {
+    assert(data_ != 0);
+    return data_;
+  }
+
+ private:
+  HGLOBAL glob_;
+
+  T* data_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(ScopedHGlobal);
+};
+
+#endif // BASE_SCOPED_HANDLE_H__
diff --git a/base/scoped_ptr.h b/base/scoped_ptr.h
new file mode 100644
index 0000000..0c9ea95
--- /dev/null
+++ b/base/scoped_ptr.h
@@ -0,0 +1,402 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// All Rights Reserved.
+
+#ifndef BASE_SCOPED_PTR_H__
+#define BASE_SCOPED_PTR_H__
+
+//  This is an implementation designed to match the anticipated future TR2
+//  implementation of the scoped_ptr class, and its closely-related brethren,
+//  scoped_array, scoped_ptr_malloc, and make_scoped_ptr.
+//
+//  See http://wiki/Main/ScopedPointerInterface for the spec that drove this
+//  file.
+
+#include <assert.h>
+#include <stdlib.h>
+#include <cstddef>
+
+
+template <class C> class scoped_ptr;
+template <class C, class Free> class scoped_ptr_malloc;
+template <class C> class scoped_array;
+
+template <class C>
+scoped_ptr<C> make_scoped_ptr(C *);
+
+// A scoped_ptr<T> is like a T*, except that the destructor of scoped_ptr<T>
+// automatically deletes the pointer it holds (if any).
+// That is, scoped_ptr<T> owns the T object that it points to.
+// Like a T*, a scoped_ptr<T> may hold either NULL or a pointer to a T object.
+// Also like T*, scoped_ptr<T> is thread-compatible, and once you
+// dereference it, you get the threadsafety guarantees of T.
+//
+// The size of a scoped_ptr is small:
+// sizeof(scoped_ptr<C>) == sizeof(C*)
+template <class C>
+class scoped_ptr {
+ public:
+
+  // The element type
+  typedef C element_type;
+
+  // Constructor.  Defaults to intializing with NULL.
+  // There is no way to create an uninitialized scoped_ptr.
+  // The input parameter must be allocated with new.
+  explicit scoped_ptr(C* p = NULL) : ptr_(p) { }
+
+  // Destructor.  If there is a C object, delete it.
+  // We don't need to test ptr_ == NULL because C++ does that for us.
+  ~scoped_ptr() {
+    enum { type_must_be_complete = sizeof(C) };
+    delete ptr_;
+  }
+
+  // Reset.  Deletes the current owned object, if any.
+  // Then takes ownership of a new object, if given.
+  // this->reset(this->get()) works.
+  void reset(C* p = NULL) {
+    if (p != ptr_) {
+      enum { type_must_be_complete = sizeof(C) };
+      delete ptr_;
+      ptr_ = p;
+    }
+  }
+
+  // Accessors to get the owned object.
+  // operator* and operator-> will assert() if there is no current object.
+  C& operator*() const {
+    assert(ptr_ != NULL);
+    return *ptr_;
+  }
+  C* operator->() const  {
+    assert(ptr_ != NULL);
+    return ptr_;
+  }
+  C* get() const { return ptr_; }
+
+  // Comparison operators.
+  // These return whether two scoped_ptr refer to the same object, not just to
+  // two different but equal objects.
+  bool operator==(C* p) const { return ptr_ == p; }
+  bool operator!=(C* p) const { return ptr_ != p; }
+
+  // Swap two scoped pointers.
+  void swap(scoped_ptr& p2) {
+    C* tmp = ptr_;
+    ptr_ = p2.ptr_;
+    p2.ptr_ = tmp;
+  }
+
+  // Release a pointer.
+  // The return value is the current pointer held by this object.
+  // If this object holds a NULL pointer, the return value is NULL.
+  // After this operation, this object will hold a NULL pointer,
+  // and will not own the object any more.
+  C* release() {
+    C* retVal = ptr_;
+    ptr_ = NULL;
+    return retVal;
+  }
+
+ private:
+  C* ptr_;
+
+  friend scoped_ptr<C> make_scoped_ptr<C>(C *p);
+
+  // Forbid comparison of scoped_ptr types.  If C2 != C, it totally doesn't
+  // make sense, and if C2 == C, it still doesn't make sense because you should
+  // never have the same object owned by two different scoped_ptrs.
+  template <class C2> bool operator==(scoped_ptr<C2> const& p2) const;
+  template <class C2> bool operator!=(scoped_ptr<C2> const& p2) const;
+
+  // Disallow evil constructors
+  scoped_ptr(const scoped_ptr&);
+  void operator=(const scoped_ptr&);
+};
+
+// Free functions
+template <class C>
+void swap(scoped_ptr<C>& p1, scoped_ptr<C>& p2) {
+  p1.swap(p2);
+}
+
+template <class C>
+bool operator==(C* p1, const scoped_ptr<C>& p2) {
+  return p1 == p2.get();
+}
+
+template <class C>
+bool operator!=(C* p1, const scoped_ptr<C>& p2) {
+  return p1 != p2.get();
+}
+
+template <class C>
+scoped_ptr<C> make_scoped_ptr(C *p) {
+  // This does nothing but to return a scoped_ptr of the type that the passed
+  // pointer is of.  (This eliminates the need to specify the name of T when
+  // making a scoped_ptr that is used anonymously/temporarily.)  From an
+  // access control point of view, we construct an unnamed scoped_ptr here
+  // which we return and thus copy-construct.  Hence, we need to have access
+  // to scoped_ptr::scoped_ptr(scoped_ptr const &).  However, it is guaranteed
+  // that we never actually call the copy constructor, which is a good thing
+  // as we would call the temporary's object destructor (and thus delete p)
+  // if we actually did copy some object, here.
+  return scoped_ptr<C>(p);
+}
+
+// scoped_array<C> is like scoped_ptr<C>, except that the caller must allocate
+// with new [] and the destructor deletes objects with delete [].
+//
+// As with scoped_ptr<C>, a scoped_array<C> either points to an object
+// or is NULL.  A scoped_array<C> owns the object that it points to.
+// scoped_array<T> is thread-compatible, and once you index into it,
+// the returned objects have only the threadsafety guarantees of T.
+//
+// Size: sizeof(scoped_array<C>) == sizeof(C*)
+template <class C>
+class scoped_array {
+ public:
+
+  // The element type
+  typedef C element_type;
+
+  // Constructor.  Defaults to intializing with NULL.
+  // There is no way to create an uninitialized scoped_array.
+  // The input parameter must be allocated with new [].
+  explicit scoped_array(C* p = NULL) : array_(p) { }
+
+  // Destructor.  If there is a C object, delete it.
+  // We don't need to test ptr_ == NULL because C++ does that for us.
+  ~scoped_array() {
+    enum { type_must_be_complete = sizeof(C) };
+    delete[] array_;
+  }
+
+  // Reset.  Deletes the current owned object, if any.
+  // Then takes ownership of a new object, if given.
+  // this->reset(this->get()) works.
+  void reset(C* p = NULL) {
+    if (p != array_) {
+      enum { type_must_be_complete = sizeof(C) };
+      delete[] array_;
+      array_ = p;
+    }
+  }
+
+  // Get one element of the current object.
+  // Will assert() if there is no current object, or index i is negative.
+  C& operator[](std::ptrdiff_t i) const {
+    assert(i >= 0);
+    assert(array_ != NULL);
+    return array_[i];
+  }
+
+  // Get a pointer to the zeroth element of the current object.
+  // If there is no current object, return NULL.
+  C* get() const {
+    return array_;
+  }
+
+  // Comparison operators.
+  // These return whether two scoped_array refer to the same object, not just to
+  // two different but equal objects.
+  bool operator==(C* p) const { return array_ == p; }
+  bool operator!=(C* p) const { return array_ != p; }
+
+  // Swap two scoped arrays.
+  void swap(scoped_array& p2) {
+    C* tmp = array_;
+    array_ = p2.array_;
+    p2.array_ = tmp;
+  }
+
+  // Release an array.
+  // The return value is the current pointer held by this object.
+  // If this object holds a NULL pointer, the return value is NULL.
+  // After this operation, this object will hold a NULL pointer,
+  // and will not own the object any more.
+  C* release() {
+    C* retVal = array_;
+    array_ = NULL;
+    return retVal;
+  }
+
+ private:
+  C* array_;
+
+  // Forbid comparison of different scoped_array types.
+  template <class C2> bool operator==(scoped_array<C2> const& p2) const;
+  template <class C2> bool operator!=(scoped_array<C2> const& p2) const;
+
+  // Disallow evil constructors
+  scoped_array(const scoped_array&);
+  void operator=(const scoped_array&);
+};
+
+// Free functions
+template <class C>
+void swap(scoped_array<C>& p1, scoped_array<C>& p2) {
+  p1.swap(p2);
+}
+
+template <class C>
+bool operator==(C* p1, const scoped_array<C>& p2) {
+  return p1 == p2.get();
+}
+
+template <class C>
+bool operator!=(C* p1, const scoped_array<C>& p2) {
+  return p1 != p2.get();
+}
+
+// This class wraps the c library function free() in a class that can be
+// passed as a template argument to scoped_ptr_malloc below.
+class ScopedPtrMallocFree {
+ public:
+  inline void operator()(void* x) const {
+    free(x);
+  }
+};
+
+// scoped_ptr_malloc<> is similar to scoped_ptr<>, but it accepts a
+// second template argument, the functor used to free the object.
+
+template<class C, class FreeProc = ScopedPtrMallocFree>
+class scoped_ptr_malloc {
+ public:
+
+  // The element type
+  typedef C element_type;
+
+  // Constructor.  Defaults to intializing with NULL.
+  // There is no way to create an uninitialized scoped_ptr.
+  // The input parameter must be allocated with an allocator that matches the
+  // Free functor.  For the default Free functor, this is malloc, calloc, or
+  // realloc.
+  explicit scoped_ptr_malloc(C* p = NULL): ptr_(p) {}
+
+  // Destructor.  If there is a C object, call the Free functor.
+  ~scoped_ptr_malloc() {
+    free_(ptr_);
+  }
+
+  // Reset.  Calls the Free functor on the current owned object, if any.
+  // Then takes ownership of a new object, if given.
+  // this->reset(this->get()) works.
+  void reset(C* p = NULL) {
+    if (ptr_ != p) {
+      free_(ptr_);
+      ptr_ = p;
+    }
+  }
+
+  // Get the current object.
+  // operator* and operator-> will cause an assert() failure if there is
+  // no current object.
+  C& operator*() const {
+    assert(ptr_ != NULL);
+    return *ptr_;
+  }
+
+  C* operator->() const {
+    assert(ptr_ != NULL);
+    return ptr_;
+  }
+
+  C* get() const {
+    return ptr_;
+  }
+
+  // Comparison operators.
+  // These return whether a scoped_ptr_malloc and a plain pointer refer
+  // to the same object, not just to two different but equal objects.
+  // For compatibility wwith the boost-derived implementation, these
+  // take non-const arguments.
+  bool operator==(C* p) const {
+    return ptr_ == p;
+  }
+
+  bool operator!=(C* p) const {
+    return ptr_ != p;
+  }
+
+  // Swap two scoped pointers.
+  void swap(scoped_ptr_malloc & b) {
+    C* tmp = b.ptr_;
+    b.ptr_ = ptr_;
+    ptr_ = tmp;
+  }
+
+  // Release a pointer.
+  // The return value is the current pointer held by this object.
+  // If this object holds a NULL pointer, the return value is NULL.
+  // After this operation, this object will hold a NULL pointer,
+  // and will not own the object any more.
+  C* release() {
+    C* tmp = ptr_;
+    ptr_ = NULL;
+    return tmp;
+  }
+
+ private:
+  C* ptr_;
+
+  // no reason to use these: each scoped_ptr_malloc should have its own object
+  template <class C2, class GP>
+  bool operator==(scoped_ptr_malloc<C2, GP> const& p) const;
+  template <class C2, class GP>
+  bool operator!=(scoped_ptr_malloc<C2, GP> const& p) const;
+
+  static FreeProc const free_;
+
+  // Disallow evil constructors
+  scoped_ptr_malloc(const scoped_ptr_malloc&);
+  void operator=(const scoped_ptr_malloc&);
+};
+
+template<class C, class FP>
+FP const scoped_ptr_malloc<C, FP>::free_ = FP();
+
+template<class C, class FP> inline
+void swap(scoped_ptr_malloc<C, FP>& a, scoped_ptr_malloc<C, FP>& b) {
+  a.swap(b);
+}
+
+template<class C, class FP> inline
+bool operator==(C* p, const scoped_ptr_malloc<C, FP>& b) {
+  return p == b.get();
+}
+
+template<class C, class FP> inline
+bool operator!=(C* p, const scoped_ptr_malloc<C, FP>& b) {
+  return p != b.get();
+}
+
+#endif  // BASE_SCOPED_PTR_H__
diff --git a/base/sha2.cc b/base/sha2.cc
new file mode 100644
index 0000000..4737a7c
--- /dev/null
+++ b/base/sha2.cc
@@ -0,0 +1,47 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/sha2.h"
+
+#include "base/third_party/nss/blapi.h"
+#include "base/third_party/nss/sha256.h"
+
+namespace base {
+
+void SHA256HashString(const std::string& str, void* output, size_t len) {
+  SHA256Context ctx;
+
+  SHA256_Begin(&ctx);
+  SHA256_Update(&ctx, reinterpret_cast<const unsigned char*>(str.data()),
+                static_cast<unsigned int>(str.length()));
+  SHA256_End(&ctx, static_cast<unsigned char*>(output), NULL,
+             static_cast<unsigned int>(len));
+}
+
+}  // namespace base
diff --git a/base/sha2.h b/base/sha2.h
new file mode 100644
index 0000000..b9d5fb2
--- /dev/null
+++ b/base/sha2.h
@@ -0,0 +1,52 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_SHA2_H__
+#define BASE_SHA2_H__
+
+#include <string>
+
+namespace base {
+
+// These functions perform SHA-256 operations.
+//
+// Functions for SHA-384 and SHA-512 can be added when the need arises.
+
+enum {
+  SHA256_LENGTH = 32  // length in bytes of a SHA-256 hash
+};
+
+// Computes the SHA-256 hash of the input string 'str' and stores the first
+// 'len' bytes of the hash in the output buffer 'output'.  If 'len' > 32,
+// only 32 bytes (the full hash) are stored in the 'output' buffer.
+void SHA256HashString(const std::string& str, void* output, size_t len);
+
+}  // namespace base
+
+#endif // BASE_SHA2_H__
diff --git a/base/sha2_unittest.cc b/base/sha2_unittest.cc
new file mode 100644
index 0000000..c0d6343
--- /dev/null
+++ b/base/sha2_unittest.cc
@@ -0,0 +1,103 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/sha2.h"
+
+#include "base/basictypes.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(Sha256Test, Test1) {
+  // Example B.1 from FIPS 180-2: one-block message.
+  std::string input1 = "abc";
+  int expected1[] = { 0xba, 0x78, 0x16, 0xbf,
+                      0x8f, 0x01, 0xcf, 0xea,
+                      0x41, 0x41, 0x40, 0xde,
+                      0x5d, 0xae, 0x22, 0x23,
+                      0xb0, 0x03, 0x61, 0xa3,
+                      0x96, 0x17, 0x7a, 0x9c,
+                      0xb4, 0x10, 0xff, 0x61,
+                      0xf2, 0x00, 0x15, 0xad };
+
+  uint8 output1[base::SHA256_LENGTH];
+  base::SHA256HashString(input1, output1, sizeof(output1));
+  for (int i = 0; i < base::SHA256_LENGTH; i++)
+    EXPECT_EQ(expected1[i], static_cast<int>(output1[i]));
+
+  uint8 output_truncated1[4];  // 4 bytes == 32 bits
+  base::SHA256HashString(input1, output_truncated1, sizeof(output_truncated1));
+  for (int i = 0; i < sizeof(output_truncated1); i++)
+    EXPECT_EQ(expected1[i], static_cast<int>(output_truncated1[i]));
+}
+
+TEST(Sha256Test, Test2) {
+  // Example B.2 from FIPS 180-2: multi-block message.
+  std::string input2 =
+      "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
+  int expected2[] = { 0x24, 0x8d, 0x6a, 0x61,
+                      0xd2, 0x06, 0x38, 0xb8,
+                      0xe5, 0xc0, 0x26, 0x93,
+                      0x0c, 0x3e, 0x60, 0x39,
+                      0xa3, 0x3c, 0xe4, 0x59,
+                      0x64, 0xff, 0x21, 0x67,
+                      0xf6, 0xec, 0xed, 0xd4,
+                      0x19, 0xdb, 0x06, 0xc1 };
+
+  uint8 output2[base::SHA256_LENGTH];
+  base::SHA256HashString(input2, output2, sizeof(output2));
+  for (int i = 0; i < base::SHA256_LENGTH; i++)
+    EXPECT_EQ(expected2[i], static_cast<int>(output2[i]));
+
+  uint8 output_truncated2[6];
+  base::SHA256HashString(input2, output_truncated2, sizeof(output_truncated2));
+  for (int i = 0; i < sizeof(output_truncated2); i++)
+    EXPECT_EQ(expected2[i], static_cast<int>(output_truncated2[i]));
+}
+
+TEST(Sha256Test, Test3) {
+  // Example B.3 from FIPS 180-2: long message.
+  std::string input3(1000000, 'a');  // 'a' repeated a million times
+  int expected3[] = { 0xcd, 0xc7, 0x6e, 0x5c,
+                      0x99, 0x14, 0xfb, 0x92,
+                      0x81, 0xa1, 0xc7, 0xe2,
+                      0x84, 0xd7, 0x3e, 0x67,
+                      0xf1, 0x80, 0x9a, 0x48,
+                      0xa4, 0x97, 0x20, 0x0e,
+                      0x04, 0x6d, 0x39, 0xcc,
+                      0xc7, 0x11, 0x2c, 0xd0 };
+
+  uint8 output3[base::SHA256_LENGTH];
+  base::SHA256HashString(input3, output3, sizeof(output3));
+  for (int i = 0; i < base::SHA256_LENGTH; i++)
+    EXPECT_EQ(expected3[i], static_cast<int>(output3[i]));
+
+  uint8 output_truncated3[12];
+  base::SHA256HashString(input3, output_truncated3, sizeof(output_truncated3));
+  for (int i = 0; i < sizeof(output_truncated3); i++)
+    EXPECT_EQ(expected3[i], static_cast<int>(output_truncated3[i]));
+}
diff --git a/base/shared_event.cc b/base/shared_event.cc
new file mode 100644
index 0000000..e0870cf
--- /dev/null
+++ b/base/shared_event.cc
@@ -0,0 +1,104 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/shared_event.h"
+#include "base/logging.h"
+#include "base/time.h"
+
+SharedEvent::~SharedEvent() {
+  Close();
+}
+
+bool SharedEvent::Create(bool manual_reset, bool initial_state) {
+  DCHECK(!event_handle_);
+  event_handle_ = CreateEvent(NULL /* security attributes */, manual_reset,
+                              initial_state, NULL /* name */);
+  DCHECK(event_handle_);
+  return !!event_handle_;
+}
+
+void SharedEvent::Close() {
+  if (event_handle_) {
+    BOOL rv = CloseHandle(event_handle_);
+    DCHECK(rv);
+    event_handle_ = NULL;
+  }
+}
+
+bool SharedEvent::SetSignaledState(bool signaled) {
+  DCHECK(event_handle_);
+  BOOL rv;
+  if (signaled) {
+    rv = SetEvent(event_handle_);
+  } else {
+    rv = ResetEvent(event_handle_);
+  }
+  return rv ? true : false;
+}
+
+bool SharedEvent::IsSignaled() {
+  DCHECK(event_handle_);
+  DWORD event_state = ::WaitForSingleObject(event_handle_, 0);
+  DCHECK(WAIT_OBJECT_0 == event_state || WAIT_TIMEOUT == event_state);
+  return event_state == WAIT_OBJECT_0;
+}
+
+bool SharedEvent::WaitUntilSignaled(const TimeDelta& timeout) {
+  DCHECK(event_handle_);
+  DWORD event_state = ::WaitForSingleObject(event_handle_,
+      static_cast<DWORD>(timeout.InMillisecondsF()));
+  return event_state == WAIT_OBJECT_0;
+}
+
+bool SharedEvent::WaitForeverUntilSignaled() {
+  DCHECK(event_handle_);
+  DWORD event_state = ::WaitForSingleObject(event_handle_,
+                                            INFINITE);
+  return event_state == WAIT_OBJECT_0;
+}
+
+bool SharedEvent::ShareToProcess(ProcessHandle process,
+                                 SharedEventHandle *new_handle) {
+  DCHECK(event_handle_);
+  HANDLE event_handle_copy;
+  BOOL rv = DuplicateHandle(GetCurrentProcess(), event_handle_, process,
+      &event_handle_copy, 0, FALSE, DUPLICATE_SAME_ACCESS);
+
+  if (rv)
+    *new_handle = event_handle_copy;
+  return rv ? true : false;
+}
+
+bool SharedEvent::GiveToProcess(ProcessHandle process,
+                                SharedEventHandle *new_handle) {
+  bool rv = ShareToProcess(process, new_handle);
+  if (rv)
+    Close();
+  return rv;
+}
diff --git a/base/shared_event.h b/base/shared_event.h
new file mode 100644
index 0000000..2ddebc0
--- /dev/null
+++ b/base/shared_event.h
@@ -0,0 +1,87 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_SHARED_EVENT__
+#define BASE_SHARED_EVENT__
+
+#include "base/process_util.h"
+
+class TimeDelta;
+
+typedef HANDLE SharedEventHandle;
+
+class SharedEvent {
+ public:
+  // Create a new SharedEvent.
+  SharedEvent() : event_handle_(NULL) { }
+
+  // Create a SharedEvent from an existing SharedEventHandle.  The new
+  // SharedEvent now owns the SharedEventHandle and will close it.
+  SharedEvent(SharedEventHandle event_handle) : event_handle_(event_handle) { }
+  ~SharedEvent();
+
+  // Create the SharedEvent.
+  bool Create(bool manual_reset, bool initial_state);
+
+  // Close the SharedEvent.
+  void Close();
+
+  // If |signaled| is true, set the signaled state, otherwise, set to nonsignaled.
+  // Returns false if we can't set the signaled state.
+  bool SetSignaledState(bool signaled);
+
+  // Returns true if the SharedEvent is signaled.
+  bool IsSignaled();
+
+  // Blocks until the event is signaled with a maximum wait time of |timeout|.
+  // Returns true if the object is signaled within the timeout.
+  bool WaitUntilSignaled(const TimeDelta& timeout);
+
+  // Blocks until the event is signaled.  Returns true if the object is
+  // signaled, otherwise an error occurred.
+  bool WaitForeverUntilSignaled();
+
+  // Get access to the underlying OS handle for this event.
+  SharedEventHandle handle() { return event_handle_; }
+
+  // Share this SharedEvent with |process|.  |new_handle| is an output
+  // parameter to receive the handle for use in |process|.  Returns false if we
+  // are unable to share the SharedEvent.
+  bool ShareToProcess(ProcessHandle process, SharedEventHandle *new_handle);
+
+  // The same as ShareToProcess followed by closing the event.
+  bool GiveToProcess(ProcessHandle process, SharedEventHandle *new_handle);
+
+ private:
+  SharedEventHandle event_handle_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(SharedEvent);
+};
+
+#endif  // BASE_SHARED_EVENT__
diff --git a/base/shared_event_unittest.cc b/base/shared_event_unittest.cc
new file mode 100644
index 0000000..5a0c468
--- /dev/null
+++ b/base/shared_event_unittest.cc
@@ -0,0 +1,81 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <process.h>  // _beginthreadex
+
+#include "base/lock.h"
+#include "base/scoped_ptr.h"
+#include "base/shared_event.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class SharedEventTest : public testing::Test {
+};
+
+Lock lock;
+
+unsigned __stdcall MultipleThreadMain(void* param) {
+  SharedEvent* shared_event = reinterpret_cast<SharedEvent*>(param);
+  AutoLock l(lock);
+  shared_event->SetSignaledState(!shared_event->IsSignaled());
+  return 0;
+}
+
+}
+
+TEST(SharedEventTest, ThreadSignaling) {
+  // Create a set of 5 threads to each open a shared event and flip the
+  // signaled state.  Verify that when the threads complete, the final state is
+  // not-signaled.
+  // I admit this doesn't test much, but short of spawning separate processes
+  // and using IPC with a SharedEventHandle, there's not much to unittest.
+  const int kNumThreads = 5;
+  HANDLE threads[kNumThreads];
+
+  scoped_ptr<SharedEvent> shared_event(new SharedEvent);
+  shared_event->Create(true, true);
+
+  // Spawn the threads.
+  for (int16 index = 0; index < kNumThreads; index++) {
+    void *argument = reinterpret_cast<void*>(shared_event.get());
+    unsigned thread_id;
+    threads[index] = reinterpret_cast<HANDLE>(
+      _beginthreadex(NULL, 0, MultipleThreadMain, argument, 0, &thread_id));
+    EXPECT_NE(threads[index], static_cast<HANDLE>(NULL));
+  }
+
+  // Wait for the threads to finish.
+  for (int index = 0; index < kNumThreads; index++) {
+    DWORD rv = WaitForSingleObject(threads[index], 60*1000);
+    EXPECT_EQ(rv, WAIT_OBJECT_0);  // verify all threads finished
+    CloseHandle(threads[index]);
+  }
+  EXPECT_FALSE(shared_event->IsSignaled());
+}
diff --git a/base/shared_memory.cc b/base/shared_memory.cc
new file mode 100644
index 0000000..bd5023f
--- /dev/null
+++ b/base/shared_memory.cc
@@ -0,0 +1,187 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/shared_memory.h"
+
+#include "base/logging.h"
+#include "base/win_util.h"
+
+SharedMemory::SharedMemory()
+    : mapped_file_(NULL),
+      memory_(NULL),
+      read_only_(false),
+      max_size_(0),
+      lock_(NULL) {
+}
+
+SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only)
+    : mapped_file_(handle),
+      memory_(NULL),
+      read_only_(read_only),
+      max_size_(0),
+      lock_(NULL) {
+}
+
+SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only,
+                           ProcessHandle process)
+    : mapped_file_(NULL),
+      memory_(NULL),
+      read_only_(read_only),
+      max_size_(0),
+      lock_(NULL) {
+  ::DuplicateHandle(process, handle,
+                    GetCurrentProcess(), &mapped_file_,
+                    STANDARD_RIGHTS_REQUIRED |
+                    (read_only_ ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS),
+                    FALSE, 0);
+}
+
+SharedMemory::~SharedMemory() {
+  Close();
+  if (lock_ != NULL)
+    CloseHandle(lock_);
+}
+
+bool SharedMemory::Create(const std::wstring &name, bool read_only,
+                          bool open_existing, size_t size) {
+  DCHECK(mapped_file_ == NULL);
+
+  name_ = name;
+  read_only_ = read_only;
+  mapped_file_ = CreateFileMapping(INVALID_HANDLE_VALUE, NULL,
+      read_only_ ? PAGE_READONLY : PAGE_READWRITE, 0, static_cast<DWORD>(size),
+      name.empty() ? NULL : name.c_str());
+  if (!mapped_file_)
+    return false;
+
+  // Check if the shared memory pre-exists.
+  if (GetLastError() == ERROR_ALREADY_EXISTS && !open_existing) {
+    Close();
+    return false;
+  }
+  max_size_ = size;
+  return true;
+}
+
+bool SharedMemory::Open(const std::wstring &name, bool read_only) {
+  DCHECK(mapped_file_ == NULL);
+
+  name_ = name;
+  read_only_ = read_only;
+  mapped_file_ = OpenFileMapping(
+      read_only_ ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS, false,
+      name.empty() ? NULL : name.c_str());
+  if (mapped_file_ != NULL) {
+    // Note: size_ is not set in this case.
+    return true;
+  }
+  return false;
+}
+
+bool SharedMemory::Map(size_t bytes) {
+  if (mapped_file_ == NULL)
+    return false;
+
+  memory_ = MapViewOfFile(mapped_file_,
+      read_only_ ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS, 0, 0, bytes);
+  if (memory_ != NULL) {
+    return true;
+  }
+  return false;
+}
+
+bool SharedMemory::Unmap() {
+  if (memory_ == NULL)
+    return false;
+
+  UnmapViewOfFile(memory_);
+  memory_ = NULL;
+  return true;
+}
+
+bool SharedMemory::ShareToProcessCommon(ProcessHandle process,
+                                        SharedMemoryHandle *new_handle,
+                                        bool close_self) {
+  *new_handle = 0;
+  DWORD access = STANDARD_RIGHTS_REQUIRED | FILE_MAP_READ;
+  DWORD options = 0;
+  HANDLE mapped_file = mapped_file_;
+  HANDLE result;
+  if (!read_only_)
+    access |= FILE_MAP_WRITE;
+  if (close_self) {
+    // DUPLICATE_CLOSE_SOURCE causes DuplicateHandle to close mapped_file.
+    options = DUPLICATE_CLOSE_SOURCE;
+    mapped_file_ = NULL;
+    Unmap();
+  }
+
+  if (process == GetCurrentProcess() && close_self) {
+    *new_handle = mapped_file;
+    return true;
+  }
+
+  if (!DuplicateHandle(GetCurrentProcess(), mapped_file, process,
+      &result, access, FALSE, options))
+    return false;
+  *new_handle = result;
+  return true;
+}
+
+
+void SharedMemory::Close() {
+  if (memory_ != NULL) {
+    UnmapViewOfFile(memory_);
+    memory_ = NULL;
+  }
+
+  if (mapped_file_ != NULL) {
+    CloseHandle(mapped_file_);
+    mapped_file_ = NULL;
+  }
+}
+
+void SharedMemory::Lock() {
+  if (lock_ == NULL) {
+    std::wstring name = name_;
+    name.append(L"lock");
+    lock_ = CreateMutex(NULL, FALSE, name.c_str());
+    DCHECK(lock_ != NULL);
+    if (lock_ == NULL) {
+      DLOG(ERROR) << "Could not create mutex" << GetLastError();
+      return;  // there is nothing good we can do here.
+    }
+  }
+  WaitForSingleObject(lock_, INFINITE);
+}
+
+void SharedMemory::Unlock() {
+  DCHECK(lock_ != NULL);
+  ReleaseMutex(lock_);
+}
diff --git a/base/shared_memory.h b/base/shared_memory.h
new file mode 100644
index 0000000..00b51f9
--- /dev/null
+++ b/base/shared_memory.h
@@ -0,0 +1,169 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_SHARED_MEMORY_H__
+#define BASE_SHARED_MEMORY_H__
+
+#include "base/process_util.h"
+
+// SharedMemoryHandle is a platform specific type which represents
+// the underlying OS handle to a shared memory segment.
+#ifdef WIN32
+typedef HANDLE SharedMemoryHandle;
+typedef HANDLE SharedMemoryLock;
+#else
+typedef int SharedMemoryHandle;
+typedef int SharedMemoryLock;
+#endif
+
+// Platform abstraction for shared memory.  Provides a C++ wrapper
+// around the OS primitive for a memory mapped file.
+class SharedMemory {
+ public:
+  // Create a new SharedMemory object.
+  SharedMemory();
+
+  // Create a new SharedMemory object from an existing, open
+  // shared memory file.
+  SharedMemory(SharedMemoryHandle handle, bool read_only);
+
+  // Create a new SharedMemory object from an existing, open
+  // shared memory file that was created by a remote process and not shared
+  // to the current process.
+  SharedMemory(SharedMemoryHandle handle, bool read_only,
+      ProcessHandle process);
+
+  // Destructor.  Will close any open files.
+  ~SharedMemory();
+
+  // Creates or opens a shared memory segment based on a name.
+  // If read_only is true, opens the memory as read-only.
+  // If open_existing is true, and the shared memory already exists,
+  // opens the existing shared memory and ignores the size parameter.
+  // Returns true on success, false on failure.
+  bool Create(const std::wstring &name, bool read_only, bool open_existing,
+      size_t size);
+
+  // Opens a shared memory segment based on a name.
+  // If read_only is true, opens for read-only access.
+  // Returns true on success, false on failure.
+  bool Open(const std::wstring &name, bool read_only);
+
+  // Maps the shared memory into the caller's address space.
+  // Returns true on success, false otherwise.  The memory address
+  // is accessed via the memory() accessor.
+  bool Map(size_t bytes);
+
+  // Unmaps the shared memory from the caller's address space.
+  // Returns true if successful; returns false on error or if the
+  // memory is not mapped.
+  bool Unmap();
+
+  // Get the size of the opened shared memory backing file.
+  // Note:  This size is only available to the creator of the
+  // shared memory, and not to those that opened shared memory
+  // created externally.
+  // Returns 0 if not opened or unknown.
+  size_t max_size() const { return max_size_; }
+
+  // Gets a pointer to the opened memory space if it has been
+  // Mapped via Map().  Returns NULL if it is not mapped.
+  void *memory() const { return memory_; }
+
+  // Get access to the underlying OS handle for this segment.
+  // Use of this handle for anything other than an opaque
+  // identifier is not portable.
+  SharedMemoryHandle handle() const { return mapped_file_; }
+
+  // Closes the open shared memory segment.
+  // It is safe to call Close repeatedly.
+  void Close();
+
+  // Share the shared memory to another process.  Attempts
+  // to create a platform-specific new_handle which can be
+  // used in a remote process to access the shared memory
+  // file.  new_handle is an ouput parameter to receive
+  // the handle for use in the remote process.
+  // Returns true on success, false otherwise.
+  bool ShareToProcess(ProcessHandle process,
+      SharedMemoryHandle *new_handle) {
+    return ShareToProcessCommon(process, new_handle, false);
+  }
+
+  // Logically equivalent to:
+  //   bool ok = ShareToProcess(process, new_handle);
+  //   Close();
+  //   return ok;
+  bool GiveToProcess(ProcessHandle process,
+      SharedMemoryHandle *new_handle) {
+    return ShareToProcessCommon(process, new_handle, true);
+  }
+
+  // Lock the shared memory.
+  // This is a cross-process lock which may be recursively
+  // locked by the same thread.
+  void Lock();
+
+  // Release the shared memory lock.
+  void Unlock();
+
+ private:
+  bool ShareToProcessCommon(ProcessHandle process,
+      SharedMemoryHandle *new_handle, bool close_self);
+
+  std::wstring       name_;
+  SharedMemoryHandle mapped_file_;
+  void*              memory_;
+  bool               read_only_;
+  size_t             max_size_;
+  SharedMemoryLock   lock_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(SharedMemory);
+};
+
+// A helper class that acquires the shared memory lock while
+// the SharedMemoryAutoLock is in scope.
+class SharedMemoryAutoLock {
+ public:
+  explicit SharedMemoryAutoLock(SharedMemory* shared_memory)
+      : shared_memory_(shared_memory) {
+    shared_memory_->Lock();
+  }
+
+  ~SharedMemoryAutoLock() {
+    shared_memory_->Unlock();
+  }
+
+ private:
+  SharedMemory* shared_memory_;
+  DISALLOW_EVIL_CONSTRUCTORS(SharedMemoryAutoLock);
+};
+
+
+#endif  // BASE_SHARED_MEMORY_H__
diff --git a/base/shared_memory_unittest.cc b/base/shared_memory_unittest.cc
new file mode 100644
index 0000000..928326a
--- /dev/null
+++ b/base/shared_memory_unittest.cc
@@ -0,0 +1,180 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <process.h>  // _beginthreadex
+#include "base/shared_memory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+
+namespace {
+
+class SharedMemoryTest : public testing::Test {
+};
+
+unsigned __stdcall MultipleThreadMain(void* param) {
+  // Each thread will open the shared memory.  Each thread will take
+  // a different 4 byte int pointer, and keep changing it, with some
+  // small pauses in between.  Verify that each thread's value in the
+  // shared memory is always correct.
+  const int kDataSize = 1024;
+  std::wstring test_name = L"SharedMemoryOpenThreadTest";
+  int16 id = reinterpret_cast<int16>(param);
+  SharedMemory memory;
+  bool rv = memory.Create(test_name, false, true, kDataSize);
+  EXPECT_TRUE(rv);
+  rv = memory.Map(kDataSize);
+  EXPECT_TRUE(rv);
+  int *ptr = static_cast<int*>(memory.memory()) + id;
+  EXPECT_EQ(*ptr, 0);
+  for (int idx = 0; idx < 100; idx++) {
+    *ptr = idx;
+    Sleep(1);  // short wait
+    EXPECT_EQ(*ptr, idx);
+  }
+  memory.Close();
+  return 0;
+}
+
+unsigned __stdcall MultipleLockThread(void* param) {
+  // Each thread will open the shared memory.  Each thread will take
+  // the memory, and keep changing it while trying to lock it, with some
+  // small pauses in between.  Verify that each thread's value in the
+  // shared memory is always correct.
+  const int kDataSize = sizeof(int);
+  int id = static_cast<int>(reinterpret_cast<INT_PTR>(param));
+  SharedMemoryHandle handle = NULL;
+  {
+    SharedMemory memory1;
+    EXPECT_TRUE(memory1.Create(L"SharedMemoryMultipleLockThreadTest", false, true,
+                              kDataSize));
+    EXPECT_TRUE(memory1.ShareToProcess(GetCurrentProcess(), &handle));
+  }
+  SharedMemory memory2(handle, false);
+  EXPECT_TRUE(memory2.Map(kDataSize));
+  volatile int* const ptr = static_cast<int*>(memory2.memory());
+  for (int idx = 0; idx < 20; idx++) {
+    memory2.Lock();
+    int i = (id << 16) + idx;
+    *ptr = i;
+    // short wait
+    Sleep(1);
+    EXPECT_EQ(*ptr, i);
+    memory2.Unlock();
+  }
+  memory2.Close();
+  return 0;
+}
+
+}  // namespace
+
+TEST(SharedMemoryTest, OpenClose) {
+  const int kDataSize = 1024;
+  std::wstring test_name = L"SharedMemoryOpenCloseTest";
+
+  // Open two handles to a memory segment, confirm that they
+  // are mapped separately yet point to the same space.
+  SharedMemory memory1;
+  bool rv = memory1.Open(test_name, false);
+  EXPECT_FALSE(rv);
+  rv = memory1.Create(test_name, false, false, kDataSize);
+  EXPECT_TRUE(rv);
+  rv = memory1.Map(kDataSize);
+  EXPECT_TRUE(rv);
+  SharedMemory memory2;
+  rv = memory2.Open(test_name, false);
+  EXPECT_TRUE(rv);
+  rv = memory2.Map(kDataSize);
+  EXPECT_TRUE(rv);
+  EXPECT_NE(memory1.memory(), memory2.memory());  // compare the pointers
+
+
+  // Write data to the first memory segment, verify contents of second.
+  memset(memory1.memory(), '1', kDataSize);
+  EXPECT_EQ(memcmp(memory1.memory(), memory2.memory(), kDataSize), 0);
+
+  // Close the first memory segment, and verify the
+  // second still has the right data.
+  memory1.Close();
+  char *start_ptr = static_cast<char *>(memory2.memory());
+  char *end_ptr = start_ptr + kDataSize;
+  for (char* ptr = start_ptr; ptr < end_ptr; ptr++)
+    EXPECT_EQ(*ptr, '1');
+
+  // Close the second memory segment
+  memory2.Close();
+}
+
+
+TEST(SharedMemoryTest, MultipleThreads) {
+  // Create a set of 5 threads to each open a shared memory segment
+  // and write to it.  Verify that they are always reading/writing
+  // consistent data.
+  const int kNumThreads = 5;
+  HANDLE threads[kNumThreads];
+
+  // Spawn the threads.
+  for (int16 index = 0; index < kNumThreads; index++) {
+    void *argument = reinterpret_cast<void*>(index);
+    unsigned thread_id;
+    threads[index] = reinterpret_cast<HANDLE>(
+      _beginthreadex(NULL, 0, MultipleThreadMain, argument, 0, &thread_id));
+    EXPECT_NE(threads[index], static_cast<HANDLE>(NULL));
+  }
+
+  // Wait for the threads to finish.
+  for (int index = 0; index < kNumThreads; index++) {
+    DWORD rv = WaitForSingleObject(threads[index], 60*1000);
+    EXPECT_EQ(rv, WAIT_OBJECT_0);  // verify all threads finished
+    CloseHandle(threads[index]);
+  }
+}
+
+
+TEST(SharedMemoryTest, Lock) {
+  // Create a set of threads to each open a shared memory segment and write to
+  // it with the lock held. Verify that they are always reading/writing
+  // consistent data.
+  const int kNumThreads = 5;
+  HANDLE threads[kNumThreads];
+
+  // Spawn the threads.
+  for (int index = 0; index < kNumThreads; ++index) {
+    void *argument = reinterpret_cast<void*>(static_cast<INT_PTR>(index));
+    threads[index] = reinterpret_cast<HANDLE>(
+      _beginthreadex(NULL, 0, &MultipleLockThread, argument, 0, NULL));
+    EXPECT_NE(threads[index], static_cast<HANDLE>(NULL));
+  }
+
+  // Wait for the threads to finish.
+  for (int index = 0; index < kNumThreads; ++index) {
+    DWORD rv = WaitForSingleObject(threads[index], 60*1000);
+    EXPECT_EQ(rv, WAIT_OBJECT_0);  // verify all threads finished
+    CloseHandle(threads[index]);
+  }
+}
diff --git a/base/singleton.h b/base/singleton.h
new file mode 100644
index 0000000..47c0c1d
--- /dev/null
+++ b/base/singleton.h
@@ -0,0 +1,293 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_SINGLETON_H__
+#define BASE_SINGLETON_H__
+
+#include <stdlib.h>
+
+#include <utility>
+
+#include "base/lock.h"
+#include "base/singleton_internal.h"
+
+#ifdef WIN32
+#include "base/fix_wp64.h"
+#else  // WIN32
+#include <pthread.h>
+#endif  // WIN32
+
+// Default traits for Singleton<Type>. Calls operator new and operator delete on
+// the object. Registers automatic deletion at library unload or process exit.
+// Overload if you need arguments or another memory allocation function.
+template<typename Type>
+struct DefaultSingletonTraits {
+  // Allocates the object.
+  static Type* New() {
+    // The parenthesis is very important here; it forces POD type
+    // initialization.
+    return new Type();
+  }
+
+  // Destroys the object.
+  static void Delete(Type* x) {
+    delete x;
+  }
+
+  // Set to true to automatically register deletion of the object on library
+  // unload or process exit.
+  static const bool kRegisterAtExit = true;
+
+  // Note: Only apply on Windows. Has *no effect* on other platform.
+  // When set to true, it signals that Trait::New() *must* not be called
+  // multiple times at construction. Anything that must be done to not enter
+  // this situation should be done at all cost. This simply involves creating a
+  // temporary lock.
+  static const bool kMustCallNewExactlyOnce = false;
+};
+
+
+// The Singleton<Type, Traits, DifferentiatingType> class manages a single
+// instance of Type which will be created on first use and will be destroyed at
+// library unload (or on normal process exit). The Trait::Delete function will
+// not be called on abnormal process exit.
+//
+// DifferentiatingType is used as a key to differentiate two different
+// singletons having the same memory allocation functions but serving a
+// different purpose. This is mainly used for Locks serving different purposes.
+//
+// Example usages: (none are preferred, they all result in the same code)
+//   1. FooClass* ptr = Singleton<FooClass>::get();
+//      ptr->Bar();
+//   2. Singleton<FooClass>()->Bar();
+//   3. Singleton<FooClass>::get()->Bar();
+//
+// Singleton<> has no non-static members and doesn't need to actually be
+// instantiated. It does no harm to instantiate it and use it as a class member
+// or at global level since it is acting as a POD type.
+//
+// This class is itself thread-safe. The underlying Type must of course be
+// thread-safe if you want to use it concurrently. Two parameters may be tuned
+// depending on the user's requirements.
+//
+// Glossary:
+//   MCNEO = kMustCallNewExactlyOnce
+//   RAE = kRegisterAtExit
+//
+// On every platform, if Traits::RAE is true, the singleton will be destroyed at
+// library unload or process exit. if Traits::RAE is false, the singleton will
+// not be freed at library unload or process exit, thus the singleton will be
+// leaked if it is ever accessed. Traits::RAE shouldn't be false unless
+// absolutely necessary. Remember that the heap where the object is allocated
+// may be destroyed by the CRT anyway.
+//
+// On Windows, now the fun begins. Traits::New() may be called more than once
+// concurrently, but no user will gain access to the object until the winning
+// Traits::New() call is completed.
+//
+// On Windows, if Traits::MCNEO and Traits::RAE are both false,
+// Traits::Delete() can still be called. The reason is that a race condition can
+// occur during the object creation which will cause Traits::Delete() to be
+// called even if Traits::RAE is false, so Traits::Delete() should still be
+// implemented or objects may be leaked when there is a race condition in
+// creating the singleton. Even though this case is very rare, it may happen in
+// practice. To work around this situation, before creating a multithreaded
+// environment, be sure to call Singleton<>::get() to force the creation of the
+// instance.
+//
+// On Windows, If Traits::MCNEO is true, a temporary lock per singleton will be
+// created to ensure that Trait::New() is only called once.
+//
+// If you want to ensure that your class can only exist as a singleton, make
+// its constructors private, and make DefaultSingletonTraits<> a friend:
+//
+//   #include "base/singleton.h"
+//   class FooClass {
+//    public:
+//     void Bar() { ... }
+//    private:
+//     FooClass() { ... }
+//     friend DefaultSingletonTraits<FooClass>;
+//
+//     DISALLOW_EVIL_CONSTRUCTORS(FooClass);
+//   };
+//
+// Caveats:
+// (a) Every call to get(), operator->() and operator*() incurs some overhead
+//     (16ns on my P4/2.8GHz) to check whether the object has already been
+//     initialized.  You may wish to cache the result of get(); it will not
+//     change.
+//
+// (b) Your factory function must never throw an exception. This class is not
+//     exception-safe.
+//
+// (c) On Windows at least, if Traits::kMustCallNewExactlyOnce is false,
+//     Traits::New() may be called two times in two different threads at the
+//     same time so it must not have side effects. Set
+//     Traits::kMustCallNewExactlyOnce to true to alleviate this issue, at
+//     the cost of a slight increase of memory use and creation time.
+//
+template <typename Type,
+          typename Traits = DefaultSingletonTraits<Type>,
+          typename DifferentiatingType = Type>
+class Singleton
+    : public SingletonStorage<
+          Type,
+          std::pair<Traits, DifferentiatingType>,
+          UseVolatileSingleton<Traits::kMustCallNewExactlyOnce>::value> {
+ public:
+  // This class is safe to be constructed and copy-constructed since it has no
+  // member.
+
+  // Return a pointer to the one true instance of the class.
+  static Type* get() {
+    Type* value = instance_;
+    // Acute readers may think: why not just discard "value" and use
+    // "instance_" directly? Astute readers will remark that instance_ can be a
+    // volatile pointer on Windows and hence the compiler would be forced to
+    // generate two memory reads instead of just one. Since this is the hotspot,
+    // this is inefficient.
+    if (value)
+      return value;
+
+#ifdef WIN32
+    // Statically determine which function to call.
+    LockedConstruct<Traits::kMustCallNewExactlyOnce>();
+#else  // WIN32
+    // Posix platforms already have the functionality embedded.
+    pthread_once(&control_, SafeConstruct);
+#endif  // WIN32
+    return instance_;
+  }
+
+  // Shortcuts.
+  Type& operator*() {
+    return *get();
+  }
+
+  Type* operator->() {
+    return get();
+  }
+
+ private:
+#ifdef WIN32
+  // Use bool template differentiation to make sure to not build the other part
+  // of the code. We don't want to instantiate Singleton<Lock, ...> uselessly.
+  template<bool kUseLock>
+  static void LockedConstruct() {
+    // Define a differentiating type for the Lock.
+    typedef std::pair<Type, std::pair<Traits, DifferentiatingType> >
+        LockDifferentiatingType;
+
+    // Object-type lock. Note that the lock singleton is different per singleton
+    // type.
+    AutoLock lock(*Singleton<Lock,
+                             DefaultSingletonTraits<Lock>,
+                             LockDifferentiatingType>());
+    // Now that we have the lock, look if the instance is created, if not yet,
+    // create it.
+    if (!instance_)
+      SafeConstruct();
+  }
+
+  template<>
+  static void LockedConstruct<false>() {
+    // Implemented using atomic compare-and-swap. The new object is
+    // constructed and used as the new value in the operation; if the
+    // compare fails, the new object will be deleted. Future implementations
+    // for Windows might use InitOnceExecuteOnce (Vista-only), similar in
+    // spirit to pthread_once.
+
+    // On Windows, multiple concurrent Traits::New() calls are tolerated.
+    Type* value = Traits::New();
+    if (InterlockedCompareExchangePointer(
+            reinterpret_cast<void* volatile*>(&instance_), value, NULL)) {
+      // Race condition, discard the temporary value.
+      Traits::Delete(value);
+    } else {
+      // Got it, register destruction at unload. atexit() is called on library
+      // unload. It is assumed that atexit() is itself thread safe. It is also
+      // assumed that registered functions by atexit are called in a thread
+      // safe manner. At least on Windows, they are called with the loader
+      // lock held. On Windows, the CRT use a structure similar to
+      // std::map<dll_handle,std::vector<registered_functions>> so the right
+      // functions are called on library unload, independent of having a DLL
+      // CRT or a static CRT or even both.
+      if (Traits::kRegisterAtExit)
+        atexit(&OnExit);
+    }
+  }
+#endif  // WIN32
+
+  // SafeConstruct is guaranteed to be executed only once.
+  static void SafeConstruct() {
+    instance_ = Traits::New();
+
+    // Porting note: this code depends on some properties of atexit which are
+    // not guaranteed by the standard:
+    //  - atexit must be thread-safe: its internal manipulation of the list of
+    //    registered functions must be tolerant of multiple threads attempting
+    //    to register exit routines simultaneously.
+    //  - exit routines must run when the executable module that contains them
+    //    is unloaded.  For routines in by dynamically-loaded modules, this
+    //    may be sooner than process termination.
+    //  - atexit should support an arbitrary number of registered exit
+    //    routines, or at least should support more routines than will
+    //    actually be registered (the standard only requires 32).
+    // The atexit implementations in contemporary versions of Mac OS X, glibc,
+    // and the Windows C runtime provide these capabilities.  To port to other
+    // systems with less-advanced (even though still standard-conforming)
+    // atexit implmentations, consider alternatives such as __cxa_atexit or
+    // custom termination sections.
+    if (Traits::kRegisterAtExit)
+      atexit(OnExit);
+  }
+
+  // Adapter function for use with atexit().
+  static void OnExit() {
+    if (!instance_)
+      return;
+    Traits::Delete(instance_);
+    instance_ = NULL;
+  }
+
+#ifndef WIN32
+  static pthread_once_t control_;
+#endif  // !WIN32
+};
+
+#ifndef WIN32
+
+template <typename Type, typename Traits, typename DifferentiatingType>
+pthread_once_t Singleton<Type, Traits, DifferentiatingType>::control_ =
+    PTHREAD_ONCE_INIT;
+
+#endif  // !WIN32
+
+#endif  // BASE_SINGLETON_H__
diff --git a/base/singleton_dll_unittest.cc b/base/singleton_dll_unittest.cc
new file mode 100644
index 0000000..c663db3
--- /dev/null
+++ b/base/singleton_dll_unittest.cc
@@ -0,0 +1,114 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/singleton_dll_unittest.h"
+#include "base/logging.h"
+
+BOOL APIENTRY DllMain(HMODULE module, DWORD reason_for_call, LPVOID reserved) {
+  switch (reason_for_call) {
+    case DLL_PROCESS_ATTACH:
+      DisableThreadLibraryCalls(module);
+      break;
+    case DLL_THREAD_ATTACH:
+    case DLL_THREAD_DETACH:
+    case DLL_PROCESS_DETACH:
+      break;
+  }
+  return TRUE;
+}
+
+COMPILE_ASSERT(DefaultSingletonTraits<int>::kRegisterAtExit == true, a);
+COMPILE_ASSERT(
+    DefaultSingletonTraits<int>::kMustCallNewExactlyOnce == false,
+    b);
+
+template<typename Type>
+struct LockTrait : public DefaultSingletonTraits<Type> {
+  static const bool kMustCallNewExactlyOnce = true;
+};
+
+struct Init5Trait : public DefaultSingletonTraits<int> {
+  static int* New() {
+    return new int(5);
+  }
+};
+
+struct CallbackTrait : public CustomAllocTrait<CallBackFunc> {
+  static void Delete(CallBackFunc* p) {
+    if (*p)
+      (*p)();
+    CHECK(CustomAllocTrait<CallBackFunc>::Delete(p));
+  }
+};
+
+struct NoLeakTrait : public CallbackTrait {
+};
+
+struct LeakTrait : public CallbackTrait {
+  static const bool kRegisterAtExit = false;
+};
+
+SINGLETON_UNITTEST_API int* WINAPI SingletonInt1() {
+  return Singleton<int>::get();
+}
+
+SINGLETON_UNITTEST_API int* WINAPI SingletonInt2() {
+  // Force to use a different singleton than SingletonInt1.
+  return Singleton<int, DefaultSingletonTraits<int> >::get();
+}
+
+class DummyDifferentiatingClass {
+};
+
+SINGLETON_UNITTEST_API int* WINAPI SingletonInt3() {
+  // Force to use a different singleton than SingletonInt1 and SingletonInt2.
+  // Note that any type can be used; int, float, std::wstring...
+  return Singleton<int, DefaultSingletonTraits<int>,
+                   DummyDifferentiatingClass>::get();
+}
+
+SINGLETON_UNITTEST_API int* WINAPI SingletonInt4() {
+  return Singleton<int, LockTrait<int> >::get();
+}
+
+SINGLETON_UNITTEST_API int* WINAPI SingletonInt5() {
+  return Singleton<int, Init5Trait>::get();
+}
+
+SINGLETON_UNITTEST_API void WINAPI SingletonNoLeak(CallBackFunc CallOnQuit) {
+  *Singleton<CallBackFunc, NoLeakTrait>::get() = CallOnQuit;
+}
+
+SINGLETON_UNITTEST_API void WINAPI SingletonLeak(CallBackFunc CallOnQuit) {
+  *Singleton<CallBackFunc, LeakTrait>::get() = CallOnQuit;
+}
+
+SINGLETON_UNITTEST_API CallBackFunc* WINAPI GetLeakySingleton() {
+  return Singleton<CallBackFunc, LeakTrait>::get();
+}
diff --git a/base/singleton_dll_unittest.h b/base/singleton_dll_unittest.h
new file mode 100644
index 0000000..d161722
--- /dev/null
+++ b/base/singleton_dll_unittest.h
@@ -0,0 +1,83 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_SINGLETON_DLL_UNITTEST_H__
+#define BASE_SINGLETON_DLL_UNITTEST_H__
+
+#include "base/singleton.h"
+
+#ifdef SINGLETON_UNITTEST_EXPORTS
+#define SINGLETON_UNITTEST_API __declspec(dllexport)
+#else
+#define SINGLETON_UNITTEST_API __declspec(dllimport)
+#endif
+
+// Function pointer for singleton getters.
+typedef int* (WINAPI* SingletonIntFunc)();
+
+// Callback function to be called on library unload.
+typedef void (WINAPI* CallBackFunc)();
+
+// Leaky/nonleak singleton initialization.
+typedef void (WINAPI* LeakySingletonFunc)(CallBackFunc);
+
+// Retrieve the leaky singleton for later disposal.
+typedef CallBackFunc* (WINAPI* GetLeakySingletonFunc)();
+
+// When using new/delete, the heap is destroyed on library unload. So use
+// VirtualAlloc/VirtualFree to bypass this behavior.
+template<typename Type>
+struct CustomAllocTrait : public DefaultSingletonTraits<Type> {
+  static Type* New() {
+    return static_cast<Type*>(VirtualAlloc(NULL, sizeof(Type), MEM_COMMIT,
+                                           PAGE_READWRITE));
+  }
+
+  static bool Delete(Type* p) {
+    return 0!=VirtualFree(p, 0, MEM_RELEASE);
+  }
+};
+
+// 1 and 2 share the same instance.
+// 3 simply use a different key.
+// 4 sets kMustCallNewExactlyOnce to true.
+// 5 default initialize to 5.
+extern "C" SINGLETON_UNITTEST_API int* WINAPI SingletonInt1();
+extern "C" SINGLETON_UNITTEST_API int* WINAPI SingletonInt2();
+extern "C" SINGLETON_UNITTEST_API int* WINAPI SingletonInt3();
+extern "C" SINGLETON_UNITTEST_API int* WINAPI SingletonInt4();
+extern "C" SINGLETON_UNITTEST_API int* WINAPI SingletonInt5();
+
+extern "C" SINGLETON_UNITTEST_API void WINAPI SingletonNoLeak(
+    CallBackFunc CallOnQuit);
+extern "C" SINGLETON_UNITTEST_API void WINAPI SingletonLeak(
+    CallBackFunc CallOnQuit);
+extern "C" SINGLETON_UNITTEST_API CallBackFunc* WINAPI GetLeakySingleton();
+
+#endif  // BASE_SINGLETON_DLL_UNITTEST_H__
diff --git a/base/singleton_internal.h b/base/singleton_internal.h
new file mode 100644
index 0000000..82fce94
--- /dev/null
+++ b/base/singleton_internal.h
@@ -0,0 +1,66 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_SINGLETON_INTERNAL_H__
+#define BASE_SINGLETON_INTERNAL_H__
+
+// Define the storage of the singleton pointer.
+template <typename Type, typename DifferentiatingType, bool kVolatile>
+class SingletonStorage {
+ protected:
+  // The actual pointer. Note that it is a volatile pointer.
+  static Type* volatile instance_;
+};
+
+// Partial specialization.
+template <typename Type, typename DifferentiatingType>
+class SingletonStorage<Type, DifferentiatingType, false> {
+ protected:
+  // The pointer does not need to be volatile for use with pthread_once or
+  // locked initialization.
+  static Type* instance_;
+};
+
+template <typename Type, typename DifferentiatingType, bool kVolatile>
+Type* volatile SingletonStorage<Type, DifferentiatingType,
+                                kVolatile>::instance_ = NULL;
+
+template <typename Type, typename DifferentiatingType>
+Type* SingletonStorage<Type, DifferentiatingType, false>::instance_ = NULL;
+
+template<bool kUseVolatile>
+struct UseVolatileSingleton {
+#ifdef WIN32
+  static const bool value = kUseVolatile;
+#else
+  static const bool value = false;
+#endif  // WIN32
+};
+
+#endif  // BASE_SINGLETON_INTERNAL_H__
diff --git a/base/singleton_unittest.cc b/base/singleton_unittest.cc
new file mode 100644
index 0000000..5d21593
--- /dev/null
+++ b/base/singleton_unittest.cc
@@ -0,0 +1,227 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "base/singleton_dll_unittest.h"
+#include "base/file_util.h"
+#include "base/path_service.h"
+
+class SingletonTest : public testing::Test {
+ public:
+  SingletonTest() {
+  }
+
+  virtual void SetUp() {
+    module_ = NULL;
+    non_leak_called_ = false;
+    leaky_called_ = false;
+  }
+
+  virtual void TearDown() {
+    ASSERT_FALSE(module_);
+  }
+
+  static bool IsTestCaseDisabled() {
+    // Check if the dll exists beside the executable.
+    std::wstring path;
+    PathService::Get(base::DIR_EXE, &path);
+    file_util::AppendToPath(&path, kLibrary);
+    return !file_util::PathExists(path);
+  }
+
+ protected:
+  void LoadLibrary() {
+    ASSERT_FALSE(module_);
+    module_ = ::LoadLibrary(kLibrary);
+    ASSERT_TRUE(module_ != NULL);
+  }
+
+  void FreeLibrary() {
+    ASSERT_TRUE(module_ != NULL);
+    ASSERT_TRUE(::FreeLibrary(module_));
+    module_ = NULL;
+  }
+
+  template<typename T>
+  void GetProc(const char* function_name, T* function) {
+    ASSERT_TRUE(module_ != NULL);
+    *function = reinterpret_cast<T>(GetProcAddress(module_, function_name));
+    ASSERT_TRUE(*function);
+  }
+
+  void VerifiesCallbacks() {
+    EXPECT_TRUE(non_leak_called_);
+    EXPECT_FALSE(leaky_called_);
+    non_leak_called_ = false;
+    leaky_called_ = false;
+  }
+
+  void VerifiesCallbacksNotCalled() {
+    EXPECT_FALSE(non_leak_called_);
+    EXPECT_FALSE(leaky_called_);
+    non_leak_called_ = false;
+    leaky_called_ = false;
+  }
+
+  static void WINAPI CallbackNoLeak() {
+    non_leak_called_ = true;
+  }
+
+  static void WINAPI CallbackLeak() {
+    leaky_called_ = true;
+  }
+
+ private:
+  static const wchar_t* const kLibrary;
+  HMODULE module_;
+  static bool non_leak_called_;
+  static bool leaky_called_;
+};
+
+bool SingletonTest::non_leak_called_ = false;
+bool SingletonTest::leaky_called_ = false;
+
+const wchar_t* const SingletonTest::kLibrary = L"singleton_dll_unittest.dll";
+
+TEST_F(SingletonTest, Basic) {
+  if (IsTestCaseDisabled())
+    return;
+
+  int* singleton_int_1;
+  int* singleton_int_2;
+  int* singleton_int_3;
+  int* singleton_int_4;
+  int* singleton_int_5;
+  CallBackFunc* leaky_singleton;
+
+  LoadLibrary();
+  {
+    SingletonIntFunc sut1;
+    SingletonIntFunc sut2;
+    SingletonIntFunc sut3;
+    SingletonIntFunc sut4;
+    SingletonIntFunc sut5;
+    {
+      GetProc("SingletonInt1", &sut1);
+      singleton_int_1 = sut1();
+    }
+    // Ensure POD type initialization.
+    EXPECT_EQ(*singleton_int_1, 0);
+    *singleton_int_1 = 1;
+
+    EXPECT_EQ(singleton_int_1, sut1());
+    EXPECT_EQ(*singleton_int_1, 1);
+
+    {
+      GetProc("SingletonInt2", &sut2);
+      singleton_int_2 = sut2();
+    }
+    // Same instance that 1.
+    EXPECT_EQ(*singleton_int_2, 1);
+    EXPECT_EQ(singleton_int_1, singleton_int_2);
+
+    {
+      GetProc("SingletonInt3", &sut3);
+      singleton_int_3 = sut3();
+    }
+    // Different instance than 1 and 2.
+    EXPECT_EQ(*singleton_int_3, 0);
+    EXPECT_NE(singleton_int_1, singleton_int_3);
+    *singleton_int_3 = 3;
+    EXPECT_EQ(*singleton_int_1, 1);
+    EXPECT_EQ(*singleton_int_2, 1);
+
+    {
+      GetProc("SingletonInt4", &sut4);
+      singleton_int_4 = sut4();
+    }
+    // Use a lock for creation. Not really tested at length.
+    EXPECT_EQ(*singleton_int_4, 0);
+    *singleton_int_4 = 4;
+    EXPECT_NE(singleton_int_1, singleton_int_4);
+    EXPECT_NE(singleton_int_3, singleton_int_4);
+
+    {
+      GetProc("SingletonInt5", &sut5);
+      singleton_int_5 = sut5();
+    }
+    // Is default initialized to 5.
+    EXPECT_EQ(*singleton_int_5, 5);
+    EXPECT_NE(singleton_int_1, singleton_int_5);
+    EXPECT_NE(singleton_int_3, singleton_int_5);
+    EXPECT_NE(singleton_int_4, singleton_int_5);
+#ifdef _DEBUG
+    // In release, the optimizer may make both exports use exactly the same
+    // code.
+    EXPECT_NE(sut1, sut2);
+#endif
+    EXPECT_NE(sut2, sut3);
+    EXPECT_NE(sut3, sut4);
+    EXPECT_NE(sut4, sut5);
+
+    LeakySingletonFunc noleak;
+    GetProc("SingletonNoLeak", &noleak);
+    noleak(&CallbackNoLeak);
+    LeakySingletonFunc leak;
+    GetProc("SingletonLeak", &leak);
+    leak(&CallbackLeak);
+    GetLeakySingletonFunc get_leaky;
+    GetProc("GetLeakySingleton", &get_leaky);
+    leaky_singleton = get_leaky();
+    EXPECT_TRUE(leaky_singleton);
+  }
+  FreeLibrary();
+
+  // Verify that only the expected callback has been called.
+  VerifiesCallbacks();
+  // Delete the leaky singleton. It is interesting to note that Purify does
+  // *not* detect the leak when this call is commented out. :(
+  EXPECT_TRUE(CustomAllocTrait<CallBackFunc>::Delete(leaky_singleton));
+
+  LoadLibrary();
+  {
+    // Verifiy that the variables were reset.
+    {
+      SingletonIntFunc sut1;
+      GetProc("SingletonInt1", &sut1);
+      singleton_int_1 = sut1();
+      EXPECT_EQ(*singleton_int_1, 0);
+    }
+    {
+      SingletonIntFunc sut5;
+      GetProc("SingletonInt5", &sut5);
+      singleton_int_5 = sut5();
+      EXPECT_EQ(*singleton_int_5, 5);
+    }
+  }
+  // The leaky singleton shouldn't leak since SingletonLeak has not been called.
+  FreeLibrary();
+
+  VerifiesCallbacksNotCalled();
+}
diff --git a/base/spin_wait.h b/base/spin_wait.h
new file mode 100644
index 0000000..7226387
--- /dev/null
+++ b/base/spin_wait.h
@@ -0,0 +1,74 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file provides a macro ONLY for use in testing.
+// DO NOT USE IN PRODUCTION CODE.  There are much better ways to wait.
+
+// This code is very helpful in testing multi-threaded code, without depending
+// on almost any primitives.  This is especially helpful if you are testing
+// those primitive multi-threaded constructs.
+
+// We provide a simple one argument spin wait (for 1 second), and a generic
+// spin wait (for longer periods of time).
+
+#ifndef BASE_SPIN_WAIT_H__
+#define BASE_SPIN_WAIT_H__
+
+#include "base/time.h"
+
+// Provide a macro that will wait no longer than 1 second for an asynchronous
+// change is the value of an expression.
+// A typical use would be:
+//
+//   SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 == f(x));
+//
+// The expression will be evaluated repeatedly until it is true, or until
+// the time (1 second) expires.
+// Since tests generally have a 5 second watch dog timer, this spin loop is
+// typically used to get the padding needed on a given test platform to assure
+// that the test passes, even if load varies, and external events vary.
+
+#define SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(expression) \
+    SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromSeconds(1), (expression))
+
+#define SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(delta, expression) do { \
+  Time start = Time::Now(); \
+  const TimeDelta kTimeout = delta; \
+    while(!(expression)) { \
+      if (kTimeout < Time::Now() - start) { \
+      EXPECT_LE((Time::Now() - start).InMilliseconds(), \
+                kTimeout.InMilliseconds()) << "Timed out"; \
+        break; \
+      } \
+      Sleep(50); \
+    } \
+  } \
+  while(0)
+
+#endif  // BASE_SPIN_WAIT_H__
diff --git a/base/stack_container.h b/base/stack_container.h
new file mode 100644
index 0000000..16c6ab7
--- /dev/null
+++ b/base/stack_container.h
@@ -0,0 +1,255 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_STACK_CONTAINER_H__
+#define BASE_STACK_CONTAINER_H__
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+
+// This allocator can be used with STL containers to provide a stack buffer
+// from which to allocate memory and overflows onto the heap. This stack buffer
+// would be allocated on the stack and allows us to avoid heap operations in
+// some situations.
+//
+// STL likes to make copies of allocators, so the allocator itself can't hold
+// the data. Instead, we make the creator responsible for creating a
+// StackAllocator::Source which contains the data. Copying the allocator
+// merely copies the pointer to this shared source, so all allocators created
+// based on our allocator will share the same stack buffer.
+//
+// This stack buffer implementation is very simple. The first allocation that
+// fits in the stack buffer will use the stack buffer. Any subsequent
+// allocations will not use the stack buffer, even if there is unused room.
+// This makes it appropriate for array-like containers, but the caller should
+// be sure to reserve() in the container up to the stack buffer size. Otherwise
+// the container will allocate a small array which will "use up" the stack
+// buffer.
+template<typename T, size_t stack_capacity>
+class StackAllocator : public std::allocator<T> {
+ public:
+  // Backing store for the allocator. The container owner is responsible for
+  // maintaining this for as long as any containers using this allocator are
+  // live.
+  struct Source {
+    Source() : used_stack_buffer_(false) {
+    }
+
+    // Casts the buffer in its right type.
+    T* stack_buffer() { return reinterpret_cast<T*>(stack_buffer_); }
+    const T* stack_buffer() const {
+      return reinterpret_cast<const T*>(stack_buffer_);
+    }
+
+    //
+    // IMPORTANT: Take care to ensure that stack_buffer_ is aligned
+    // since it is used to mimic an array of T.
+    // Be careful while declaring any unaligned types (like bool)
+    // before stack_buffer_.
+    //
+
+    // The buffer itself. It is not of type T because we don't want the
+    // constructors and destructors to be automatically called. Define a POD
+    // buffer of the right size instead.
+    char stack_buffer_[sizeof(T[stack_capacity])];
+
+    // Set when the stack buffer is used for an allocation. We do not track
+    // how much of the buffer is used, only that somebody is using it.
+    bool used_stack_buffer_;
+  };
+
+  // Used by containers when they want to refer to an allocator of type U.
+  template<typename U>
+  struct rebind {
+    typedef StackAllocator<U, stack_capacity> other;
+  };
+
+  StackAllocator(Source* source) : source_(source) {
+  }
+  StackAllocator(const StackAllocator& other) : source_(other.source_) {
+  }
+
+  // Actually do the allocation. Use the stack buffer if nobody has used it yet
+  // and the size requested fits. Otherwise, fall through to the standard
+  // allocator.
+  pointer allocate(size_type n, void* hint = 0) {
+    if (!source_->used_stack_buffer_ && n <= stack_capacity) {
+      source_->used_stack_buffer_ = true;
+      return source_->stack_buffer();
+    } else {
+      return std::allocator<T>::allocate(n, hint);
+    }
+  }
+
+  // Free: when trying to free the stack buffer, just mark it as free. For
+  // non-stack-buffer pointers, just fall though to the standard allocator.
+  void deallocate(pointer p, size_type n) {
+    if (p == source_->stack_buffer())
+      source_->used_stack_buffer_ = false;
+    else
+      std::allocator<T>::deallocate(p, n);
+  }
+
+ private:
+  Source* source_;
+};
+
+// A wrapper around STL containers that maintains a stack-sized buffer that the
+// initial capacity of the vector is based on. Growing the container beyond the
+// stack capacity will transparently overflow onto the heap. The container must
+// support reserve().
+//
+// WATCH OUT: the ContainerType MUST use the proper StackAllocator for this
+// type. This object is really intended to be used only internally. You'll want
+// to use the wrappers below for different types.
+template<typename ContainerType, int stack_capacity>
+class StackContainer {
+ public:
+  typedef typename ContainerType ContainerType;
+  typedef typename ContainerType::value_type ContainedType;
+  typedef StackAllocator<ContainedType, stack_capacity> Allocator;
+
+  // Allocator must be constructed before the container!
+  StackContainer() : allocator_(&stack_data_), container_(allocator_) {
+    // Make the container use the stack allocation by reserving our buffer size
+    // before doing anything else.
+    container_.reserve(stack_capacity);
+  }
+
+  // Getters for the actual container.
+  //
+  // Danger: any copies of this made using the copy constructor must have
+  // shorter lifetimes than the source. The copy will share the same allocator
+  // and therefore the same stack buffer as the original. Use std::copy to
+  // copy into a "real" container for longer-lived objects.
+  ContainerType& container() { return container_; }
+  const ContainerType& container() const { return container_; }
+
+  // Support operator-> to get to the container. This allows nicer syntax like:
+  //   StackContainer<...> foo;
+  //   std::sort(foo->begin(), foo->end());
+  ContainerType* operator->() { return &container_; }
+  const ContainerType* operator->() const { return &container_; }
+
+#ifdef UNIT_TEST
+  // Retrieves the stack source so that that unit tests can verify that the
+  // buffer is being used properly.
+  typename const Allocator::Source& stack_data() const {
+    return stack_data_;
+  }
+#endif
+
+ protected:
+  typename Allocator::Source stack_data_;
+  Allocator allocator_;
+  ContainerType container_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(StackContainer);
+};
+
+// StackString
+template<size_t stack_capacity>
+class StackString : public StackContainer<
+    std::basic_string<char,
+                      std::char_traits<char>,
+                      StackAllocator<char, stack_capacity> >,
+    stack_capacity> {
+ public:
+  StackString() : StackContainer<
+      std::basic_string<char,
+                        std::char_traits<char>,
+                        StackAllocator<char, stack_capacity> >,
+      stack_capacity>() {
+  }
+
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(StackString);
+};
+
+// StackWString
+template<size_t stack_capacity>
+class StackWString : public StackContainer<
+    std::basic_string<wchar_t,
+                      std::char_traits<wchar_t>,
+                      StackAllocator<wchar_t, stack_capacity> >,
+    stack_capacity> {
+ public:
+  StackWString() : StackContainer<
+      std::basic_string<wchar_t,
+                        std::char_traits<wchar_t>,
+                        StackAllocator<wchar_t, stack_capacity> >,
+      stack_capacity>() {
+  }
+
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(StackWString);
+};
+
+// StackVector
+//
+// Example:
+//   StackVector<int, 16> foo;
+//   foo->push_back(22);  // we have overloaded operator->
+//   foo[0] = 10;         // as well as operator[]
+template<typename T, size_t stack_capacity>
+class StackVector : public StackContainer<
+    std::vector<T, StackAllocator<T, stack_capacity> >,
+    stack_capacity> {
+ public:
+  StackVector() : StackContainer<
+      std::vector<T, StackAllocator<T, stack_capacity> >,
+      stack_capacity>() {
+  }
+
+  // We need to put this in STL containers sometimes, which requires a copy
+  // constructor. We can't call the regular copy constructor because that will
+  // take the stack buffer from the original. Here, we create an empty object
+  // and make a stack buffer of its own.
+  StackVector(const StackVector<T, stack_capacity>& other)
+      : StackContainer<
+            std::vector<T, StackAllocator<T, stack_capacity> >,
+            stack_capacity>() {
+    container().assign(other->begin(), other->end());
+  }
+
+  StackVector<T, stack_capacity>& operator=(
+      const StackVector<T, stack_capacity>& other) {
+    container().assign(other->begin(), other->end());
+    return *this;
+  }
+
+  // Vectors are commonly indexed, which isn't very convenient even with
+  // operator-> (using "->at()" does exception stuff we don't want).
+  T& operator[](size_t i) { return container().operator[](i); }
+  const T& operator[](size_t i) const { return container().operator[](i); }
+};
+
+#endif  // BASE_STACK_CONTAINER_H__
diff --git a/base/stack_container_unittest.cc b/base/stack_container_unittest.cc
new file mode 100644
index 0000000..aa8f34a
--- /dev/null
+++ b/base/stack_container_unittest.cc
@@ -0,0 +1,137 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/stack_container.h"
+
+#include <algorithm>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "base/ref_counted.h"
+
+namespace {
+
+class Dummy : public base::RefCounted<Dummy> {
+ public:
+  Dummy(int* alive) : alive_(alive) {
+    ++*alive_;
+  }
+  ~Dummy() {
+    --*alive_;
+  }
+ private:
+  int* const alive_;
+};
+
+}  // namespace
+
+TEST(StackContainer, Vector) {
+  const int stack_size = 3;
+  StackVector<int, stack_size> vect;
+  const int* stack_buffer = &vect.stack_data().stack_buffer()[0];
+
+  // The initial |stack_size| elements should appear in the stack buffer.
+  EXPECT_EQ(stack_size, vect.container().capacity());
+  for (int i = 0; i < stack_size; i++) {
+    vect.container().push_back(i);
+    EXPECT_EQ(stack_buffer, &vect.container()[0]);
+    EXPECT_TRUE(vect.stack_data().used_stack_buffer_);
+  }
+
+  // Adding more elements should push the array onto the heap.
+  for (int i = 0; i < stack_size; i++) {
+    vect.container().push_back(i + stack_size);
+    EXPECT_NE(stack_buffer, &vect.container()[0]);
+    EXPECT_FALSE(vect.stack_data().used_stack_buffer_);
+  }
+
+  // The array should still be in order.
+  for (int i = 0; i < stack_size * 2; i++)
+    EXPECT_EQ(i, vect.container()[i]);
+
+  // Resize to smaller. Our STL implementation won't reallocate in this case,
+  // otherwise it might use our stack buffer. We reserve right after the resize
+  // to guarantee it isn't using the stack buffer, even though it doesn't have
+  // much data.
+  vect.container().resize(stack_size);
+  vect.container().reserve(stack_size * 2);
+  EXPECT_FALSE(vect.stack_data().used_stack_buffer_);
+
+  // Copying the small vector to another should use the same allocator and use
+  // the now-unused stack buffer. GENERALLY CALLERS SHOULD NOT DO THIS since
+  // they have to get the template types just right and it can cause errors.
+  std::vector<int, StackAllocator<int, stack_size> > other(vect.container());
+  EXPECT_EQ(stack_buffer, &other.front());
+  EXPECT_TRUE(vect.stack_data().used_stack_buffer_);
+  for (int i = 0; i < stack_size; i++)
+    EXPECT_EQ(i, other[i]);
+}
+
+TEST(StackContainer, VectorDoubleDelete) {
+  // Regression testing for double-delete.
+  typedef StackVector<scoped_refptr<Dummy>, 2> Vector;
+  typedef Vector::ContainerType Container;
+  Vector vect;
+
+  int alive = 0;
+  scoped_refptr<Dummy> dummy(new Dummy(&alive));
+  EXPECT_EQ(alive, 1);
+
+  vect->push_back(dummy);
+  EXPECT_EQ(alive, 1);
+
+  Dummy* dummy_unref = dummy.get();
+  dummy = NULL;
+  EXPECT_EQ(alive, 1);
+
+  Container::iterator itr = std::find(vect->begin(), vect->end(), dummy_unref);
+  EXPECT_EQ(itr->get(), dummy_unref);
+  vect->erase(itr);
+  EXPECT_EQ(alive, 0);
+
+  // Shouldn't crash at exit.
+}
+
+TEST(StackContainer, BufferAlignment) {
+  StackVector<wchar_t, 16> text;
+  text->push_back(L'A');
+  text->push_back(L'B');
+  text->push_back(L'C');
+  text->push_back(L'D');
+  text->push_back(L'E');
+  text->push_back(L'F');
+  text->push_back(0);
+
+  const wchar_t* buffer = &text[1];
+  bool even_aligned = (0 == (((size_t)buffer) & 0x1));
+  EXPECT_EQ(even_aligned, true);
+}
+
+// Make sure all the class compiles correctly.
+template StackVector<int, 2>;
+template StackVector<scoped_refptr<Dummy>, 2>;
diff --git a/base/stats_counters.h b/base/stats_counters.h
new file mode 100644
index 0000000..778b824
--- /dev/null
+++ b/base/stats_counters.h
@@ -0,0 +1,297 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+#ifndef BASE_STATS_COUNTERS_H__
+#define BASE_STATS_COUNTERS_H__
+
+#include <string>
+#include "base/stats_table.h"
+#include "base/time.h"
+
+// StatsCounters are dynamically created values which can be tracked in
+// the StatsTable.  They are designed to be lightweight to create and
+// easy to use.
+//
+// Since StatsCounters can be created dynamically by name, there is
+// a hash table lookup to find the counter in the table.  A StatsCounter
+// object can be created once and used across multiple threads safely.
+//
+// Example usage:
+//    {
+//      StatsCounter request_count("RequestCount");
+//      request_count.Increment();
+//    }
+//
+// Note that creating counters on the stack does work, however creating
+// the counter object requires a hash table lookup.  For inner loops, it
+// may be better to create the counter either as a member of another object
+// (or otherwise outside of the loop) for maximum performance.
+//
+// Internally, a counter represents a value in a row of a StatsTable.
+// The row has a 32bit value for each process/thread in the table and also
+// a name (stored in the table metadata).
+//
+// NOTE: In order to make stats_counters usable in lots of different code,
+// avoid any dependencies inside this header file.
+//
+
+//------------------------------------------------------------------------------
+// Define macros for ease of use. They also allow us to change definitions
+// as the implementation varies, or depending on compile options.
+//------------------------------------------------------------------------------
+// First provide generic macros, which exist in production as well as debug.
+#define STATS_COUNTER(name, delta) do { \
+  static StatsCounter counter(name); \
+  counter.Add(delta); \
+} while (0)
+
+#define SIMPLE_STATS_COUNTER(name) STATS_COUNTER(name, 1)
+
+#define RATE_COUNTER(name, duration) do { \
+  static StatsRate hit_count(name); \
+  hit_count.AddTime(duration); \
+} while (0)
+
+// Define Debug vs non-debug flavors of macros.
+#ifndef NDEBUG
+
+#define DSTATS_COUNTER(name, delta) STATS_COUNTER(name, delta)
+#define DSIMPLE_STATS_COUNTER(name) SIMPLE_STATS_COUNTER(name)
+#define DRATE_COUNTER(name, duration) RATE_COUNTER(name, duration)
+
+#else  // NDEBUG
+
+#define DSTATS_COUNTER(name, delta) do {} while (0)
+#define DSIMPLE_STATS_COUNTER(name) do {} while (0)
+#define DRATE_COUNTER(name, duration) do {} while (0)
+
+#endif  // NDEBUG
+
+//------------------------------------------------------------------------------
+// StatsCounter represents a counter in the StatsTable class.
+class StatsCounter {
+ public:
+  // Create a StatsCounter object.
+  explicit StatsCounter(const std::wstring& name)
+       : counter_id_(-1) {
+    // We prepend the name with 'c:' to indicate that it is a counter.
+    name_ = L"c:";
+    name_.append(name);
+  };
+
+  virtual ~StatsCounter() {}
+
+  // Sets the counter to a specific value.
+  void Set(int value) {
+    int* loc = GetPtr();
+    if (loc) *loc = value;
+  }
+
+  // Increments the counter.
+  void Increment() {
+    Add(1);
+  }
+
+  // TODO(jar) temporary hack include method till base and chrome use new name.
+  void Increment(int value) {
+    Add(value);
+  }
+
+  virtual void Add(int value) {
+    int* loc = GetPtr();
+    if (loc)
+      (*loc) += value;
+  }
+
+  // Decrements the counter.
+  void Decrement() {
+    Add(-1);
+  }
+
+  void Subtract(int value) {
+    Add(-value);
+  }
+
+  // TODO(jar) temporary hack includes method till base and chrome use new name.
+  void Decrement(int value) {
+    Add(-value);
+  }
+
+  // Is this counter enabled?
+  // Returns false if table is full.
+  bool Enabled() {
+    return GetPtr() != NULL;
+  }
+
+  int value() {
+    int* loc = GetPtr();
+    if (loc) return *loc;
+    return 0;
+  }
+
+ protected:
+  StatsCounter()
+    : counter_id_(-1) {
+  }
+
+  // Returns the cached address of this counter location.
+  int* GetPtr() {
+    StatsTable* table = StatsTable::current();
+    if (!table)
+      return NULL;
+
+    // If counter_id_ is -1, then we haven't looked it up yet.
+    if (counter_id_ == -1) {
+      counter_id_ = table->FindCounter(name_);
+      if (table->GetSlot() == 0) {
+        if (!table->RegisterThread(L"")) {
+          // There is no room for this thread.  This thread
+          // cannot use counters.
+          counter_id_ = 0;
+          return NULL;
+        }
+      }
+    }
+
+    // If counter_id_ is > 0, then we have a valid counter.
+    if (counter_id_ > 0)
+      return table->GetLocation(counter_id_, table->GetSlot());
+
+    // counter_id_ was zero, which means the table is full.
+    return NULL;
+  }
+
+  std::wstring name_;
+  // The counter id in the table.  We initialize to -1 (an invalid value)
+  // and then cache it once it has been looked up.  The counter_id is
+  // valid across all threads and processes.
+  int32 counter_id_;
+};
+
+
+// A StatsCounterTimer is a StatsCounter which keeps a timer during
+// the scope of the StatsCounterTimer.  On destruction, it will record
+// its time measurement.
+class StatsCounterTimer : protected StatsCounter {
+ public:
+  // Constructs and starts the timer.
+  explicit StatsCounterTimer(const std::wstring& name) {
+    // we prepend the name with 't:' to indicate that it is a timer.
+    name_ = L"t:";
+    name_.append(name);
+  }
+
+  // Start the timer.
+  void Start() {
+    if (!Enabled())
+      return;
+    start_time_ = TimeTicks::Now();
+    stop_time_ = TimeTicks();
+  }
+
+  // Stop the timer and record the results.
+  void Stop() {
+    if (!Enabled() || !Running())
+      return;
+    stop_time_ = TimeTicks::Now();
+    Record();
+  }
+
+  // Returns true if the timer is running.
+  bool Running() {
+    return Enabled() && !start_time_.is_null() && stop_time_.is_null();
+  }
+
+  // Accept a TimeDelta to increment.
+  virtual void AddTime(TimeDelta time) {
+    Add(static_cast<int>(time.InMilliseconds()));
+  }
+
+  // TODO(jar)  temporary hack include method till base and chrome use new name.
+  void IncrementTimer(TimeDelta time) {
+    AddTime(time);
+  }
+
+ protected:
+  // Compute the delta between start and stop, in milliseconds.
+  void Record() {
+    AddTime(stop_time_ - start_time_);
+  }
+
+  TimeTicks start_time_;
+  TimeTicks stop_time_;
+};
+
+// A StatsRate is a timer that keeps a count of the number of intervals added so
+// that several statistics can be produced:
+//    min, max, avg, count, total
+class StatsRate : public StatsCounterTimer {
+ public:
+  // Constructs and starts the timer.
+  explicit StatsRate(const wchar_t* name)
+      : StatsCounterTimer(name),
+      counter_(name),
+      largest_add_(std::wstring(L" ").append(name).append(L"MAX").c_str()) {
+  }
+
+  virtual void Add(int value) {
+    counter_.Increment();
+    StatsCounterTimer::Add(value);
+    if (value > largest_add_.value())
+      largest_add_.Set(value);
+  }
+
+ private:
+  StatsCounter counter_;
+  StatsCounter largest_add_;
+};
+
+
+// Helper class for scoping a timer or rate.
+template<class T> class StatsScope {
+ public:
+  explicit StatsScope<T>(T& timer)
+      : timer_(timer) {
+    timer_.Start();
+  }
+
+  ~StatsScope() {
+    timer_.Stop();
+  }
+
+  void Stop() {
+    timer_.Stop();
+  }
+
+ private:
+  T& timer_;
+};
+
+#endif  // BASE_STATS_COUNTERS_H__
diff --git a/base/stats_table.cc b/base/stats_table.cc
new file mode 100644
index 0000000..8213c34
--- /dev/null
+++ b/base/stats_table.cc
@@ -0,0 +1,545 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/stats_table.h"
+
+#include "base/logging.h"
+#include "base/thread_local_storage.h"
+
+// The StatsTable uses a shared memory segment that is laid out as follows
+//
+// +-------------------------------------------+
+// | Version | Size | MaxCounters | MaxThreads |
+// +-------------------------------------------+
+// | Thread names table                        |
+// +-------------------------------------------+
+// | Thread TID table                          |
+// +-------------------------------------------+
+// | Thread PID table                          |
+// +-------------------------------------------+
+// | Counter names table                       |
+// +-------------------------------------------+
+// | Data                                      |
+// +-------------------------------------------+
+//
+// The data layout is a grid, where the columns are the thread_ids and the
+// rows are the counter_ids.
+//
+// If the first character of the thread_name is '\0', then that column is
+// empty.
+// If the first character of the counter_name is '\0', then that row is
+// empty.
+//
+// About Locking:
+// This class is designed to be both multi-thread and multi-process safe.
+// Aside from initialization, this is done by partitioning the data which
+// each thread uses so that no locking is required.  However, to allocate
+// the rows and columns of the table to particular threads, locking is
+// required.
+//
+// At the shared-memory level, we have a lock.  This lock protects the
+// shared-memory table only, and is used when we create new counters (e.g.
+// use rows) or when we register new threads (e.g. use columns).  Reading
+// data from the table does not require any locking at the shared memory
+// level.
+//
+// Each process which accesses the table will create a StatsTable object.
+// The StatsTable maintains a hash table of the existing counters in the
+// table for faster lookup.  Since the hash table is process specific,
+// each process maintains its own cache.  We avoid complexity here by never
+// de-allocating from the hash table.  (Counters are dynamically added,
+// but not dynamically removed).
+
+// In order for external viewers to be able to read our shared memory,
+// we all need to use the same size ints.
+COMPILE_ASSERT(sizeof(int)==4, expect_4_byte_ints);
+
+namespace {
+
+// An internal version in case we ever change the format of this
+// file, and so that we can identify our table.
+const int kTableVersion = 0x13131313;
+
+// The name for un-named counters and threads in the table.
+const wchar_t kUnknownName[] = L"<unknown>";
+
+// Various header information contained in the memory mapped segment.
+struct TableHeader {
+  int version;
+  int size;
+  int max_counters;
+  int max_threads;
+};
+
+// Calculates delta to align an offset to the size of an int
+inline int AlignOffset(int offset) {
+  return (sizeof(int) - (offset % sizeof(int))) % sizeof(int);
+}
+
+inline int AlignedSize(int size) {
+  return size + AlignOffset(size);
+}
+
+// StatsTableTLSData carries the data stored in the TLS slots for the
+// StatsTable.  This is used so that we can properly cleanup when the
+// thread exits and return the table slot.
+//
+// Each thread that calls RegisterThread in the StatsTable will have
+// a StatsTableTLSData stored in its TLS.
+struct StatsTableTLSData {
+  StatsTable* table;
+  int slot;
+};
+
+// The SlotReturnFunction is called at thread exit for each thread
+// which used the StatsTable.
+static void SlotReturnFunction(void* data) {
+  StatsTableTLSData* tls_data = static_cast<StatsTableTLSData*>(data);
+  if (tls_data) {
+    DCHECK(tls_data->table);
+    tls_data->table->UnregisterThread();
+  }
+}
+
+}  // namespace
+
+// The StatsTablePrivate maintains convenience pointers into the
+// shared memory segment.  Use this class to keep the data structure
+// clean and accessible.
+class StatsTablePrivate {
+ public:
+  // Create the StatsTablePrivate based on expected size parameters.
+  StatsTablePrivate(void* memory, int size, int max_threads, int max_counters);
+
+  // Accessors for our header pointers
+  TableHeader* table_header() const { return table_header_; }
+  int version() const { return table_header_->version; }
+  int size() const { return table_header_->size; }
+  int max_counters() const { return table_header_->max_counters; }
+  int max_threads() const { return table_header_->max_threads; }
+
+  // Accessors for our tables
+  wchar_t* thread_name(int slot_id) const {
+    return &thread_names_table_[
+      (slot_id-1) * (StatsTable::kMaxThreadNameLength)];
+  }
+  int* thread_tid(int slot_id) const {
+    return &(thread_tid_table_[slot_id-1]);
+  }
+  int* thread_pid(int slot_id) const {
+    return &(thread_pid_table_[slot_id-1]);
+  }
+  wchar_t* counter_name(int counter_id) const {
+    return &counter_names_table_[
+      (counter_id-1) * (StatsTable::kMaxCounterNameLength)];
+  }
+  int* row(int counter_id) const {
+    return &data_table_[(counter_id-1) * max_threads()];
+  }
+
+ private:
+  // Initializes the table on first access.  Sets header values
+  // appropriately and zeroes all counters.
+  void InitializeTable(void* memory, int size, int max_counters,
+                       int max_threads);
+
+  // Initializes our in-memory pointers into a pre-created StatsTable.
+  void ComputeMappedPointers(void* memory);
+
+  TableHeader* table_header_;
+  wchar_t* thread_names_table_;
+  int* thread_tid_table_;
+  int* thread_pid_table_;
+  wchar_t* counter_names_table_;
+  int* data_table_;
+};
+
+StatsTablePrivate::StatsTablePrivate(void* memory, int size, int max_threads,
+                                     int max_counters) {
+  TableHeader* header = static_cast<TableHeader*>(memory);
+  // If the version does not match, then assume the table needs
+  // to be initialized.
+  if (header->version != kTableVersion)
+    InitializeTable(memory, size, max_counters, max_threads);
+
+  // We have a valid table, so compute our pointers.
+  ComputeMappedPointers(memory);
+}
+
+void StatsTablePrivate::InitializeTable(void* memory, int size,
+                                        int max_counters,
+                                        int max_threads) {
+  // Zero everything.
+  memset(memory, 0, size);
+
+  // Initialize the header.
+  TableHeader* header = static_cast<TableHeader*>(memory);
+  header->version = kTableVersion;
+  header->size = size;
+  header->max_counters = max_counters;
+  header->max_threads = max_threads;
+}
+
+void StatsTablePrivate::ComputeMappedPointers(void* memory) {
+  char* data = static_cast<char*>(memory);
+  int offset = 0;
+
+  table_header_ = reinterpret_cast<TableHeader*>(data);
+  offset += sizeof(*table_header_);
+  offset += AlignOffset(offset);
+
+  // Verify we're looking at a valid StatsTable.
+  DCHECK_EQ(table_header_->version, kTableVersion);
+
+  thread_names_table_ = reinterpret_cast<wchar_t*>(data + offset);
+  offset += sizeof(wchar_t) *
+            max_threads() * StatsTable::kMaxThreadNameLength;
+  offset += AlignOffset(offset);
+
+  thread_tid_table_ = reinterpret_cast<int*>(data + offset);
+  offset += sizeof(int) * max_threads();
+  offset += AlignOffset(offset);
+
+  thread_pid_table_ = reinterpret_cast<int*>(data + offset);
+  offset += sizeof(int) * max_threads();
+  offset += AlignOffset(offset);
+
+  counter_names_table_ = reinterpret_cast<wchar_t*>(data + offset);
+  offset += sizeof(wchar_t) *
+            max_counters() * StatsTable::kMaxCounterNameLength;
+  offset += AlignOffset(offset);
+
+  data_table_ = reinterpret_cast<int*>(data + offset);
+  offset += sizeof(int) * max_threads() * max_counters();
+
+  DCHECK_EQ(offset, size());
+}
+
+
+
+// We keep a singleton table which can be easily accessed.
+StatsTable* StatsTable::global_table_ = NULL;
+
+StatsTable::StatsTable(const std::wstring& name, int max_threads,
+                       int max_counters)
+    : tls_index_(ThreadLocalStorage::Alloc(SlotReturnFunction)) {
+  int table_size =
+    AlignedSize(sizeof(TableHeader)) +
+    AlignedSize((max_counters * sizeof(wchar_t) * kMaxCounterNameLength)) +
+    AlignedSize((max_threads * sizeof(wchar_t) * kMaxThreadNameLength)) +
+    AlignedSize(max_threads * sizeof(int)) +
+    AlignedSize(max_threads * sizeof(int)) +
+    AlignedSize((sizeof(int) * (max_counters * max_threads)));
+
+  impl_ = NULL;
+  // TODO(mbelshe): Move this out of the constructor
+  if (shared_memory_.Create(name, false, true, table_size))
+    if (shared_memory_.Map(table_size))
+      impl_ = new StatsTablePrivate(shared_memory_.memory(), table_size,
+                                    max_threads, max_counters);
+  if (!impl_)
+    LOG(ERROR) << "StatsTable did not initialize:" << GetLastError();
+}
+
+StatsTable::~StatsTable() {
+  // Before we tear down our copy of the table, be sure to
+  // unregister our thread.
+  UnregisterThread();
+
+  // Return ThreadLocalStorage.  At this point, if any registered threads
+  // still exist, they cannot Unregister.
+  ThreadLocalStorage::Free(tls_index_);
+
+  // Cleanup our shared memory.
+  delete impl_;
+
+  // If we are the global table, unregister ourselves.
+  if (global_table_ == this)
+    global_table_ = NULL;
+}
+
+int StatsTable::RegisterThread(const std::wstring& name) {
+  int slot = 0;
+
+  // Registering a thread requires that we lock the shared memory
+  // so that two threads don't grab the same slot.  Fortunately,
+  // thread creation shouldn't happen in inner loops.
+  {
+    SharedMemoryAutoLock lock(&shared_memory_);
+    slot = FindEmptyThread();
+    if (!slot) {
+      return 0;
+    }
+
+    DCHECK(impl_);
+
+    // We have space, so consume a column in the table.
+    std::wstring thread_name = name;
+    if (name.empty())
+      thread_name = kUnknownName;
+    wcsncpy_s(impl_->thread_name(slot), kMaxThreadNameLength,
+      thread_name.c_str(), _TRUNCATE);
+    *(impl_->thread_tid(slot)) = GetCurrentThreadId();
+    *(impl_->thread_pid(slot)) = GetCurrentProcessId();
+  }
+
+  // Set our thread local storage.
+  StatsTableTLSData* data = new StatsTableTLSData;
+  data->table = this;
+  data->slot = slot;
+  ThreadLocalStorage::Set(tls_index_, data);
+  return slot;
+}
+
+StatsTableTLSData* StatsTable::GetTLSData() const {
+  StatsTableTLSData* data =
+    static_cast<StatsTableTLSData*>(ThreadLocalStorage::Get(tls_index_));
+  if (!data)
+    return NULL;
+
+  DCHECK(data->slot);
+  DCHECK_EQ(data->table, this);
+  return data;
+}
+
+void StatsTable::UnregisterThread() {
+  StatsTableTLSData* data = GetTLSData();
+  if (!data)
+    return;
+  DCHECK(impl_);
+
+  // Mark the slot free by zeroing out the thread name.
+  wchar_t* name = impl_->thread_name(data->slot);
+  *name = L'\0';
+
+  // Remove the calling thread's TLS so that it cannot use the slot.
+  ThreadLocalStorage::Set(tls_index_, NULL);
+  delete data;
+}
+
+int StatsTable::CountThreadsRegistered() const {
+  if (!impl_)
+    return 0;
+
+  // Loop through the shared memory and count the threads that are active.
+  // We intentionally do not lock the table during the operation.
+  int count = 0;
+  for (int index = 1; index <= impl_->max_threads(); index++) {
+    wchar_t* name = impl_->thread_name(index);
+    if (*name != L'\0')
+      count++;
+  }
+  return count;
+}
+
+int StatsTable::GetSlot() const {
+  StatsTableTLSData* data = GetTLSData();
+  if (!data)
+    return 0;
+  return data->slot;
+}
+
+int StatsTable::FindEmptyThread() const {
+  // Note: the API returns slots numbered from 1..N, although
+  // internally, the array is 0..N-1.  This is so that we can return
+  // zero as "not found".
+  //
+  // The reason for doing this is because the thread 'slot' is stored
+  // in TLS, which is always initialized to zero, not -1.  If 0 were
+  // returned as a valid slot number, it would be confused with the
+  // uninitialized state.
+  if (!impl_)
+    return 0;
+
+  int index = 1;
+  for (; index <= impl_->max_threads(); index++) {
+    wchar_t* name = impl_->thread_name(index);
+    if (!*name)
+      break;
+  }
+  if (index > impl_->max_threads())
+    return 0;  // The table is full.
+  return index;
+}
+
+int StatsTable::FindCounterOrEmptyRow(const std::wstring& name) const {
+  // Note: the API returns slots numbered from 1..N, although
+  // internally, the array is 0..N-1.  This is so that we can return
+  // zero as "not found".
+  //
+  // There isn't much reason for this other than to be consistent
+  // with the way we track columns for thread slots.  (See comments
+  // in FindEmptyThread for why it is done this way).
+  if (!impl_)
+    return 0;
+
+  int free_slot = 0;
+  for (int index = 1; index <= impl_->max_counters(); index++) {
+    wchar_t* row_name = impl_->counter_name(index);
+    if (!*row_name && !free_slot)
+      free_slot = index;  // save that we found a free slot
+    else if (!wcsncmp(row_name, name.c_str(), kMaxCounterNameLength))
+      return index;
+  }
+  return free_slot;
+}
+
+int StatsTable::FindCounter(const std::wstring& name) {
+  // Note: the API returns counters numbered from 1..N, although
+  // internally, the array is 0..N-1.  This is so that we can return
+  // zero as "not found".
+  if (!impl_)
+    return 0;
+
+  // Create a scope for our auto-lock.
+  {
+    AutoLock scoped_lock(counters_lock_);
+
+    // Attempt to find the counter.
+    CountersMap::const_iterator iter;
+    iter = counters_.find(name);
+    if (iter != counters_.end())
+      return iter->second;
+  }
+
+  // Counter does not exist, so add it.
+  return AddCounter(name);
+}
+
+int StatsTable::AddCounter(const std::wstring& name) {
+  DCHECK(impl_);
+
+  if (!impl_)
+    return 0;
+
+  int counter_id = 0;
+  {
+    // To add a counter to the shared memory, we need the
+    // shared memory lock.
+    SharedMemoryAutoLock lock(&shared_memory_);
+
+    // We have space, so create a new counter.
+    counter_id = FindCounterOrEmptyRow(name);
+    if (!counter_id)
+      return 0;
+
+    std::wstring counter_name = name;
+    if (name.empty())
+      counter_name = kUnknownName;
+    wcsncpy_s(impl_->counter_name(counter_id), kMaxCounterNameLength,
+      counter_name.c_str(), _TRUNCATE);
+  }
+
+  // now add to our in-memory cache
+  {
+    AutoLock lock(counters_lock_);
+    counters_[name] = counter_id;
+  }
+  return counter_id;
+}
+
+int* StatsTable::GetLocation(int counter_id, int slot_id) const {
+  if (!impl_)
+    return NULL;
+  if (slot_id > impl_->max_threads())
+    return NULL;
+
+  int* row = impl_->row(counter_id);
+  return &(row[slot_id-1]);
+}
+
+const wchar_t* StatsTable::GetRowName(int index) const {
+  if (!impl_)
+    return NULL;
+
+  return impl_->counter_name(index);
+}
+
+int StatsTable::GetRowValue(int index, int pid) const {
+  if (!impl_)
+    return 0;
+
+  int rv = 0;
+  int* row = impl_->row(index);
+  for (int index = 0; index < impl_->max_threads(); index++) {
+    if (pid == 0 || *impl_->thread_pid(index) == pid)
+      rv += row[index];
+  }
+  return rv;
+}
+
+int StatsTable::GetRowValue(int index) const {
+  return GetRowValue(index, 0);
+}
+
+int StatsTable::GetCounterValue(const std::wstring& name, int pid) {
+  if (!impl_)
+    return 0;
+
+  int row = FindCounter(name);
+  if (!row)
+    return 0;
+  return GetRowValue(row, pid);
+}
+
+int StatsTable::GetCounterValue(const std::wstring& name) {
+  return GetCounterValue(name, 0);
+}
+
+int StatsTable::GetMaxCounters() const {
+  if (!impl_)
+    return 0;
+  return impl_->max_counters();
+}
+
+int StatsTable::GetMaxThreads() const {
+  if (!impl_)
+    return 0;
+  return impl_->max_threads();
+}
+
+int* StatsTable::FindLocation(const wchar_t* name) {
+  // Get the static StatsTable
+  StatsTable *table = StatsTable::current();
+  if (!table)
+    return NULL;
+
+  // Get the slot for this thread.  Try to register
+  // it if none exists.
+  int slot = table->GetSlot();
+  if (!slot && !(slot = table->RegisterThread(L"")))
+      return NULL;
+
+  // Find the counter id for the counter.
+  std::wstring str_name(name);
+  int counter = table->FindCounter(str_name);
+
+  // Now we can find the location in the table.
+  return table->GetLocation(counter, slot);
+}
diff --git a/base/stats_table.h b/base/stats_table.h
new file mode 100644
index 0000000..0938099
--- /dev/null
+++ b/base/stats_table.h
@@ -0,0 +1,209 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// A StatsTable is a table of statistics.  It can be used across multiple
+// processes and threads, maintaining cheap statistics counters without
+// locking.
+//
+// The goal is to make it very cheap and easy for developers to add
+// counters to code, without having to build one-off utilities or mechanisms
+// to track the counters, and also to allow a single "view" to display
+// the contents of all counters.
+//
+// To achieve this, StatsTable creates a shared memory segment to store
+// the data for the counters.  Upon creation, it has a specific size
+// which governs the maximum number of counters and concurrent
+// threads/processes which can use it.
+//
+
+#ifndef BASE_STATS_TABLE_H__
+#define BASE_STATS_TABLE_H__
+
+#include <string>
+#include "base/basictypes.h"
+#include "base/hash_tables.h"
+#include "base/lock.h"
+#include "base/shared_memory.h"
+#include "base/thread_local_storage.h"
+
+class StatsTablePrivate;
+
+namespace {
+struct StatsTableTLSData;
+}
+
+class StatsTable {
+ public:
+  // Create a new StatsTable.
+  // If a StatsTable already exists with the specified name, this StatsTable
+  // will use the same shared memory segment as the original.  Otherwise,
+  // a new StatsTable is created and all counters are zeroed.
+  //
+  // name is the name of the StatsTable to use.
+  //
+  // max_threads is the maximum number of threads the table will support.
+  // If the StatsTable already exists, this number is ignored.
+  //
+  // max_counters is the maximum number of counters the table will support.
+  // If the StatsTable already exists, this number is ignored.
+  StatsTable(const std::wstring& name, int max_threads, int max_counters);
+
+  // Destroys the StatsTable.  When the last StatsTable is destroyed
+  // (across all processes), the StatsTable is removed from disk.
+  ~StatsTable();
+
+  // For convenience, we create a static table.  This is generally
+  // used automatically by the counters.
+  static StatsTable* current() { return global_table_; }
+
+  // Set the global table for use in this process.
+  static void set_current(StatsTable* value) { global_table_ = value; }
+
+  // Get the slot id for the calling thread. Returns 0 if no
+  // slot is assigned.
+  int GetSlot() const;
+
+  // All threads that contribute data to the table must register with the
+  // table first.  This function will set thread local storage for the
+  // thread containing the location in the table where this thread will
+  // write its counter data.
+  //
+  // name is just a debugging tag to label the thread, and it does not
+  // need to be unique.  It will be truncated to kMaxThreadNameLength-1
+  // characters.
+  //
+  // On success, returns the slot id for this thread.  On failure,
+  // returns 0.
+  int RegisterThread(const std::wstring& name);
+
+  // Returns the space occupied by a thread in the table.  Generally used
+  // if a thread terminates but the process continues.  This function
+  // does not zero out the thread's counters.
+  void UnregisterThread();
+
+  // Returns the number of threads currently registered.  This is really not
+  // useful except for diagnostics and debugging.
+  int CountThreadsRegistered() const;
+
+  // Find a counter in the StatsTable.
+  //
+  // Returns an id for the counter which can be used to call GetLocation().
+  // If the counter does not exist, attempts to create a row for the new
+  // counter.  If there is no space in the table for the new counter,
+  // returns 0.
+  int FindCounter(const std::wstring& name);
+
+  // TODO(mbelshe): implement RemoveCounter.
+
+  // Gets the location of a particular value in the table based on
+  // the counter id and slot id.
+  int* GetLocation(int counter_id, int slot_id) const;
+
+  // Gets the counter name at a particular row.  If the row is empty,
+  // returns NULL.
+  const wchar_t* GetRowName(int index) const;
+
+  // Gets the sum of the values for a particular row.
+  int GetRowValue(int index) const;
+
+  // Gets the sum of the values for a particular row for a given pid.
+  int GetRowValue(int index, int pid) const;
+
+  // Gets the sum of the values for a particular counter.  If the counter
+  // does not exist, creates the counter.
+  int GetCounterValue(const std::wstring& name);
+
+  // Gets the sum of the values for a particular counter for a given pid.
+  // If the counter does not exist, creates the counter.
+  int GetCounterValue(const std::wstring& name, int pid);
+
+  // The maxinum number of counters/rows in the table.
+  int GetMaxCounters() const;
+
+  // The maxinum number of threads/columns in the table.
+  int GetMaxThreads() const;
+
+  // The maximum length (in characters) of a Thread's name including
+  // null terminator, as stored in the shared memory.
+  static const int kMaxThreadNameLength = 32;
+
+  // The maximum length (in characters) of a Counter's name including
+  // null terminator, as stored in the shared memory.
+  static const int kMaxCounterNameLength = 32;
+
+  // Convenience function to lookup a counter location for a
+  // counter by name for the calling thread.  Will register
+  // the thread if it is not already registered.
+  static int* FindLocation(const wchar_t *name);
+
+ private:
+  // Locates a free slot in the table.  Returns a number > 0 on success,
+  // or 0 on failure.  The caller must hold the shared_memory lock when
+  // calling this function.
+  int FindEmptyThread() const;
+
+  // Locates a counter in the table or finds an empty row.  Returns a
+  // number > 0 on success, or 0 on failure.  The caller must hold the
+  // shared_memory_lock when calling this function.
+  int FindCounterOrEmptyRow(const std::wstring& name) const;
+
+  // Internal function to add a counter to the StatsTable.  Assumes that
+  // the counter does not already exist in the table.
+  //
+  // name is a unique identifier for this counter, and will be truncated
+  // to kMaxCounterNameLength-1 characters.
+  //
+  // On success, returns the counter_id for the newly added counter.
+  // On failure, returns 0.
+  int AddCounter(const std::wstring& name);
+
+  // Get the TLS data for the calling thread.  Returns NULL if none is
+  // initialized.
+  StatsTableTLSData* GetTLSData() const;
+
+  typedef base::hash_map<std::wstring, int> CountersMap;
+
+  bool                opened_;
+  SharedMemory        shared_memory_;
+  StatsTablePrivate*  impl_;
+  // The counters_lock_ protects the counters_ hash table.
+  Lock                counters_lock_;
+  // The counters_ hash map is an in-memory hash of the counters.
+  // It is used for quick lookup of counters, but is cannot be used
+  // as a substitute for what is in the shared memory.  Even though
+  // we don't have a counter in our hash table, another process may
+  // have created it.
+  CountersMap         counters_;
+  TLSSlot             tls_index_;
+
+  static StatsTable*  global_table_;
+  DISALLOW_EVIL_CONSTRUCTORS(StatsTable);
+};
+
+#endif  // BASE_STATS_TABLE_H__
diff --git a/base/stats_table_unittest.cc b/base/stats_table_unittest.cc
new file mode 100644
index 0000000..28527fc
--- /dev/null
+++ b/base/stats_table_unittest.cc
@@ -0,0 +1,396 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <process.h>
+#include <windows.h>
+
+#include "base/multiprocess_test.h"
+#include "base/stats_table.h"
+#include "base/stats_counters.h"
+#include "base/string_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+  class StatsTableTest : public MultiProcessTest {
+  };
+}
+
+// Open a StatsTable and verify that we can write to each of the
+// locations in the table.
+TEST_F(StatsTableTest, VerifySlots) {
+  const std::wstring kTableName = L"VerifySlotsStatTable";
+  const int kMaxThreads = 1;
+  const int kMaxCounter = 5;
+  StatsTable table(kTableName, kMaxThreads, kMaxCounter);
+
+  // Register a single thread.
+  std::wstring thread_name = L"mainThread";
+  int slot_id = table.RegisterThread(thread_name);
+  EXPECT_TRUE(slot_id);
+
+  // Fill up the table with counters.
+  std::wstring counter_base_name = L"counter";
+  for (int index=0; index < kMaxCounter; index++) {
+    std::wstring counter_name = counter_base_name;
+    StringAppendF(&counter_name, L"counter.ctr%d", index);
+    int counter_id = table.FindCounter(counter_name);
+    EXPECT_GT(counter_id, 0);
+  }
+
+  // Try to allocate an additional thread.  Verify it fails.
+  slot_id = table.RegisterThread(L"too many threads");
+  EXPECT_EQ(slot_id, 0);
+
+  // Try to allocate an additional counter.  Verify it fails.
+  int counter_id = table.FindCounter(counter_base_name);
+  EXPECT_EQ(counter_id, 0);
+}
+
+// CounterZero will continually be set to 0.
+const std::wstring kCounterZero = L"CounterZero";
+// Counter1313 will continually be set to 1313.
+const std::wstring kCounter1313 = L"Counter1313";
+// CounterIncrement will be incremented each time.
+const std::wstring kCounterIncrement = L"CounterIncrement";
+// CounterDecrement will be decremented each time.
+const std::wstring kCounterDecrement = L"CounterDecrement";
+// CounterMixed will be incremented by odd numbered threads and
+// decremented by even threads.
+const std::wstring kCounterMixed = L"CounterMixed";
+// The number of thread loops that we will do.
+const int kThreadLoops = 1000;
+
+unsigned __stdcall StatsTableMultipleThreadMain(void* param) {
+  // Each thread will open the shared memory and set counters
+  // concurrently in a loop.  We'll use some pauses to
+  // mixup the thread scheduling.
+  int16 id = reinterpret_cast<int16>(param);
+
+  StatsCounter zero_counter(kCounterZero);
+  StatsCounter lucky13_counter(kCounter1313);
+  StatsCounter increment_counter(kCounterIncrement);
+  StatsCounter decrement_counter(kCounterDecrement);
+  for (int index = 0; index < kThreadLoops; index++) {
+    StatsCounter mixed_counter(kCounterMixed);  // create this one in the loop
+    zero_counter.Set(0);
+    lucky13_counter.Set(1313);
+    increment_counter.Increment();
+    decrement_counter.Decrement();
+    if (id % 2)
+      mixed_counter.Decrement();
+    else
+      mixed_counter.Increment();
+    Sleep(index % 10);   // short wait
+  }
+  return 0;
+}
+// Create a few threads and have them poke on their counters.
+TEST_F(StatsTableTest, MultipleThreads) {
+  // Create a stats table.
+  const std::wstring kTableName = L"MultipleThreadStatTable";
+  const int kMaxThreads = 20;
+  const int kMaxCounter = 5;
+  StatsTable table(kTableName, kMaxThreads, kMaxCounter);
+  StatsTable::set_current(&table);
+
+  EXPECT_EQ(0, table.CountThreadsRegistered());
+
+  // Spin up a set of threads to go bang on the various counters.
+  // After we join the threads, we'll make sure the counters
+  // contain the values we expected.
+  HANDLE threads[kMaxThreads];
+
+  // Spawn the threads.
+  for (int16 index = 0; index < kMaxThreads; index++) {
+    void* argument = reinterpret_cast<void*>(index);
+    unsigned thread_id;
+    threads[index] = reinterpret_cast<HANDLE>(
+      _beginthreadex(NULL, 0, StatsTableMultipleThreadMain, argument, 0,
+        &thread_id));
+    EXPECT_NE((HANDLE)NULL, threads[index]);
+  }
+
+  // Wait for the threads to finish.
+  for (int index = 0; index < kMaxThreads; index++) {
+    DWORD rv = WaitForSingleObject(threads[index], 60 * 1000);
+    EXPECT_EQ(rv, WAIT_OBJECT_0);  // verify all threads finished
+  }
+  StatsCounter zero_counter(kCounterZero);
+  StatsCounter lucky13_counter(kCounter1313);
+  StatsCounter increment_counter(kCounterIncrement);
+  StatsCounter decrement_counter(kCounterDecrement);
+  StatsCounter mixed_counter(kCounterMixed);
+
+  // Verify the various counters are correct.
+  std::wstring name;
+  name = L"c:" + kCounterZero;
+  EXPECT_EQ(0, table.GetCounterValue(name));
+  name = L"c:" + kCounter1313;
+  EXPECT_EQ(1313 * kMaxThreads,
+      table.GetCounterValue(name));
+  name = L"c:" + kCounterIncrement;
+  EXPECT_EQ(kMaxThreads * kThreadLoops,
+      table.GetCounterValue(name));
+  name = L"c:" + kCounterDecrement;
+  EXPECT_EQ(-kMaxThreads * kThreadLoops,
+      table.GetCounterValue(name));
+  name = L"c:" + kCounterMixed;
+  EXPECT_EQ((kMaxThreads % 2) * kThreadLoops,
+      table.GetCounterValue(name));
+  EXPECT_EQ(0, table.CountThreadsRegistered());
+}
+
+const std::wstring kTableName = L"MultipleProcessStatTable";
+
+extern "C" int __declspec(dllexport) ChildProcessMain() {
+  // Each process will open the shared memory and set counters
+  // concurrently in a loop.  We'll use some pauses to
+  // mixup the scheduling.
+
+  StatsTable table(kTableName, 0, 0);
+  StatsTable::set_current(&table);
+  StatsCounter zero_counter(kCounterZero);
+  StatsCounter lucky13_counter(kCounter1313);
+  StatsCounter increment_counter(kCounterIncrement);
+  StatsCounter decrement_counter(kCounterDecrement);
+  for (int index = 0; index < kThreadLoops; index++) {
+    zero_counter.Set(0);
+    lucky13_counter.Set(1313);
+    increment_counter.Increment();
+    decrement_counter.Decrement();
+    Sleep(index % 10);   // short wait
+  }
+  return 0;
+}
+
+// Create a few threads and have them poke on their counters.
+TEST_F(StatsTableTest, MultipleProcesses) {
+  // Create a stats table.
+  const std::wstring kTableName = L"MultipleProcessStatTable";
+  const int kMaxThreads = 20;
+  const int kMaxCounter = 5;
+  StatsTable table(kTableName, kMaxThreads, kMaxCounter);
+  StatsTable::set_current(&table);
+
+  EXPECT_EQ(0, table.CountThreadsRegistered());
+
+  // Spin up a set of threads to go bang on the various counters.
+  // After we join the threads, we'll make sure the counters
+  // contain the values we expected.
+  HANDLE threads[kMaxThreads];
+
+  // Spawn the processes.
+  for (int16 index = 0; index < kMaxThreads; index++) {
+    threads[index] = this->SpawnChild(L"ChildProcessMain");
+    EXPECT_NE((HANDLE)NULL, threads[index]);
+  }
+
+  // Wait for the threads to finish.
+  for (int index = 0; index < kMaxThreads; index++) {
+    DWORD rv = WaitForSingleObject(threads[index], 60 * 1000);
+    EXPECT_EQ(rv, WAIT_OBJECT_0);  // verify all threads finished
+  }
+  StatsCounter zero_counter(kCounterZero);
+  StatsCounter lucky13_counter(kCounter1313);
+  StatsCounter increment_counter(kCounterIncrement);
+  StatsCounter decrement_counter(kCounterDecrement);
+
+  // Verify the various counters are correct.
+  std::wstring name;
+  name = L"c:" + kCounterZero;
+  EXPECT_EQ(0, table.GetCounterValue(name));
+  name = L"c:" + kCounter1313;
+  EXPECT_EQ(1313 * kMaxThreads,
+      table.GetCounterValue(name));
+  name = L"c:" + kCounterIncrement;
+  EXPECT_EQ(kMaxThreads * kThreadLoops,
+      table.GetCounterValue(name));
+  name = L"c:" + kCounterDecrement;
+  EXPECT_EQ(-kMaxThreads * kThreadLoops,
+      table.GetCounterValue(name));
+  EXPECT_EQ(0, table.CountThreadsRegistered());
+}
+
+class MockStatsCounter : public StatsCounter {
+ public:
+  MockStatsCounter(const std::wstring& name)
+      : StatsCounter(name) {}
+  int* Pointer() { return GetPtr(); }
+};
+
+// Test some basic StatsCounter operations
+TEST_F(StatsTableTest, StatsCounter) {
+  // Create a stats table.
+  const std::wstring kTableName = L"StatTable";
+  const int kMaxThreads = 20;
+  const int kMaxCounter = 5;
+  StatsTable table(kTableName, kMaxThreads, kMaxCounter);
+  StatsTable::set_current(&table);
+
+  MockStatsCounter foo(L"foo");
+
+  // Test initial state.
+  EXPECT_TRUE(foo.Enabled());
+  EXPECT_NE(foo.Pointer(), static_cast<int*>(0));
+  EXPECT_EQ(0, table.GetCounterValue(L"c:foo"));
+  EXPECT_EQ(0, *(foo.Pointer()));
+
+  // Test Increment.
+  while(*(foo.Pointer()) < 123) foo.Increment();
+  EXPECT_EQ(123, table.GetCounterValue(L"c:foo"));
+  foo.Add(0);
+  EXPECT_EQ(123, table.GetCounterValue(L"c:foo"));
+  foo.Add(-1);
+  EXPECT_EQ(122, table.GetCounterValue(L"c:foo"));
+
+  // Test Set.
+  foo.Set(0);
+  EXPECT_EQ(0, table.GetCounterValue(L"c:foo"));
+  foo.Set(100);
+  EXPECT_EQ(100, table.GetCounterValue(L"c:foo"));
+  foo.Set(-1);
+  EXPECT_EQ(-1, table.GetCounterValue(L"c:foo"));
+  foo.Set(0);
+  EXPECT_EQ(0, table.GetCounterValue(L"c:foo"));
+
+  // Test Decrement.
+  foo.Decrement(1);
+  EXPECT_EQ(-1, table.GetCounterValue(L"c:foo"));
+  foo.Decrement(0);
+  EXPECT_EQ(-1, table.GetCounterValue(L"c:foo"));
+  foo.Decrement(-1);
+  EXPECT_EQ(0, table.GetCounterValue(L"c:foo"));
+}
+
+class MockStatsCounterTimer : public StatsCounterTimer {
+ public:
+  MockStatsCounterTimer(const std::wstring& name)
+      : StatsCounterTimer(name) {}
+
+  TimeTicks start_time() { return start_time_; }
+  TimeTicks stop_time() { return stop_time_; }
+};
+
+// Test some basic StatsCounterTimer operations
+TEST_F(StatsTableTest, StatsCounterTimer) {
+  // Create a stats table.
+  const std::wstring kTableName = L"StatTable";
+  const int kMaxThreads = 20;
+  const int kMaxCounter = 5;
+  StatsTable table(kTableName, kMaxThreads, kMaxCounter);
+  StatsTable::set_current(&table);
+
+  MockStatsCounterTimer bar(L"bar");
+
+  // Test initial state.
+  EXPECT_FALSE(bar.Running());
+  EXPECT_TRUE(bar.start_time().is_null());
+  EXPECT_TRUE(bar.stop_time().is_null());
+
+  // Do some timing.
+  bar.Start();
+  Sleep(500);
+  bar.Stop();
+  EXPECT_LE(500, table.GetCounterValue(L"t:bar"));
+
+  // Verify that timing again is additive.
+  bar.Start();
+  Sleep(500);
+  bar.Stop();
+  EXPECT_LE(1000, table.GetCounterValue(L"t:bar"));
+}
+
+// Test some basic StatsRate operations
+TEST_F(StatsTableTest, StatsRate) {
+  // Create a stats table.
+  const std::wstring kTableName = L"StatTable";
+  const int kMaxThreads = 20;
+  const int kMaxCounter = 5;
+  StatsTable table(kTableName, kMaxThreads, kMaxCounter);
+  StatsTable::set_current(&table);
+
+  StatsRate baz(L"baz");
+
+  // Test initial state.
+  EXPECT_FALSE(baz.Running());
+  EXPECT_EQ(0, table.GetCounterValue(L"c:baz"));
+  EXPECT_EQ(0, table.GetCounterValue(L"t:baz"));
+
+  // Do some timing.
+  baz.Start();
+  Sleep(500);
+  baz.Stop();
+  EXPECT_EQ(1, table.GetCounterValue(L"c:baz"));
+  EXPECT_LE(500, table.GetCounterValue(L"t:baz"));
+
+  // Verify that timing again is additive.
+  baz.Start();
+  Sleep(500);
+  baz.Stop();
+  EXPECT_EQ(2, table.GetCounterValue(L"c:baz"));
+  EXPECT_LE(1000, table.GetCounterValue(L"t:baz"));
+}
+
+// Test some basic StatsScope operations
+TEST_F(StatsTableTest, StatsScope) {
+  // Create a stats table.
+  const std::wstring kTableName = L"StatTable";
+  const int kMaxThreads = 20;
+  const int kMaxCounter = 5;
+  StatsTable table(kTableName, kMaxThreads, kMaxCounter);
+  StatsTable::set_current(&table);
+
+  StatsCounterTimer foo(L"foo");
+  StatsRate bar(L"bar");
+
+  // Test initial state.
+  EXPECT_EQ(0, table.GetCounterValue(L"t:foo"));
+  EXPECT_EQ(0, table.GetCounterValue(L"t:bar"));
+  EXPECT_EQ(0, table.GetCounterValue(L"c:bar"));
+
+  // Try a scope.
+  {
+    StatsScope<StatsCounterTimer> timer(foo);
+    StatsScope<StatsRate> timer2(bar);
+    Sleep(500);
+  }
+  EXPECT_LE(500, table.GetCounterValue(L"t:foo"));
+  EXPECT_LE(500, table.GetCounterValue(L"t:bar"));
+  EXPECT_EQ(1, table.GetCounterValue(L"c:bar"));
+
+  // Try a second scope.
+  {
+    StatsScope<StatsCounterTimer> timer(foo);
+    StatsScope<StatsRate> timer2(bar);
+    Sleep(500);
+  }
+  EXPECT_LE(1000, table.GetCounterValue(L"t:foo"));
+  EXPECT_LE(1000, table.GetCounterValue(L"t:bar"));
+  EXPECT_EQ(2, table.GetCounterValue(L"c:bar"));
+}
\ No newline at end of file
diff --git a/base/string16.h b/base/string16.h
new file mode 100644
index 0000000..2466028
--- /dev/null
+++ b/base/string16.h
@@ -0,0 +1,217 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+//  1. Redistributions of source code must retain the above copyright notice,
+//     this list of conditions and the following disclaimer.
+//  2. Redistributions in binary form must reproduce the above copyright notice,
+//     this list of conditions and the following disclaimer in the documentation
+//     and/or other materials provided with the distribution.
+//  3. Neither the name of Google Inc. nor the names of its contributors may be
+//     used to endorse or promote products derived from this software without
+//     specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// WHAT:
+// A version of std::basic_string that works even on Linux when 2-byte wchar_t
+// values (-fshort-wchar) are used. You can access this class as std::string16.
+// We also define char16, which std::string16 is based upon.
+//
+// WHY:
+// Firefox uses 2-byte wide characters (UTF-16). On Windows, this is
+// mostly compatible with wchar_t, which is 2 bytes (UCS2).
+//
+// On Linux, sizeof(wchar_t) is 4 bytes by default. We can make it 2 bytes
+// using the GCC flag -fshort-wchar. But then std::wstring fails at run time,
+// because it calls some functions (like wcslen) that come from glibc -- which
+// was built with a 4-byte wchar_t!
+//
+// So we define std::string16, which is similar to std::wstring but replaces
+// all glibc functions with custom, 2-byte-char compatible routines. Fortuntely
+// for us, std::wstring uses mostly *inline* wchar_t-based functions (like
+// wmemcmp) that are defined in .h files and do not need to be overridden.
+
+#ifndef BASE_STRING16_H__
+#define BASE_STRING16_H__
+
+#include <string>
+
+#ifdef WIN32
+
+typedef wchar_t char16;
+
+namespace std {
+  typedef wstring string16;
+}
+
+#else
+
+typedef unsigned short char16;
+
+namespace std {
+  typedef basic_string<char16> string16;
+}
+
+
+// Define char16 versions of functions required below in char_traits<char16>
+extern "C" {
+
+  inline char16 *char16_wmemmove(char16 *s1, const char16 *s2, size_t n) {
+    return (char16 *)memmove(s1, s2, n * sizeof(char16));
+  }
+
+  inline char16 *char16_wmemcpy(char16 *s1, const char16 *s2, size_t n) {
+    return (char16 *)memcpy(s1, s2, n * sizeof(char16));
+  }
+
+  inline int char16_wmemcmp(const char16 *s1, const char16 *s2, size_t n) {
+    // we cannot call memcmp because that changes the semantics.
+    while (n > 0) {
+      if (*s1 != *s2) {
+        // we cannot use (*s1 - *s2) because char16 is unsigned
+        return ((*s1 < *s2) ? -1 : 1);
+      }
+      ++s1; ++s2; --n;
+    }
+    return 0;
+  }
+
+  inline const char16 *char16_wmemchr(const char16 *s, char16 c, size_t n) {
+    while (n > 0) {
+      if (*s == c) {
+        return s;
+      }
+      ++s; --n;
+    }
+    return 0;
+  }
+
+  inline char16 *char16_wmemset(char16 *s, char16 c, size_t n) {
+    char16 *s_orig = s;
+    while (n > 0) {
+      *s = c;
+      ++s; --n;
+    }
+    return s_orig;
+  }
+
+  inline size_t char16_wcslen(const char16 *s) {
+    const char16 *s_orig = s;
+    while (*s) { ++s; }
+    return (s - s_orig);
+  }
+
+} // END: extern "C"
+
+
+// Definition of char_traits<char16>, which enables basic_string<char16>
+//
+// This is a slightly modified version of char_traits<wchar_t> from gcc 3.2.2
+namespace std {
+
+  template<>
+    struct char_traits<char16>
+    {
+      typedef char16 		char_type;
+      typedef wint_t 		int_type;
+      typedef streamoff 	off_type;
+      typedef wstreampos 	pos_type;
+      typedef mbstate_t 	state_type;
+
+      static void
+      assign(char_type& __c1, const char_type& __c2)
+      { __c1 = __c2; }
+
+      static bool
+      eq(const char_type& __c1, const char_type& __c2)
+      { return __c1 == __c2; }
+
+      static bool
+      lt(const char_type& __c1, const char_type& __c2)
+      { return __c1 < __c2; }
+
+      static int
+      compare(const char_type* __s1, const char_type* __s2, size_t __n)
+      { return char16_wmemcmp(__s1, __s2, __n); }
+
+      static size_t
+      length(const char_type* __s)
+      { return char16_wcslen(__s); }
+
+      static const char_type*
+      find(const char_type* __s, size_t __n, const char_type& __a)
+      { return char16_wmemchr(__s, __a, __n); }
+
+      static char_type*
+      move(char_type* __s1, const char_type* __s2, int_type __n)
+      { return char16_wmemmove(__s1, __s2, __n); }
+
+      static char_type*
+      copy(char_type* __s1, const char_type* __s2, size_t __n)
+      { return char16_wmemcpy(__s1, __s2, __n); }
+
+      static char_type*
+      assign(char_type* __s, size_t __n, char_type __a)
+      { return char16_wmemset(__s, __a, __n); }
+
+      static char_type
+      to_char_type(const int_type& __c) { return char_type(__c); }
+
+      static int_type
+      to_int_type(const char_type& __c) { return int_type(__c); }
+
+      static bool
+      eq_int_type(const int_type& __c1, const int_type& __c2)
+      { return __c1 == __c2; }
+
+      static int_type
+      eof() { return static_cast<int_type>(WEOF); }
+
+      static int_type
+      not_eof(const int_type& __c)
+      { return eq_int_type(__c, eof()) ? 0 : __c; }
+  };
+
+} // END: namespace std
+
+#endif // END: WIN32
+
+#endif // END: BASE_STRING16_H__
diff --git a/base/string_escape.cc b/base/string_escape.cc
new file mode 100644
index 0000000..018531e
--- /dev/null
+++ b/base/string_escape.cc
@@ -0,0 +1,122 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/string_escape.h"
+
+#include <string>
+
+#include "base/string_util.h"
+
+namespace string_escape {
+
+// Try to escape |c| as a "SingleEscapeCharacter" (\n, etc).  If successful,
+// returns true and appends the escape sequence to |dst|.
+template<typename CHAR>
+static bool JavascriptSingleEscapeChar(const CHAR c, std::string* dst) {
+  switch (c) {
+    case '\b':
+      dst->append("\\b");
+      break;
+    case '\f':
+      dst->append("\\f");
+      break;
+    case '\n':
+      dst->append("\\n");
+      break;
+    case '\r':
+      dst->append("\\r");
+      break;
+    case '\t':
+      dst->append("\\t");
+      break;
+    case '\v':
+      dst->append("\\v");
+      break;
+    case '\\':
+      dst->append("\\\\");
+      break;
+    case '"':
+      dst->append("\\\"");
+      break;
+    default:
+      return false;
+  }
+  return true;
+}
+
+void JavascriptDoubleQuote(const std::wstring& str,
+                           bool put_in_quotes,
+                           std::string* dst) {
+  if (put_in_quotes)
+    dst->push_back('"');
+
+  for (std::wstring::const_iterator it = str.begin(); it != str.end(); ++it) {
+    wchar_t c = *it;
+    if (!JavascriptSingleEscapeChar(c, dst)) {
+      if (c > 255) {
+        // Non-ascii values need to be unicode dst->
+        // TODO(tc): Some unicode values are handled specially. See
+        // spidermonkey code.
+        StringAppendF(dst, "\\u%04X", c);
+      } else if (c < 32 || c > 126) {
+        // Spidermonkey hex escapes these values.
+        StringAppendF(dst, "\\x%02X", c);
+      } else {
+        dst->push_back(static_cast<char>(c));
+      }
+    }
+  }
+
+  if (put_in_quotes)
+    dst->push_back('"');
+}
+
+void JavascriptDoubleQuote(const std::string& str,
+                           bool put_in_quotes,
+                           std::string* dst) {
+  if (put_in_quotes)
+    dst->push_back('"');
+
+  for (std::string::const_iterator it = str.begin(); it != str.end(); ++it) {
+    unsigned char c = *it;
+    if (!JavascriptSingleEscapeChar(c, dst)) {
+      // Hex encode if the character is non-printable 7bit ascii
+      if (c < 32 || c == 127) {
+        StringAppendF(dst, "\\x%02X", c);
+      } else {
+        dst->push_back(static_cast<char>(c));
+      }
+    }
+  }
+
+  if (put_in_quotes)
+    dst->push_back('"');
+}
+
+}  // namespace string_escape
diff --git a/base/string_escape.h b/base/string_escape.h
new file mode 100644
index 0000000..a78ebe9
--- /dev/null
+++ b/base/string_escape.h
@@ -0,0 +1,60 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// This file defines utility functions for escaping strings.
+
+#ifndef BASE_STRING_ESCAPE_H__
+#define BASE_STRING_ESCAPE_H__
+
+#include <string>
+
+namespace string_escape {
+
+// Escape |str| appropriately for a javascript string litereal, _appending_ the
+// result to |dst|. This will create standard escape sequences (\b, \n),
+// hex escape sequences (\x00), and unicode escape sequences (\uXXXX).
+// If |put_in_quotes| is true, the result will be surrounded in double quotes.
+// The outputted literal, when interpreted by the browser, should result in a
+// javascript string that is identical and the same length as the input |str|.
+void JavascriptDoubleQuote(const std::wstring& str,
+                           bool put_in_quotes,
+                           std::string* dst);
+
+// Similar to the wide version, but for narrow strings.  It will not use
+// \uXXXX unicode escape sequences.  It will pass non-7bit characters directly
+// into the string unencoded, allowing the browser to interpret the encoding.
+// The outputted literal, when interpreted by the browser, could result in a
+// javascript string of a different length than the input |str|.
+void JavascriptDoubleQuote(const std::string& str,
+                           bool put_in_quotes,
+                           std::string* dst);
+
+}  // namespace string_escape
+
+#endif  // BASE_STRING_ESCAPE_H__
diff --git a/base/string_escape_unittest.cc b/base/string_escape_unittest.cc
new file mode 100644
index 0000000..91f15b7
--- /dev/null
+++ b/base/string_escape_unittest.cc
@@ -0,0 +1,79 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "base/string_escape.h"
+
+TEST(StringEscapeTest, JavascriptDoubleQuote) {
+  static const char* kToEscape          = "\b\001aZ\"\\wee";
+  static const char* kEscaped           = "\\b\\x01aZ\\\"\\\\wee";
+  static const char* kEscapedQuoted     = "\"\\b\\x01aZ\\\"\\\\wee\"";
+  static const wchar_t* kUToEscape      = L"\b\x0001" L"a\x123fZ\"\\wee";
+  static const char* kUEscaped          = "\\b\\x01a\\u123FZ\\\"\\\\wee";
+  static const char* kUEscapedQuoted    = "\"\\b\\x01a\\u123FZ\\\"\\\\wee\"";
+
+  std::string out;
+
+  // Test wide unicode escaping
+  out = "testy: ";
+  string_escape::JavascriptDoubleQuote(std::wstring(kUToEscape), false, &out);
+  ASSERT_EQ(std::string("testy: ") + kUEscaped, out);
+
+  out = "testy: ";
+  string_escape::JavascriptDoubleQuote(std::wstring(kUToEscape), true, &out);
+  ASSERT_EQ(std::string("testy: ") + kUEscapedQuoted, out);
+
+  // Test null and high bit / negative unicode values
+  std::wstring wstr(L"TeSt");
+  wstr.push_back(0);
+  wstr.push_back(0xffb1);
+  wstr.push_back(0x00ff);
+
+  out = "testy: ";
+  string_escape::JavascriptDoubleQuote(wstr, false, &out);
+  ASSERT_EQ("testy: TeSt\\x00\\uFFB1\\xFF", out);
+
+  // Test escaping of 7bit ascii
+  out = "testy: ";
+  string_escape::JavascriptDoubleQuote(std::string(kToEscape), false, &out);
+  ASSERT_EQ(std::string("testy: ") + kEscaped, out);
+
+  // Test null, non-printable, and non-7bit
+  std::string str("TeSt");
+  str.push_back(0);
+  str.push_back(15);
+  str.push_back(127);
+  str.push_back(-16);
+  str.push_back(-128);
+  str.push_back('!');
+
+  out = "testy: ";
+  string_escape::JavascriptDoubleQuote(str, false, &out);
+  ASSERT_EQ("testy: TeSt\\x00\\x0F\\x7F\xf0\x80!", out);
+}
diff --git a/base/string_piece.cc b/base/string_piece.cc
new file mode 100644
index 0000000..e2c0aa7
--- /dev/null
+++ b/base/string_piece.cc
@@ -0,0 +1,240 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Copied from strings/stringpiece.cc with modifications
+
+#include <algorithm>
+#include <iostream>
+
+#include "base/string_piece.h"
+
+typedef StringPiece::size_type size_type;
+
+std::ostream& operator<<(std::ostream& o, const StringPiece& piece) {
+  o.write(piece.data(), static_cast<std::streamsize>(piece.size()));
+  return o;
+}
+
+bool operator==(const StringPiece& x, const StringPiece& y) {
+  if (x.size() != y.size())
+    return false;
+
+  return StringPiece::wordmemcmp(x.data(), y.data(), x.size()) == 0;
+}
+
+void StringPiece::CopyToString(std::string* target) const {
+  target->assign(!empty() ? data() : "", size());
+}
+
+void StringPiece::AppendToString(std::string* target) const {
+  if (!empty())
+    target->append(data(), size());
+}
+
+size_type StringPiece::copy(char* buf, size_type n, size_type pos) const {
+  size_type ret = std::min(length_ - pos, n);
+  memcpy(buf, ptr_ + pos, ret);
+  return ret;
+}
+
+size_type StringPiece::find(const StringPiece& s, size_type pos) const {
+  if (pos > length_)
+    return npos;
+
+  const char* result = std::search(ptr_ + pos, ptr_ + length_,
+                                   s.ptr_, s.ptr_ + s.length_);
+  const size_type xpos = result - ptr_;
+  return xpos + s.length_ <= length_ ? xpos : npos;
+}
+
+size_type StringPiece::find(char c, size_type pos) const {
+  if (pos >= length_)
+    return npos;
+
+  const char* result = std::find(ptr_ + pos, ptr_ + length_, c);
+  return result != ptr_ + length_ ? result - ptr_ : npos;
+}
+
+size_type StringPiece::rfind(const StringPiece& s, size_type pos) const {
+  if (length_ < s.length_)
+    return npos;
+
+  if (s.empty())
+    return std::min(length_, pos);
+
+  const char* last = ptr_ + std::min(length_ - s.length_, pos) + s.length_;
+  const char* result = std::find_end(ptr_, last, s.ptr_, s.ptr_ + s.length_);
+  return result != last ? result - ptr_ : npos;
+}
+
+size_type StringPiece::rfind(char c, size_type pos) const {
+  if (length_ == 0)
+    return npos;
+
+  for (size_type i = std::min(pos, length_ - 1); ; --i) {
+    if (ptr_[i] == c)
+      return i;
+    if (i == 0)
+      break;
+  }
+  return npos;
+}
+
+// For each character in characters_wanted, sets the index corresponding
+// to the ASCII code of that character to 1 in table.  This is used by
+// the find_.*_of methods below to tell whether or not a character is in
+// the lookup table in constant time.
+// The argument `table' must be an array that is large enough to hold all
+// the possible values of an unsigned char.  Thus it should be be declared
+// as follows:
+//   bool table[UCHAR_MAX + 1]
+static inline void BuildLookupTable(const StringPiece& characters_wanted,
+                                    bool* table) {
+  const size_type length = characters_wanted.length();
+  const char* const data = characters_wanted.data();
+  for (size_type i = 0; i < length; ++i) {
+    table[static_cast<unsigned char>(data[i])] = true;
+  }
+}
+
+size_type StringPiece::find_first_of(const StringPiece& s,
+                                     size_type pos) const {
+  if (length_ == 0 || s.length_ == 0)
+    return npos;
+
+  // Avoid the cost of BuildLookupTable() for a single-character search.
+  if (s.length_ == 1)
+    return find_first_of(s.ptr_[0], pos);
+
+  bool lookup[UCHAR_MAX + 1] = { false };
+  BuildLookupTable(s, lookup);
+  for (size_type i = pos; i < length_; ++i) {
+    if (lookup[static_cast<unsigned char>(ptr_[i])]) {
+      return i;
+    }
+  }
+  return npos;
+}
+
+size_type StringPiece::find_first_not_of(const StringPiece& s,
+                                         size_type pos) const {
+  if (length_ == 0)
+    return npos;
+
+  if (s.length_ == 0)
+    return 0;
+
+  // Avoid the cost of BuildLookupTable() for a single-character search.
+  if (s.length_ == 1)
+    return find_first_not_of(s.ptr_[0], pos);
+
+  bool lookup[UCHAR_MAX + 1] = { false };
+  BuildLookupTable(s, lookup);
+  for (size_type i = pos; i < length_; ++i) {
+    if (!lookup[static_cast<unsigned char>(ptr_[i])]) {
+      return i;
+    }
+  }
+  return npos;
+}
+
+size_type StringPiece::find_first_not_of(char c, size_type pos) const {
+  if (length_ == 0)
+    return npos;
+
+  for (; pos < length_; ++pos) {
+    if (ptr_[pos] != c) {
+      return pos;
+    }
+  }
+  return npos;
+}
+
+size_type StringPiece::find_last_of(const StringPiece& s, size_type pos) const {
+  if (length_ == 0 || s.length_ == 0)
+    return npos;
+
+  // Avoid the cost of BuildLookupTable() for a single-character search.
+  if (s.length_ == 1)
+    return find_last_of(s.ptr_[0], pos);
+
+  bool lookup[UCHAR_MAX + 1] = { false };
+  BuildLookupTable(s, lookup);
+  for (size_type i = std::min(pos, length_ - 1); ; --i) {
+    if (lookup[static_cast<unsigned char>(ptr_[i])])
+      return i;
+    if (i == 0)
+      break;
+  }
+  return npos;
+}
+
+size_type StringPiece::find_last_not_of(const StringPiece& s,
+                                        size_type pos) const {
+  if (length_ == 0)
+    return npos;
+
+  size_type i = std::min(pos, length_ - 1);
+  if (s.length_ == 0)
+    return i;
+
+  // Avoid the cost of BuildLookupTable() for a single-character search.
+  if (s.length_ == 1)
+    return find_last_not_of(s.ptr_[0], pos);
+
+  bool lookup[UCHAR_MAX + 1] = { false };
+  BuildLookupTable(s, lookup);
+  for (; ; --i) {
+    if (!lookup[static_cast<unsigned char>(ptr_[i])])
+      return i;
+    if (i == 0)
+      break;
+  }
+  return npos;
+}
+
+size_type StringPiece::find_last_not_of(char c, size_type pos) const {
+  if (length_ == 0)
+    return npos;
+
+  for (size_type i = std::min(pos, length_ - 1); ; --i) {
+    if (ptr_[i] != c)
+      return i;
+    if (i == 0)
+      break;
+  }
+  return npos;
+}
+
+StringPiece StringPiece::substr(size_type pos, size_type n) const {
+  if (pos > length_) pos = length_;
+  if (n > length_ - pos) n = length_ - pos;
+  return StringPiece(ptr_ + pos, n);
+}
+
+const StringPiece::size_type StringPiece::npos = size_type(-1);
diff --git a/base/string_piece.h b/base/string_piece.h
new file mode 100644
index 0000000..8cb375e
--- /dev/null
+++ b/base/string_piece.h
@@ -0,0 +1,209 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Copied from strings/stringpiece.h with modifications
+//
+// A string-like object that points to a sized piece of memory.
+//
+// Functions or methods may use const StringPiece& parameters to accept either
+// a "const char*" or a "string" value that will be implicitly converted to
+// a StringPiece.  The implicit conversion means that it is often appropriate
+// to include this .h file in other files rather than forward-declaring
+// StringPiece as would be appropriate for most other Google classes.
+//
+// Systematic usage of StringPiece is encouraged as it will reduce unnecessary
+// conversions from "const char*" to "string" and back again.
+//
+
+#ifndef BASE_STRING_PIECE_H__
+#define BASE_STRING_PIECE_H__
+
+#include <algorithm>
+#include <iosfwd>
+#include <string>
+
+#include "base/basictypes.h"
+
+class StringPiece {
+ public:
+  typedef size_t size_type;
+
+ private:
+  const char*   ptr_;
+  size_type     length_;
+
+ public:
+  // We provide non-explicit singleton constructors so users can pass
+  // in a "const char*" or a "string" wherever a "StringPiece" is
+  // expected.
+  StringPiece() : ptr_(NULL), length_(0) { }
+  StringPiece(const char* str)
+    : ptr_(str), length_((str == NULL) ? 0 : strlen(str)) { }
+  StringPiece(const std::string& str)
+    : ptr_(str.data()), length_(str.size()) { }
+  StringPiece(const char* offset, size_type len)
+    : ptr_(offset), length_(len) { }
+
+  // data() may return a pointer to a buffer with embedded NULs, and the
+  // returned buffer may or may not be null terminated.  Therefore it is
+  // typically a mistake to pass data() to a routine that expects a NUL
+  // terminated string.
+  const char* data() const { return ptr_; }
+  size_type size() const { return length_; }
+  size_type length() const { return length_; }
+  bool empty() const { return length_ == 0; }
+
+  void clear() { ptr_ = NULL; length_ = 0; }
+  void set(const char* data, size_type len) { ptr_ = data; length_ = len; }
+  void set(const char* str) {
+    ptr_ = str;
+    length_ = str ? strlen(str) : 0;
+  }
+  void set(const void* data, size_type len) {
+    ptr_ = reinterpret_cast<const char*>(data);
+    length_ = len;
+  }
+
+  char operator[](size_type i) const { return ptr_[i]; }
+
+  void remove_prefix(size_type n) {
+    ptr_ += n;
+    length_ -= n;
+  }
+
+  void remove_suffix(size_type n) {
+    length_ -= n;
+  }
+
+  int compare(const StringPiece& x) const {
+    int r = wordmemcmp(ptr_, x.ptr_, std::min(length_, x.length_));
+    if (r == 0) {
+      if (length_ < x.length_) r = -1;
+      else if (length_ > x.length_) r = +1;
+    }
+    return r;
+  }
+
+  std::string as_string() const {
+    // std::string doesn't like to take a NULL pointer even with a 0 size.
+    return std::string(!empty() ? data() : "", size());
+  }
+
+  void CopyToString(std::string* target) const;
+  void AppendToString(std::string* target) const;
+
+  // Does "this" start with "x"
+  bool starts_with(const StringPiece& x) const {
+    return ((length_ >= x.length_) &&
+            (wordmemcmp(ptr_, x.ptr_, x.length_) == 0));
+  }
+
+  // Does "this" end with "x"
+  bool ends_with(const StringPiece& x) const {
+    return ((length_ >= x.length_) &&
+            (wordmemcmp(ptr_ + (length_-x.length_), x.ptr_, x.length_) == 0));
+  }
+
+  // standard STL container boilerplate
+  typedef char value_type;
+  typedef const char* pointer;
+  typedef const char& reference;
+  typedef const char& const_reference;
+  typedef ptrdiff_t difference_type;
+  static const size_type npos;
+  typedef const char* const_iterator;
+  typedef const char* iterator;
+  typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+  typedef std::reverse_iterator<iterator> reverse_iterator;
+  iterator begin() const { return ptr_; }
+  iterator end() const { return ptr_ + length_; }
+  const_reverse_iterator rbegin() const {
+    return const_reverse_iterator(ptr_ + length_);
+  }
+  const_reverse_iterator rend() const {
+    return const_reverse_iterator(ptr_);
+  }
+
+  size_type max_size() const { return length_; }
+  size_type capacity() const { return length_; }
+
+  size_type copy(char* buf, size_type n, size_type pos = 0) const;
+
+  size_type find(const StringPiece& s, size_type pos = 0) const;
+  size_type find(char c, size_type pos = 0) const;
+  size_type rfind(const StringPiece& s, size_type pos = npos) const;
+  size_type rfind(char c, size_type pos = npos) const;
+
+  size_type find_first_of(const StringPiece& s, size_type pos = 0) const;
+  size_type find_first_of(char c, size_type pos = 0) const {
+    return find(c, pos);
+  }
+  size_type find_first_not_of(const StringPiece& s, size_type pos = 0) const;
+  size_type find_first_not_of(char c, size_type pos = 0) const;
+  size_type find_last_of(const StringPiece& s, size_type pos = npos) const;
+  size_type find_last_of(char c, size_type pos = npos) const {
+    return rfind(c, pos);
+  }
+  size_type find_last_not_of(const StringPiece& s, size_type pos = npos) const;
+  size_type find_last_not_of(char c, size_type pos = npos) const;
+
+  StringPiece substr(size_type pos, size_type n = npos) const;
+
+  static int wordmemcmp(const char* p, const char* p2, size_type N) {
+    return memcmp(p, p2, N);
+  }
+};
+
+bool operator==(const StringPiece& x, const StringPiece& y);
+
+inline bool operator!=(const StringPiece& x, const StringPiece& y) {
+  return !(x == y);
+}
+
+inline bool operator<(const StringPiece& x, const StringPiece& y) {
+  const int r = StringPiece::wordmemcmp(x.data(), y.data(),
+                                        std::min(x.size(), y.size()));
+  return ((r < 0) || ((r == 0) && (x.size() < y.size())));
+}
+
+inline bool operator>(const StringPiece& x, const StringPiece& y) {
+  return y < x;
+}
+
+inline bool operator<=(const StringPiece& x, const StringPiece& y) {
+  return !(x > y);
+}
+
+inline bool operator>=(const StringPiece& x, const StringPiece& y) {
+  return !(x < y);
+}
+
+// allow StringPiece to be logged (needed for unit testing).
+extern std::ostream& operator<<(std::ostream& o, const StringPiece& piece);
+
+#endif  // BASE_STRING_PIECE_H__
diff --git a/base/string_piece_unittest.cc b/base/string_piece_unittest.cc
new file mode 100644
index 0000000..af8f6f4
--- /dev/null
+++ b/base/string_piece_unittest.cc
@@ -0,0 +1,565 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <string>
+
+#include "base/string_piece.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(StringPieceTest, CheckComparisonOperators) {
+#define CMP_Y(op, x, y)                                               \
+  ASSERT_TRUE( (StringPiece((x)) op StringPiece((y))));               \
+  ASSERT_TRUE( (StringPiece((x)).compare(StringPiece((y))) op 0))
+
+#define CMP_N(op, x, y)                                          \
+  ASSERT_FALSE(StringPiece((x)) op StringPiece((y)));               \
+  ASSERT_FALSE(StringPiece((x)).compare(StringPiece((y))) op 0)
+
+  CMP_Y(==, "",   "");
+  CMP_Y(==, "a",  "a");
+  CMP_Y(==, "aa", "aa");
+  CMP_N(==, "a",  "");
+  CMP_N(==, "",   "a");
+  CMP_N(==, "a",  "b");
+  CMP_N(==, "a",  "aa");
+  CMP_N(==, "aa", "a");
+
+  CMP_N(!=, "",   "");
+  CMP_N(!=, "a",  "a");
+  CMP_N(!=, "aa", "aa");
+  CMP_Y(!=, "a",  "");
+  CMP_Y(!=, "",   "a");
+  CMP_Y(!=, "a",  "b");
+  CMP_Y(!=, "a",  "aa");
+  CMP_Y(!=, "aa", "a");
+
+  CMP_Y(<, "a",  "b");
+  CMP_Y(<, "a",  "aa");
+  CMP_Y(<, "aa", "b");
+  CMP_Y(<, "aa", "bb");
+  CMP_N(<, "a",  "a");
+  CMP_N(<, "b",  "a");
+  CMP_N(<, "aa", "a");
+  CMP_N(<, "b",  "aa");
+  CMP_N(<, "bb", "aa");
+
+  CMP_Y(<=, "a",  "a");
+  CMP_Y(<=, "a",  "b");
+  CMP_Y(<=, "a",  "aa");
+  CMP_Y(<=, "aa", "b");
+  CMP_Y(<=, "aa", "bb");
+  CMP_N(<=, "b",  "a");
+  CMP_N(<=, "aa", "a");
+  CMP_N(<=, "b",  "aa");
+  CMP_N(<=, "bb", "aa");
+
+  CMP_N(>=, "a",  "b");
+  CMP_N(>=, "a",  "aa");
+  CMP_N(>=, "aa", "b");
+  CMP_N(>=, "aa", "bb");
+  CMP_Y(>=, "a",  "a");
+  CMP_Y(>=, "b",  "a");
+  CMP_Y(>=, "aa", "a");
+  CMP_Y(>=, "b",  "aa");
+  CMP_Y(>=, "bb", "aa");
+
+  CMP_N(>, "a",  "a");
+  CMP_N(>, "a",  "b");
+  CMP_N(>, "a",  "aa");
+  CMP_N(>, "aa", "b");
+  CMP_N(>, "aa", "bb");
+  CMP_Y(>, "b",  "a");
+  CMP_Y(>, "aa", "a");
+  CMP_Y(>, "b",  "aa");
+  CMP_Y(>, "bb", "aa");
+
+  std::string x;
+  for (int i = 0; i < 256; i++) {
+    x += 'a';
+    std::string y = x;
+    CMP_Y(==, x, y);
+    for (int j = 0; j < i; j++) {
+      std::string z = x;
+      z[j] = 'b';       // Differs in position 'j'
+      CMP_N(==, x, z);
+    }
+  }
+
+#undef CMP_Y
+#undef CMP_N
+}
+
+TEST(StringPieceTest, CheckSTL) {
+  StringPiece a("abcdefghijklmnopqrstuvwxyz");
+  StringPiece b("abc");
+  StringPiece c("xyz");
+  StringPiece d("foobar");
+  StringPiece e;
+  std::string temp("123");
+  temp += '\0';
+  temp += "456";
+  StringPiece f(temp);
+
+  ASSERT_EQ(a[6], 'g');
+  ASSERT_EQ(b[0], 'a');
+  ASSERT_EQ(c[2], 'z');
+  ASSERT_EQ(f[3], '\0');
+  ASSERT_EQ(f[5], '5');
+
+  ASSERT_EQ(*d.data(), 'f');
+  ASSERT_EQ(d.data()[5], 'r');
+  ASSERT_TRUE(e.data() == NULL);
+
+  ASSERT_EQ(*a.begin(), 'a');
+  ASSERT_EQ(*(b.begin() + 2), 'c');
+  ASSERT_EQ(*(c.end() - 1), 'z');
+
+  ASSERT_EQ(*a.rbegin(), 'z');
+  ASSERT_EQ(*(b.rbegin() + 2), 'a');
+  ASSERT_EQ(*(c.rend() - 1), 'x');
+  ASSERT_TRUE(a.rbegin() + 26 == a.rend());
+
+  ASSERT_EQ(a.size(), 26);
+  ASSERT_EQ(b.size(), 3);
+  ASSERT_EQ(c.size(), 3);
+  ASSERT_EQ(d.size(), 6);
+  ASSERT_EQ(e.size(), 0);
+  ASSERT_EQ(f.size(), 7);
+
+  ASSERT_TRUE(!d.empty());
+  ASSERT_TRUE(d.begin() != d.end());
+  ASSERT_TRUE(d.begin() + 6 == d.end());
+
+  ASSERT_TRUE(e.empty());
+  ASSERT_TRUE(e.begin() == e.end());
+
+  d.clear();
+  ASSERT_EQ(d.size(), 0);
+  ASSERT_TRUE(d.empty());
+  ASSERT_TRUE(d.data() == NULL);
+  ASSERT_TRUE(d.begin() == d.end());
+
+  ASSERT_GE(a.max_size(), a.capacity());
+  ASSERT_GE(a.capacity(), a.size());
+
+  char buf[4] = { '%', '%', '%', '%' };
+  ASSERT_EQ(a.copy(buf, 4), 4);
+  ASSERT_EQ(buf[0], a[0]);
+  ASSERT_EQ(buf[1], a[1]);
+  ASSERT_EQ(buf[2], a[2]);
+  ASSERT_EQ(buf[3], a[3]);
+  ASSERT_EQ(a.copy(buf, 3, 7), 3);
+  ASSERT_EQ(buf[0], a[7]);
+  ASSERT_EQ(buf[1], a[8]);
+  ASSERT_EQ(buf[2], a[9]);
+  ASSERT_EQ(buf[3], a[3]);
+  ASSERT_EQ(c.copy(buf, 99), 3);
+  ASSERT_EQ(buf[0], c[0]);
+  ASSERT_EQ(buf[1], c[1]);
+  ASSERT_EQ(buf[2], c[2]);
+  ASSERT_EQ(buf[3], a[3]);
+
+  ASSERT_EQ(StringPiece::npos, std::string::npos);
+
+  ASSERT_EQ(a.find(b), 0);
+  ASSERT_EQ(a.find(b, 1), StringPiece::npos);
+  ASSERT_EQ(a.find(c), 23);
+  ASSERT_EQ(a.find(c, 9), 23);
+  ASSERT_EQ(a.find(c, StringPiece::npos), StringPiece::npos);
+  ASSERT_EQ(b.find(c), StringPiece::npos);
+  ASSERT_EQ(b.find(c, StringPiece::npos), StringPiece::npos);
+  ASSERT_EQ(a.find(d), 0);
+  ASSERT_EQ(a.find(e), 0);
+  ASSERT_EQ(a.find(d, 12), 12);
+  ASSERT_EQ(a.find(e, 17), 17);
+  StringPiece g("xx not found bb");
+  ASSERT_EQ(a.find(g), StringPiece::npos);
+  // empty string nonsense
+  ASSERT_EQ(d.find(b), StringPiece::npos);
+  ASSERT_EQ(e.find(b), StringPiece::npos);
+  ASSERT_EQ(d.find(b, 4), StringPiece::npos);
+  ASSERT_EQ(e.find(b, 7), StringPiece::npos);
+
+  size_t empty_search_pos = std::string().find(std::string());
+  ASSERT_EQ(d.find(d), empty_search_pos);
+  ASSERT_EQ(d.find(e), empty_search_pos);
+  ASSERT_EQ(e.find(d), empty_search_pos);
+  ASSERT_EQ(e.find(e), empty_search_pos);
+  ASSERT_EQ(d.find(d, 4), std::string().find(std::string(), 4));
+  ASSERT_EQ(d.find(e, 4), std::string().find(std::string(), 4));
+  ASSERT_EQ(e.find(d, 4), std::string().find(std::string(), 4));
+  ASSERT_EQ(e.find(e, 4), std::string().find(std::string(), 4));
+
+  ASSERT_EQ(a.find('a'), 0);
+  ASSERT_EQ(a.find('c'), 2);
+  ASSERT_EQ(a.find('z'), 25);
+  ASSERT_EQ(a.find('$'), StringPiece::npos);
+  ASSERT_EQ(a.find('\0'), StringPiece::npos);
+  ASSERT_EQ(f.find('\0'), 3);
+  ASSERT_EQ(f.find('3'), 2);
+  ASSERT_EQ(f.find('5'), 5);
+  ASSERT_EQ(g.find('o'), 4);
+  ASSERT_EQ(g.find('o', 4), 4);
+  ASSERT_EQ(g.find('o', 5), 8);
+  ASSERT_EQ(a.find('b', 5), StringPiece::npos);
+  // empty string nonsense
+  ASSERT_EQ(d.find('\0'), StringPiece::npos);
+  ASSERT_EQ(e.find('\0'), StringPiece::npos);
+  ASSERT_EQ(d.find('\0', 4), StringPiece::npos);
+  ASSERT_EQ(e.find('\0', 7), StringPiece::npos);
+  ASSERT_EQ(d.find('x'), StringPiece::npos);
+  ASSERT_EQ(e.find('x'), StringPiece::npos);
+  ASSERT_EQ(d.find('x', 4), StringPiece::npos);
+  ASSERT_EQ(e.find('x', 7), StringPiece::npos);
+
+  ASSERT_EQ(a.rfind(b), 0);
+  ASSERT_EQ(a.rfind(b, 1), 0);
+  ASSERT_EQ(a.rfind(c), 23);
+  ASSERT_EQ(a.rfind(c, 22), StringPiece::npos);
+  ASSERT_EQ(a.rfind(c, 1), StringPiece::npos);
+  ASSERT_EQ(a.rfind(c, 0), StringPiece::npos);
+  ASSERT_EQ(b.rfind(c), StringPiece::npos);
+  ASSERT_EQ(b.rfind(c, 0), StringPiece::npos);
+  ASSERT_EQ(a.rfind(d), a.as_string().rfind(std::string()));
+  ASSERT_EQ(a.rfind(e), a.as_string().rfind(std::string()));
+  ASSERT_EQ(a.rfind(d, 12), 12);
+  ASSERT_EQ(a.rfind(e, 17), 17);
+  ASSERT_EQ(a.rfind(g), StringPiece::npos);
+  ASSERT_EQ(d.rfind(b), StringPiece::npos);
+  ASSERT_EQ(e.rfind(b), StringPiece::npos);
+  ASSERT_EQ(d.rfind(b, 4), StringPiece::npos);
+  ASSERT_EQ(e.rfind(b, 7), StringPiece::npos);
+  // empty string nonsense
+  ASSERT_EQ(d.rfind(d, 4), std::string().rfind(std::string()));
+  ASSERT_EQ(e.rfind(d, 7), std::string().rfind(std::string()));
+  ASSERT_EQ(d.rfind(e, 4), std::string().rfind(std::string()));
+  ASSERT_EQ(e.rfind(e, 7), std::string().rfind(std::string()));
+  ASSERT_EQ(d.rfind(d), std::string().rfind(std::string()));
+  ASSERT_EQ(e.rfind(d), std::string().rfind(std::string()));
+  ASSERT_EQ(d.rfind(e), std::string().rfind(std::string()));
+  ASSERT_EQ(e.rfind(e), std::string().rfind(std::string()));
+
+  ASSERT_EQ(g.rfind('o'), 8);
+  ASSERT_EQ(g.rfind('q'), StringPiece::npos);
+  ASSERT_EQ(g.rfind('o', 8), 8);
+  ASSERT_EQ(g.rfind('o', 7), 4);
+  ASSERT_EQ(g.rfind('o', 3), StringPiece::npos);
+  ASSERT_EQ(f.rfind('\0'), 3);
+  ASSERT_EQ(f.rfind('\0', 12), 3);
+  ASSERT_EQ(f.rfind('3'), 2);
+  ASSERT_EQ(f.rfind('5'), 5);
+  // empty string nonsense
+  ASSERT_EQ(d.rfind('o'), StringPiece::npos);
+  ASSERT_EQ(e.rfind('o'), StringPiece::npos);
+  ASSERT_EQ(d.rfind('o', 4), StringPiece::npos);
+  ASSERT_EQ(e.rfind('o', 7), StringPiece::npos);
+
+  ASSERT_EQ(a.find_first_of(b), 0);
+  ASSERT_EQ(a.find_first_of(b, 0), 0);
+  ASSERT_EQ(a.find_first_of(b, 1), 1);
+  ASSERT_EQ(a.find_first_of(b, 2), 2);
+  ASSERT_EQ(a.find_first_of(b, 3), StringPiece::npos);
+  ASSERT_EQ(a.find_first_of(c), 23);
+  ASSERT_EQ(a.find_first_of(c, 23), 23);
+  ASSERT_EQ(a.find_first_of(c, 24), 24);
+  ASSERT_EQ(a.find_first_of(c, 25), 25);
+  ASSERT_EQ(a.find_first_of(c, 26), StringPiece::npos);
+  ASSERT_EQ(g.find_first_of(b), 13);
+  ASSERT_EQ(g.find_first_of(c), 0);
+  ASSERT_EQ(a.find_first_of(f), StringPiece::npos);
+  ASSERT_EQ(f.find_first_of(a), StringPiece::npos);
+  // empty string nonsense
+  ASSERT_EQ(a.find_first_of(d), StringPiece::npos);
+  ASSERT_EQ(a.find_first_of(e), StringPiece::npos);
+  ASSERT_EQ(d.find_first_of(b), StringPiece::npos);
+  ASSERT_EQ(e.find_first_of(b), StringPiece::npos);
+  ASSERT_EQ(d.find_first_of(d), StringPiece::npos);
+  ASSERT_EQ(e.find_first_of(d), StringPiece::npos);
+  ASSERT_EQ(d.find_first_of(e), StringPiece::npos);
+  ASSERT_EQ(e.find_first_of(e), StringPiece::npos);
+
+  ASSERT_EQ(a.find_first_not_of(b), 3);
+  ASSERT_EQ(a.find_first_not_of(c), 0);
+  ASSERT_EQ(b.find_first_not_of(a), StringPiece::npos);
+  ASSERT_EQ(c.find_first_not_of(a), StringPiece::npos);
+  ASSERT_EQ(f.find_first_not_of(a), 0);
+  ASSERT_EQ(a.find_first_not_of(f), 0);
+  ASSERT_EQ(a.find_first_not_of(d), 0);
+  ASSERT_EQ(a.find_first_not_of(e), 0);
+  // empty string nonsense
+  ASSERT_EQ(d.find_first_not_of(a), StringPiece::npos);
+  ASSERT_EQ(e.find_first_not_of(a), StringPiece::npos);
+  ASSERT_EQ(d.find_first_not_of(d), StringPiece::npos);
+  ASSERT_EQ(e.find_first_not_of(d), StringPiece::npos);
+  ASSERT_EQ(d.find_first_not_of(e), StringPiece::npos);
+  ASSERT_EQ(e.find_first_not_of(e), StringPiece::npos);
+
+  StringPiece h("====");
+  ASSERT_EQ(h.find_first_not_of('='), StringPiece::npos);
+  ASSERT_EQ(h.find_first_not_of('=', 3), StringPiece::npos);
+  ASSERT_EQ(h.find_first_not_of('\0'), 0);
+  ASSERT_EQ(g.find_first_not_of('x'), 2);
+  ASSERT_EQ(f.find_first_not_of('\0'), 0);
+  ASSERT_EQ(f.find_first_not_of('\0', 3), 4);
+  ASSERT_EQ(f.find_first_not_of('\0', 2), 2);
+  // empty string nonsense
+  ASSERT_EQ(d.find_first_not_of('x'), StringPiece::npos);
+  ASSERT_EQ(e.find_first_not_of('x'), StringPiece::npos);
+  ASSERT_EQ(d.find_first_not_of('\0'), StringPiece::npos);
+  ASSERT_EQ(e.find_first_not_of('\0'), StringPiece::npos);
+
+  //  StringPiece g("xx not found bb");
+  StringPiece i("56");
+  ASSERT_EQ(h.find_last_of(a), StringPiece::npos);
+  ASSERT_EQ(g.find_last_of(a), g.size()-1);
+  ASSERT_EQ(a.find_last_of(b), 2);
+  ASSERT_EQ(a.find_last_of(c), a.size()-1);
+  ASSERT_EQ(f.find_last_of(i), 6);
+  ASSERT_EQ(a.find_last_of('a'), 0);
+  ASSERT_EQ(a.find_last_of('b'), 1);
+  ASSERT_EQ(a.find_last_of('z'), 25);
+  ASSERT_EQ(a.find_last_of('a', 5), 0);
+  ASSERT_EQ(a.find_last_of('b', 5), 1);
+  ASSERT_EQ(a.find_last_of('b', 0), StringPiece::npos);
+  ASSERT_EQ(a.find_last_of('z', 25), 25);
+  ASSERT_EQ(a.find_last_of('z', 24), StringPiece::npos);
+  ASSERT_EQ(f.find_last_of(i, 5), 5);
+  ASSERT_EQ(f.find_last_of(i, 6), 6);
+  ASSERT_EQ(f.find_last_of(a, 4), StringPiece::npos);
+  // empty string nonsense
+  ASSERT_EQ(f.find_last_of(d), StringPiece::npos);
+  ASSERT_EQ(f.find_last_of(e), StringPiece::npos);
+  ASSERT_EQ(f.find_last_of(d, 4), StringPiece::npos);
+  ASSERT_EQ(f.find_last_of(e, 4), StringPiece::npos);
+  ASSERT_EQ(d.find_last_of(d), StringPiece::npos);
+  ASSERT_EQ(d.find_last_of(e), StringPiece::npos);
+  ASSERT_EQ(e.find_last_of(d), StringPiece::npos);
+  ASSERT_EQ(e.find_last_of(e), StringPiece::npos);
+  ASSERT_EQ(d.find_last_of(f), StringPiece::npos);
+  ASSERT_EQ(e.find_last_of(f), StringPiece::npos);
+  ASSERT_EQ(d.find_last_of(d, 4), StringPiece::npos);
+  ASSERT_EQ(d.find_last_of(e, 4), StringPiece::npos);
+  ASSERT_EQ(e.find_last_of(d, 4), StringPiece::npos);
+  ASSERT_EQ(e.find_last_of(e, 4), StringPiece::npos);
+  ASSERT_EQ(d.find_last_of(f, 4), StringPiece::npos);
+  ASSERT_EQ(e.find_last_of(f, 4), StringPiece::npos);
+
+  ASSERT_EQ(a.find_last_not_of(b), a.size()-1);
+  ASSERT_EQ(a.find_last_not_of(c), 22);
+  ASSERT_EQ(b.find_last_not_of(a), StringPiece::npos);
+  ASSERT_EQ(b.find_last_not_of(b), StringPiece::npos);
+  ASSERT_EQ(f.find_last_not_of(i), 4);
+  ASSERT_EQ(a.find_last_not_of(c, 24), 22);
+  ASSERT_EQ(a.find_last_not_of(b, 3), 3);
+  ASSERT_EQ(a.find_last_not_of(b, 2), StringPiece::npos);
+  // empty string nonsense
+  ASSERT_EQ(f.find_last_not_of(d), f.size()-1);
+  ASSERT_EQ(f.find_last_not_of(e), f.size()-1);
+  ASSERT_EQ(f.find_last_not_of(d, 4), 4);
+  ASSERT_EQ(f.find_last_not_of(e, 4), 4);
+  ASSERT_EQ(d.find_last_not_of(d), StringPiece::npos);
+  ASSERT_EQ(d.find_last_not_of(e), StringPiece::npos);
+  ASSERT_EQ(e.find_last_not_of(d), StringPiece::npos);
+  ASSERT_EQ(e.find_last_not_of(e), StringPiece::npos);
+  ASSERT_EQ(d.find_last_not_of(f), StringPiece::npos);
+  ASSERT_EQ(e.find_last_not_of(f), StringPiece::npos);
+  ASSERT_EQ(d.find_last_not_of(d, 4), StringPiece::npos);
+  ASSERT_EQ(d.find_last_not_of(e, 4), StringPiece::npos);
+  ASSERT_EQ(e.find_last_not_of(d, 4), StringPiece::npos);
+  ASSERT_EQ(e.find_last_not_of(e, 4), StringPiece::npos);
+  ASSERT_EQ(d.find_last_not_of(f, 4), StringPiece::npos);
+  ASSERT_EQ(e.find_last_not_of(f, 4), StringPiece::npos);
+
+  ASSERT_EQ(h.find_last_not_of('x'), h.size() - 1);
+  ASSERT_EQ(h.find_last_not_of('='), StringPiece::npos);
+  ASSERT_EQ(b.find_last_not_of('c'), 1);
+  ASSERT_EQ(h.find_last_not_of('x', 2), 2);
+  ASSERT_EQ(h.find_last_not_of('=', 2), StringPiece::npos);
+  ASSERT_EQ(b.find_last_not_of('b', 1), 0);
+  // empty string nonsense
+  ASSERT_EQ(d.find_last_not_of('x'), StringPiece::npos);
+  ASSERT_EQ(e.find_last_not_of('x'), StringPiece::npos);
+  ASSERT_EQ(d.find_last_not_of('\0'), StringPiece::npos);
+  ASSERT_EQ(e.find_last_not_of('\0'), StringPiece::npos);
+
+  ASSERT_EQ(a.substr(0, 3), b);
+  ASSERT_EQ(a.substr(23), c);
+  ASSERT_EQ(a.substr(23, 3), c);
+  ASSERT_EQ(a.substr(23, 99), c);
+  ASSERT_EQ(a.substr(0), a);
+  ASSERT_EQ(a.substr(3, 2), "de");
+  // empty string nonsense
+  ASSERT_EQ(a.substr(99, 2), e);
+  ASSERT_EQ(d.substr(99), e);
+  ASSERT_EQ(d.substr(0, 99), e);
+  ASSERT_EQ(d.substr(99, 99), e);
+}
+
+TEST(StringPieceTest, CheckCustom) {
+  StringPiece a("foobar");
+  std::string s1("123");
+  s1 += '\0';
+  s1 += "456";
+  StringPiece b(s1);
+  StringPiece e;
+  std::string s2;
+
+  // CopyToString
+  a.CopyToString(&s2);
+  ASSERT_EQ(s2.size(), 6);
+  ASSERT_EQ(s2, "foobar");
+  b.CopyToString(&s2);
+  ASSERT_EQ(s2.size(), 7);
+  ASSERT_EQ(s1, s2);
+  e.CopyToString(&s2);
+  ASSERT_TRUE(s2.empty());
+
+  // AppendToString
+  s2.erase();
+  a.AppendToString(&s2);
+  ASSERT_EQ(s2.size(), 6);
+  ASSERT_EQ(s2, "foobar");
+  a.AppendToString(&s2);
+  ASSERT_EQ(s2.size(), 12);
+  ASSERT_EQ(s2, "foobarfoobar");
+
+  // starts_with
+  ASSERT_TRUE(a.starts_with(a));
+  ASSERT_TRUE(a.starts_with("foo"));
+  ASSERT_TRUE(a.starts_with(e));
+  ASSERT_TRUE(b.starts_with(s1));
+  ASSERT_TRUE(b.starts_with(b));
+  ASSERT_TRUE(b.starts_with(e));
+  ASSERT_TRUE(e.starts_with(""));
+  ASSERT_TRUE(!a.starts_with(b));
+  ASSERT_TRUE(!b.starts_with(a));
+  ASSERT_TRUE(!e.starts_with(a));
+
+  // ends with
+  ASSERT_TRUE(a.ends_with(a));
+  ASSERT_TRUE(a.ends_with("bar"));
+  ASSERT_TRUE(a.ends_with(e));
+  ASSERT_TRUE(b.ends_with(s1));
+  ASSERT_TRUE(b.ends_with(b));
+  ASSERT_TRUE(b.ends_with(e));
+  ASSERT_TRUE(e.ends_with(""));
+  ASSERT_TRUE(!a.ends_with(b));
+  ASSERT_TRUE(!b.ends_with(a));
+  ASSERT_TRUE(!e.ends_with(a));
+
+  // remove_prefix
+  StringPiece c(a);
+  c.remove_prefix(3);
+  ASSERT_EQ(c, "bar");
+  c = a;
+  c.remove_prefix(0);
+  ASSERT_EQ(c, a);
+  c.remove_prefix(c.size());
+  ASSERT_EQ(c, e);
+
+  // remove_suffix
+  c = a;
+  c.remove_suffix(3);
+  ASSERT_EQ(c, "foo");
+  c = a;
+  c.remove_suffix(0);
+  ASSERT_EQ(c, a);
+  c.remove_suffix(c.size());
+  ASSERT_EQ(c, e);
+
+  // set
+  c.set("foobar", 6);
+  ASSERT_EQ(c, a);
+  c.set("foobar", 0);
+  ASSERT_EQ(c, e);
+  c.set("foobar", 7);
+  ASSERT_NE(c, a);
+
+  c.set("foobar");
+  ASSERT_EQ(c, a);
+
+  c.set(static_cast<const void*>("foobar"), 6);
+  ASSERT_EQ(c, a);
+  c.set(static_cast<const void*>("foobar"), 0);
+  ASSERT_EQ(c, e);
+  c.set(static_cast<const void*>("foobar"), 7);
+  ASSERT_NE(c, a);
+
+  // as_string
+  std::string s3(a.as_string().c_str(), 7);
+  ASSERT_EQ(c, s3);
+  std::string s4(e.as_string());
+  ASSERT_TRUE(s4.empty());
+}
+
+TEST(StringPieceTest, CheckNULL) {
+  // we used to crash here, but now we don't.
+  StringPiece s(NULL);
+  ASSERT_EQ(s.data(), (const char*)NULL);
+  ASSERT_EQ(s.size(), 0);
+
+  s.set(NULL);
+  ASSERT_EQ(s.data(), (const char*)NULL);
+  ASSERT_EQ(s.size(), 0);
+}
+
+TEST(StringPieceTest, CheckComparisons2) {
+  StringPiece abc("abcdefghijklmnopqrstuvwxyz");
+
+  // check comparison operations on strings longer than 4 bytes.
+  ASSERT_TRUE(abc == StringPiece("abcdefghijklmnopqrstuvwxyz"));
+  ASSERT_TRUE(abc.compare(StringPiece("abcdefghijklmnopqrstuvwxyz")) == 0);
+
+  ASSERT_TRUE(abc < StringPiece("abcdefghijklmnopqrstuvwxzz"));
+  ASSERT_TRUE(abc.compare(StringPiece("abcdefghijklmnopqrstuvwxzz")) < 0);
+
+  ASSERT_TRUE(abc > StringPiece("abcdefghijklmnopqrstuvwxyy"));
+  ASSERT_TRUE(abc.compare(StringPiece("abcdefghijklmnopqrstuvwxyy")) > 0);
+
+  // starts_with
+  ASSERT_TRUE(abc.starts_with(abc));
+  ASSERT_TRUE(abc.starts_with("abcdefghijklm"));
+  ASSERT_TRUE(!abc.starts_with("abcdefguvwxyz"));
+
+  // ends_with
+  ASSERT_TRUE(abc.ends_with(abc));
+  ASSERT_TRUE(!abc.ends_with("abcdefguvwxyz"));
+  ASSERT_TRUE(abc.ends_with("nopqrstuvwxyz"));
+}
+
+TEST(StringPieceTest, StringCompareNotAmbiguous) {
+  ASSERT_TRUE("hello" == std::string("hello"));
+  ASSERT_TRUE("hello" < std::string("world"));
+}
+
+TEST(StringPieceTest, HeterogenousStringPieceEquals) {
+  ASSERT_TRUE(StringPiece("hello") == std::string("hello"));
+  ASSERT_TRUE("hello" == StringPiece("hello"));
+}
diff --git a/base/string_tokenizer.h b/base/string_tokenizer.h
new file mode 100644
index 0000000..9b0c468
--- /dev/null
+++ b/base/string_tokenizer.h
@@ -0,0 +1,225 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_STRING_TOKENIZER_H__
+#define BASE_STRING_TOKENIZER_H__
+
+#include <string>
+
+// StringTokenizerT is a simple string tokenizer class.  It works like an
+// iterator that with each step (see the Advance method) updates members that
+// refer to the next token in the input string.  The user may optionally
+// configure the tokenizer to return delimiters.
+//
+//
+// EXAMPLE 1:
+//
+//   StringTokenizer t("this is a test", " ");
+//   while (t.GetNext()) {
+//     printf("%s\n", t.token().c_str());
+//   }
+//
+// Output:
+//
+//   this
+//   is
+//   a
+//   test
+//
+//
+// EXAMPLE 2:
+//
+//   StringTokenizer t("no-cache=\"foo, bar\", private", ", ");
+//   t.set_quote_chars("\"");
+//   while (t.GetNext()) {
+//     printf("%s\n", t.token().c_str());
+//   }
+//
+// Output:
+//
+//   no-cache="foo, bar"
+//   private
+//
+//
+// EXAMPLE 3:
+//
+//   bool next_is_option = false, next_is_value = false;
+//   std::string input = "text/html; charset=UTF-8; foo=bar";
+//   StringTokenizer t(input, "; =");
+//   t.set_options(StringTokenizer::RETURN_DELIMS);
+//   while (t.GetNext()) {
+//     if (t.token_is_delim()) {
+//       switch (*t.token_begin()) {
+//         case ';':
+//           next_is_option = true;
+//           break;
+//         case '=':
+//           next_is_value = true;
+//           break;
+//       }
+//     } else {
+//       const char* label;
+//       if (next_is_option) {
+//         label = "option-name";
+//         next_is_option = false;
+//       } else if (next_is_value) {
+//         label = "option-value";
+//         next_is_value = false;
+//       } else {
+//         label = "mime-type";
+//       }
+//       printf("%s: %s\n", label, t.token().c_str());
+//     }
+//   }
+//
+//
+template <class str>
+class StringTokenizerT {
+ public:
+  typedef typename str::const_iterator const_iterator;
+  typedef typename str::value_type char_type;
+
+  // Options that may be pass to set_options()
+  enum {
+    // Specifies the delimiters should be returned as tokens
+    RETURN_DELIMS = 1 << 0,
+  };
+
+  StringTokenizerT(const str& string,
+                   const str& delims) {
+    Init(string.begin(), string.end(), delims);
+  }
+
+  StringTokenizerT(const_iterator string_begin,
+                   const_iterator string_end,
+                   const str& delims) {
+    Init(string_begin, string_end, delims);
+  }
+
+  // Set the options for this tokenizer.  By default, this is 0.
+  void set_options(int options) { options_ = options; }
+
+  // Set the characters to regard as quotes.  By default, this is empty.  When
+  // a quote char is encountered, the tokenizer will switch into a mode where
+  // it ignores delimiters that it finds.  It switches out of this mode once it
+  // finds another instance of the quote char.  If a backslash is encountered
+  // within a quoted string, then the next character is skipped.
+  void set_quote_chars(const std::string& quotes) { quotes_ = quotes; }
+
+  // Call this method to advance the tokenizer to the next delimiter.  This
+  // returns false if the tokenizer is complete.  This method must be called
+  // before calling any of the token* methods.
+  bool GetNext() {
+    AdvanceState state;
+    token_is_delim_ = false;
+    for (;;) {
+      token_begin_ = token_end_;
+      if (token_end_ == end_)
+        return false;
+      ++token_end_;
+      if (AdvanceOne(&state, *token_begin_))
+        break;
+      if (options_ & RETURN_DELIMS) {
+        token_is_delim_ = true;
+        return true;
+      }
+      // else skip over delim
+    }
+    while (token_end_ != end_ && AdvanceOne(&state, *token_end_))
+      ++token_end_;
+    return true;
+  }
+
+  // Returns true if token is a delimiter.  When the tokenizer is constructed
+  // with the RETURN_DELIMS option, this method can be used to check if the
+  // returned token is actually a delimiter.
+  bool token_is_delim() const { return token_is_delim_; }
+
+  // If GetNext() returned true, then these methods may be used to read the
+  // value of the token.
+  const_iterator token_begin() const { return token_begin_; }
+  const_iterator token_end() const { return token_end_; }
+  str token() const { return str(token_begin_, token_end_); }
+
+ private:
+  void Init(const_iterator string_begin,
+            const_iterator string_end,
+            const str& delims) {
+    token_end_ = string_begin;
+    end_ = string_end;
+    delims_ = delims;
+    options_ = 0;
+  }
+
+  bool IsDelim(char_type c) const {
+    return delims_.find(c) != str::npos;
+  }
+
+  bool IsQuote(char_type c) const {
+    return quotes_.find(c) != str::npos;
+  }
+
+  struct AdvanceState {
+    bool in_quote;
+    bool in_escape;
+    char_type quote_char;
+    AdvanceState() : in_quote(false), in_escape(false) {}
+  };
+
+  // Returns true if a delimiter was not hit.
+  bool AdvanceOne(AdvanceState* state, char_type c) {
+    if (state->in_quote) {
+      if (state->in_escape) {
+        state->in_escape = false;
+      } else if (c == '\\') {
+        state->in_escape = true;
+      } else if (c == state->quote_char) {
+        state->in_quote = false;
+      }
+    } else {
+      if (IsDelim(c))
+        return false;
+      state->in_quote = IsQuote(state->quote_char = c);
+    }
+    return true;
+  }
+
+  const_iterator token_begin_;
+  const_iterator token_end_;
+  const_iterator end_;
+  str delims_;
+  str quotes_;
+  int options_;
+  bool token_is_delim_;
+};
+
+typedef StringTokenizerT<std::string> StringTokenizer;
+typedef StringTokenizerT<std::wstring> WStringTokenizer;
+
+#endif  // BASE_STRING_TOKENIZER_H__
diff --git a/base/string_tokenizer_unittest.cc b/base/string_tokenizer_unittest.cc
new file mode 100644
index 0000000..16b8018
--- /dev/null
+++ b/base/string_tokenizer_unittest.cc
@@ -0,0 +1,232 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/string_tokenizer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+
+namespace {
+class StringTokenizerTest : public testing::Test {};
+}
+
+TEST(StringTokenizerTest, Simple) {
+  string input = "this is a test";
+  StringTokenizer t(input, " ");
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ(string("this"), t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ(string("is"), t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ(string("a"), t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ(string("test"), t.token());
+
+  EXPECT_FALSE(t.GetNext());
+}
+
+TEST(StringTokenizerTest, RetDelims) {
+  string input = "this is a test";
+  StringTokenizer t(input, " ");
+  t.set_options(StringTokenizer::RETURN_DELIMS);
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ(string("this"), t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ(string(" "), t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ(string("is"), t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ(string(" "), t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ(string("a"), t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ(string(" "), t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ(string("test"), t.token());
+
+  EXPECT_FALSE(t.GetNext());
+}
+
+TEST(StringTokenizerTest, ManyDelims) {
+  string input = "this: is, a-test";
+  StringTokenizer t(input, ": ,-");
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ(string("this"), t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ(string("is"), t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ(string("a"), t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ(string("test"), t.token());
+
+  EXPECT_FALSE(t.GetNext());
+}
+
+TEST(StringTokenizerTest, ParseHeader) {
+  string input = "Content-Type: text/html ; charset=UTF-8";
+  StringTokenizer t(input, ": ;=");
+  t.set_options(StringTokenizer::RETURN_DELIMS);
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_FALSE(t.token_is_delim());
+  EXPECT_EQ(string("Content-Type"), t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_TRUE(t.token_is_delim());
+  EXPECT_EQ(string(":"), t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_TRUE(t.token_is_delim());
+  EXPECT_EQ(string(" "), t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_FALSE(t.token_is_delim());
+  EXPECT_EQ(string("text/html"), t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_TRUE(t.token_is_delim());
+  EXPECT_EQ(string(" "), t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_TRUE(t.token_is_delim());
+  EXPECT_EQ(string(";"), t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_TRUE(t.token_is_delim());
+  EXPECT_EQ(string(" "), t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_FALSE(t.token_is_delim());
+  EXPECT_EQ(string("charset"), t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_TRUE(t.token_is_delim());
+  EXPECT_EQ(string("="), t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_FALSE(t.token_is_delim());
+  EXPECT_EQ(string("UTF-8"), t.token());
+
+  EXPECT_FALSE(t.GetNext());
+  EXPECT_FALSE(t.token_is_delim());
+}
+
+TEST(StringTokenizerTest, ParseQuotedString) {
+  string input = "foo bar 'hello world' baz";
+  StringTokenizer t(input, " ");
+  t.set_quote_chars("'");
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ(string("foo"), t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ(string("bar"), t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ(string("'hello world'"), t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ(string("baz"), t.token());
+
+  EXPECT_FALSE(t.GetNext());
+}
+
+TEST(StringTokenizerTest, ParseQuotedString_Malformed) {
+  string input = "bar 'hello wo";
+  StringTokenizer t(input, " ");
+  t.set_quote_chars("'");
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ(string("bar"), t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ(string("'hello wo"), t.token());
+
+  EXPECT_FALSE(t.GetNext());
+}
+
+TEST(StringTokenizerTest, ParseQuotedString_Multiple) {
+  string input = "bar 'hel\"lo\" wo' baz\"";
+  StringTokenizer t(input, " ");
+  t.set_quote_chars("'\"");
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ(string("bar"), t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ(string("'hel\"lo\" wo'"), t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ(string("baz\""), t.token());
+
+  EXPECT_FALSE(t.GetNext());
+}
+
+TEST(StringTokenizerTest, ParseQuotedString_EscapedQuotes) {
+  string input = "foo 'don\\'t do that'";
+  StringTokenizer t(input, " ");
+  t.set_quote_chars("'");
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ(string("foo"), t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ(string("'don\\'t do that'"), t.token());
+
+  EXPECT_FALSE(t.GetNext());
+}
+
+TEST(StringTokenizerTest, ParseQuotedString_EscapedQuotes2) {
+  string input = "foo='a, b', bar";
+  StringTokenizer t(input, ", ");
+  t.set_quote_chars("'");
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ(string("foo='a, b'"), t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ(string("bar"), t.token());
+
+  EXPECT_FALSE(t.GetNext());
+}
diff --git a/base/string_util.cc b/base/string_util.cc
new file mode 100644
index 0000000..2122b9f
--- /dev/null
+++ b/base/string_util.cc
@@ -0,0 +1,1021 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// StringPrintf stuff based on strings/stringprintf.cc by Sanjay Ghemawat
+
+#include "base/string_util.h"
+
+#include <algorithm>
+#include <math.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "base/singleton.h"
+
+namespace {
+
+// Hack to convert any char-like type to its unsigned counterpart.
+// For example, it will convert char, signed char and unsigned char to unsigned
+// char.
+template<typename T>
+struct ToUnsigned {
+  typedef T Unsigned;
+};
+
+template<>
+struct ToUnsigned<char> {
+  typedef unsigned char Unsigned;
+};
+template<>
+struct ToUnsigned<signed char> {
+  typedef unsigned char Unsigned;
+};
+template<>
+struct ToUnsigned<wchar_t> {
+  typedef unsigned short Unsigned;
+};
+template<>
+struct ToUnsigned<short> {
+  typedef unsigned short Unsigned;
+};
+
+// Used by ReplaceStringPlaceholders to track the position in the string of
+// replaced parameters.
+struct ReplacementOffset {
+  ReplacementOffset(int parameter, size_t offset)
+      : parameter(parameter),
+        offset(offset) {}
+
+  // Index of the parameter.
+  int parameter;
+
+  // Starting position in the string.
+  size_t offset;
+};
+
+static bool CompareParameter(const ReplacementOffset& elem1,
+                             const ReplacementOffset& elem2) {
+  return elem1.parameter < elem2.parameter;
+}
+
+}  // namespace
+
+
+const std::string& EmptyString() {
+  return *Singleton<std::string>::get();
+}
+
+const std::wstring& EmptyWString() {
+  return *Singleton<std::wstring>::get();
+}
+
+const wchar_t kWhitespaceWide[] = {
+  0x0009,  // <control-0009> to <control-000D>
+  0x000A,
+  0x000B,
+  0x000C,
+  0x000D,
+  0x0020,  // Space
+  0x0085,  // <control-0085>
+  0x00A0,  // No-Break Space
+  0x1680,  // Ogham Space Mark
+  0x180E,  // Mongolian Vowel Separator
+  0x2000,  // En Quad to Hair Space
+  0x2001,
+  0x2002,
+  0x2003,
+  0x2004,
+  0x2005,
+  0x2006,
+  0x2007,
+  0x2008,
+  0x2009,
+  0x200A,
+  0x200C,  // Zero Width Non-Joiner
+  0x2028,  // Line Separator
+  0x2029,  // Paragraph Separator
+  0x202F,  // Narrow No-Break Space
+  0x205F,  // Medium Mathematical Space
+  0x3000,  // Ideographic Space
+  0
+};
+const char kWhitespaceASCII[] = {
+  0x09,    // <control-0009> to <control-000D>
+  0x0A,
+  0x0B,
+  0x0C,
+  0x0D,
+  0x20,    // Space
+  '\x85',  // <control-0085>
+  '\xa0',  // No-Break Space
+  0
+};
+const char* const kCodepageUTF8 = "UTF-8";
+
+template<typename STR>
+TrimPositions TrimStringT(const STR& input,
+                          const typename STR::value_type trim_chars[],
+                          TrimPositions positions,
+                          STR* output) {
+  // Find the edges of leading/trailing whitespace as desired.
+  const typename STR::size_type last_char = input.length() - 1;
+  const typename STR::size_type first_good_char = (positions & TRIM_LEADING) ?
+      input.find_first_not_of(trim_chars) : 0;
+  const typename STR::size_type last_good_char = (positions & TRIM_TRAILING) ?
+      input.find_last_not_of(trim_chars) : last_char;
+
+  // When the string was all whitespace, report that we stripped off whitespace
+  // from whichever position the caller was interested in.  For empty input, we
+  // stripped no whitespace, but we still need to clear |output|.
+  if (input.empty() ||
+      (first_good_char == STR::npos) || (last_good_char == STR::npos)) {
+    bool input_was_empty = input.empty();  // in case output == &input
+    output->clear();
+    return input_was_empty ? TRIM_NONE : positions;
+  }
+
+  // Trim the whitespace.
+  *output =
+      input.substr(first_good_char, last_good_char - first_good_char + 1);
+
+  // Return where we trimmed from.
+  return static_cast<TrimPositions>(
+      ((first_good_char == 0) ? TRIM_NONE : TRIM_LEADING) |
+      ((last_good_char == last_char) ? TRIM_NONE : TRIM_TRAILING));
+}
+
+bool TrimString(const std::wstring& input,
+                wchar_t trim_chars[],
+                std::wstring* output) {
+  return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
+}
+
+bool TrimString(const std::string& input,
+                char trim_chars[],
+                std::string* output) {
+  return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
+}
+
+TrimPositions TrimWhitespace(const std::wstring& input,
+                             TrimPositions positions,
+                             std::wstring* output) {
+  return TrimStringT(input, kWhitespaceWide, positions, output);
+}
+
+TrimPositions TrimWhitespace(const std::string& input,
+                             TrimPositions positions,
+                             std::string* output) {
+  return TrimStringT(input, kWhitespaceASCII, positions, output);
+}
+
+std::wstring CollapseWhitespace(const std::wstring& text,
+                                bool trim_sequences_with_line_breaks) {
+  std::wstring result;
+  result.resize(text.size());
+
+  // Set flags to pretend we're already in a trimmed whitespace sequence, so we
+  // will trim any leading whitespace.
+  bool in_whitespace = true;
+  bool already_trimmed = true;
+
+  int chars_written = 0;
+  for (std::wstring::const_iterator i(text.begin()); i != text.end(); ++i) {
+    if (IsWhitespace(*i)) {
+      if (!in_whitespace) {
+        // Reduce all whitespace sequences to a single space.
+        in_whitespace = true;
+        result[chars_written++] = L' ';
+      }
+      if (trim_sequences_with_line_breaks && !already_trimmed &&
+          ((*i == '\n') || (*i == '\r'))) {
+        // Whitespace sequences containing CR or LF are eliminated entirely.
+        already_trimmed = true;
+        --chars_written;
+      }
+    } else {
+      // Non-whitespace chracters are copied straight across.
+      in_whitespace = false;
+      already_trimmed = false;
+      result[chars_written++] = *i;
+    }
+  }
+
+  if (in_whitespace && !already_trimmed) {
+    // Any trailing whitespace is eliminated.
+    --chars_written;
+  }
+
+  result.resize(chars_written);
+  return result;
+}
+
+std::string WideToASCII(const std::wstring& wide) {
+  DCHECK(IsStringASCII(wide));
+  return std::string(wide.begin(), wide.end());
+}
+
+std::wstring ASCIIToWide(const std::string& ascii) {
+  DCHECK(IsStringASCII(ascii));
+  return std::wstring(ascii.begin(), ascii.end());
+}
+
+// Latin1 is just the low range of Unicode, so we can copy directly to convert.
+bool WideToLatin1(const std::wstring& wide, std::string* latin1) {
+  std::string output;
+  output.resize(wide.size());
+  latin1->clear();
+  for (size_t i = 0; i < wide.size(); i++) {
+    if (wide[i] > 255)
+      return false;
+    output[i] = static_cast<char>(wide[i]);
+  }
+  latin1->swap(output);
+  return true;
+}
+
+bool IsString8Bit(const std::wstring& str) {
+  for (size_t i = 0; i < str.length(); i++) {
+    if (str[i] > 255)
+      return false;
+  }
+  return true;
+}
+
+bool IsStringASCII(const std::wstring& str) {
+  for (size_t i = 0; i < str.length(); i++) {
+    if (str[i] > 0x7F)
+      return false;
+  }
+  return true;
+}
+
+bool IsStringASCII(const std::string& str) {
+  for (size_t i = 0; i < str.length(); i++) {
+    if (static_cast<unsigned char>(str[i]) > 0x7F)
+      return false;
+  }
+  return true;
+}
+
+// Helper functions that determine whether the given character begins a
+// UTF-8 sequence of bytes with the given length. A character satisfies
+// "IsInUTF8Sequence" if it is anything but the first byte in a multi-byte
+// character.
+static inline bool IsBegin2ByteUTF8(int c) {
+  return (c & 0xE0) == 0xC0;
+}
+static inline bool IsBegin3ByteUTF8(int c) {
+  return (c & 0xF0) == 0xE0;
+}
+static inline bool IsBegin4ByteUTF8(int c) {
+  return (c & 0xF8) == 0xF0;
+}
+static inline bool IsInUTF8Sequence(int c) {
+  return (c & 0xC0) == 0x80;
+}
+
+// This function was copied from Mozilla, with modifications. The original code
+// was 'IsUTF8' in xpcom/string/src/nsReadableUtils.cpp. The license block for
+// this function is:
+//   This function subject to the Mozilla Public License Version
+//   1.1 (the "License"); you may not use this code except in compliance with
+//   the License. You may obtain a copy of the License at
+//   http://www.mozilla.org/MPL/
+//
+//   Software distributed under the License is distributed on an "AS IS" basis,
+//   WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+//   for the specific language governing rights and limitations under the
+//   License.
+//
+//   The Original Code is mozilla.org code.
+//
+//   The Initial Developer of the Original Code is
+//   Netscape Communications Corporation.
+//   Portions created by the Initial Developer are Copyright (C) 2000
+//   the Initial Developer. All Rights Reserved.
+//
+//   Contributor(s):
+//     Scott Collins <scc@mozilla.org> (original author)
+//
+// This is a template so that it can be run on wide and 8-bit strings. We want
+// to run it on wide strings when we have input that we think may have
+// originally been UTF-8, but has been converted to wide characters because
+// that's what we (and Windows) use internally.
+template<typename CHAR>
+static bool IsStringUTF8T(const CHAR* str) {
+  bool overlong = false;
+  bool surrogate = false;
+  bool nonchar = false;
+
+  // overlong byte upper bound
+  typename ToUnsigned<CHAR>::Unsigned olupper = 0;
+
+  // surrogate byte lower bound
+  typename ToUnsigned<CHAR>::Unsigned slower = 0;
+
+  // incremented when inside a multi-byte char to indicate how many bytes
+  // are left in the sequence
+  int positions_left = 0;
+
+  for (int i = 0; str[i] != 0; i++) {
+    // This whole function assume an unsigned value so force its conversion to
+    // an unsigned value.
+    typename ToUnsigned<CHAR>::Unsigned c = str[i];
+    if (c < 0x80)
+      continue;  // ASCII
+
+    if (c <= 0xC1) {
+      // [80-BF] where not expected, [C0-C1] for overlong
+      return false;
+    } else if (IsBegin2ByteUTF8(c)) {
+      positions_left = 1;
+    } else if (IsBegin3ByteUTF8(c)) {
+      positions_left = 2;
+      if (c == 0xE0) {
+        // to exclude E0[80-9F][80-BF]
+        overlong = true;
+        olupper = 0x9F;
+      } else if (c == 0xED) {
+        // ED[A0-BF][80-BF]: surrogate codepoint
+        surrogate = true;
+        slower = 0xA0;
+      } else if (c == 0xEF) {
+        // EF BF [BE-BF] : non-character
+        nonchar = true;
+      }
+    } else if (c <= 0xF4) {
+      positions_left = 3;
+      nonchar = true;
+      if (c == 0xF0) {
+        // to exclude F0[80-8F][80-BF]{2}
+        overlong = true;
+        olupper = 0x8F;
+      } else if (c == 0xF4) {
+        // to exclude F4[90-BF][80-BF]
+        // actually not surrogates but codepoints beyond 0x10FFFF
+        surrogate = true;
+        slower = 0x90;
+      }
+    } else {
+      return false;
+    }
+
+    // eat the rest of this multi-byte character
+    while (positions_left) {
+      positions_left--;
+      i++;
+      c = str[i];
+      if (!c)
+        return false;  // end of string but not end of character sequence
+
+      // non-character : EF BF [BE-BF] or F[0-7] [89AB]F BF [BE-BF]
+      if (nonchar && (!positions_left && c < 0xBE ||
+                      positions_left == 1 && c != 0xBF ||
+                      positions_left == 2 && 0x0F != (0x0F & c) )) {
+        nonchar = false;
+      }
+      if (!IsInUTF8Sequence(c) || overlong && c <= olupper ||
+          surrogate && slower <= c || nonchar && !positions_left ) {
+        return false;
+      }
+      overlong = surrogate = false;
+    }
+  }
+  return true;
+}
+
+bool IsStringUTF8(const char* str) {
+  return IsStringUTF8T(str);
+}
+
+bool IsStringWideUTF8(const wchar_t* str) {
+  return IsStringUTF8T(str);
+}
+
+template<typename Iter>
+static inline bool DoLowerCaseEqualsASCII(Iter a_begin,
+                                          Iter a_end,
+                                          const char* b) {
+  for (Iter it = a_begin; it != a_end; ++it, ++b) {
+    if (!*b || ToLowerASCII(*it) != *b)
+      return false;
+  }
+  return *b == 0;
+}
+
+// Front-ends for LowerCaseEqualsASCII.
+bool LowerCaseEqualsASCII(const std::string& a, const char* b) {
+  return DoLowerCaseEqualsASCII(a.begin(), a.end(), b);
+}
+
+bool LowerCaseEqualsASCII(const std::wstring& a, const char* b) {
+  return DoLowerCaseEqualsASCII(a.begin(), a.end(), b);
+}
+
+bool LowerCaseEqualsASCII(std::string::const_iterator a_begin,
+                          std::string::const_iterator a_end,
+                          const char* b) {
+  return DoLowerCaseEqualsASCII(a_begin, a_end, b);
+}
+
+bool LowerCaseEqualsASCII(std::wstring::const_iterator a_begin,
+                          std::wstring::const_iterator a_end,
+                          const char* b) {
+  return DoLowerCaseEqualsASCII(a_begin, a_end, b);
+}
+bool LowerCaseEqualsASCII(const char* a_begin,
+                          const char* a_end,
+                          const char* b) {
+  return DoLowerCaseEqualsASCII(a_begin, a_end, b);
+}
+bool LowerCaseEqualsASCII(const wchar_t* a_begin,
+                          const wchar_t* a_end,
+                          const char* b) {
+  return DoLowerCaseEqualsASCII(a_begin, a_end, b);
+}
+
+bool StartsWithASCII(const std::string& str,
+                     const std::string& search,
+                     bool case_sensitive) {
+  if (case_sensitive)
+    return str.compare(0, search.length(), search) == 0;
+  else
+    return StrNCaseCmp(str.c_str(), search.c_str(), search.length()) == 0;
+}
+
+DataUnits GetByteDisplayUnits(int64 bytes) {
+  // The byte thresholds at which we display amounts.  A byte count is displayed
+  // in unit U when kUnitThresholds[U] <= bytes < kUnitThresholds[U+1].
+  // This must match the DataUnits enum.
+  static const int64 kUnitThresholds[] = {
+    0,              // DATA_UNITS_BYTE,
+    3*1024,         // DATA_UNITS_KILOBYTE,
+    2*1024*1024,    // DATA_UNITS_MEGABYTE,
+    1024*1024*1024  // DATA_UNITS_GIGABYTE,
+  };
+
+  if (bytes < 0) {
+    NOTREACHED() << "Negative bytes value";
+    return DATA_UNITS_BYTE;
+  }
+
+  int unit_index = arraysize(kUnitThresholds);
+  while (--unit_index > 0) {
+    if (bytes >= kUnitThresholds[unit_index])
+      break;
+  }
+
+  DCHECK(unit_index >= DATA_UNITS_BYTE && unit_index <= DATA_UNITS_GIGABYTE);
+  return DataUnits(unit_index);
+}
+
+// TODO(mpcomplete): deal with locale
+// Byte suffixes.  This must match the DataUnits enum.
+static const wchar_t* const kByteStrings[] = {
+  L"B",
+  L"kB",
+  L"MB",
+  L"GB"
+};
+
+static const wchar_t* const kSpeedStrings[] = {
+  L"B/s",
+  L"kB/s",
+  L"MB/s",
+  L"GB/s"
+};
+
+std::wstring FormatBytesInternal(int64 bytes,
+                                 DataUnits units,
+                                 bool show_units,
+                                 const wchar_t* const* suffix) {
+  if (bytes < 0) {
+    NOTREACHED() << "Negative bytes value";
+    return std::wstring();
+  }
+
+  DCHECK(units >= DATA_UNITS_BYTE && units <= DATA_UNITS_GIGABYTE);
+
+  // Put the quantity in the right units.
+  double unit_amount = static_cast<double>(bytes);
+  for (int i = 0; i < units; ++i)
+    unit_amount /= 1024.0;
+
+  wchar_t tmp[64];
+  // If the first decimal digit is 0, don't show it.
+  double int_part;
+  double fractional_part = modf(unit_amount, &int_part);
+  modf(fractional_part * 10, &int_part);
+  if (int_part == 0)
+    SWPrintF(tmp, arraysize(tmp), L"%lld", static_cast<int64>(unit_amount));
+  else
+    SWPrintF(tmp, arraysize(tmp), L"%.1lf", unit_amount);
+
+  std::wstring ret(tmp);
+  if (show_units) {
+    ret += L" ";
+    ret += suffix[units];
+  }
+
+  return ret;
+}
+
+std::wstring FormatBytes(int64 bytes, DataUnits units, bool show_units) {
+  return FormatBytesInternal(bytes, units, show_units, kByteStrings);
+}
+
+std::wstring FormatSpeed(int64 bytes, DataUnits units, bool show_units) {
+  return FormatBytesInternal(bytes, units, show_units, kSpeedStrings);
+}
+
+template<class StringType>
+void DoReplaceSubstringsAfterOffset(StringType* str,
+                                    typename StringType::size_type start_offset,
+                                    const StringType& find_this,
+                                    const StringType& replace_with) {
+  if ((start_offset == StringType::npos) || (start_offset >= str->length()))
+    return;
+
+  DCHECK(!find_this.empty());
+  for (typename StringType::size_type offs(str->find(find_this, start_offset));
+      offs != StringType::npos; offs = str->find(find_this, offs)) {
+    str->replace(offs, find_this.length(), replace_with);
+    offs += replace_with.length();
+  }
+}
+
+void ReplaceSubstringsAfterOffset(std::wstring* str,
+                                  std::wstring::size_type start_offset,
+                                  const std::wstring& find_this,
+                                  const std::wstring& replace_with) {
+  DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with);
+}
+
+void ReplaceSubstringsAfterOffset(std::string* str,
+                                  std::string::size_type start_offset,
+                                  const std::string& find_this,
+                                  const std::string& replace_with) {
+  DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with);
+}
+
+// Overloaded wrappers around vsnprintf and vswprintf. The buf_size parameter
+// is the size of the buffer. These return the number of characters in the
+// formatted string excluding the NUL terminator, or if the buffer is not
+// large enough to accommodate the formatted string without truncation, the
+// number of characters that would be in the fully-formatted string.
+inline int vsnprintfT(char* buffer,
+                      size_t buf_size,
+                      const char* format,
+                      va_list argptr) {
+  return VSNPrintF(buffer, buf_size, format, argptr);
+}
+
+inline int vsnprintfT(wchar_t* buffer,
+                      size_t buf_size,
+                      const wchar_t* format,
+                      va_list argptr) {
+  return VSWPrintF(buffer, buf_size, format, argptr);
+}
+
+// Templatized backend for StringPrintF/StringAppendF. This does not finalize
+// the va_list, the caller is expected to do that.
+template <class char_type>
+static void StringAppendVT(
+    std::basic_string<char_type, std::char_traits<char_type> >* dst,
+    const char_type* format,
+    va_list ap) {
+
+  // First try with a small fixed size buffer.
+  // This buffer size should be kept in sync with StringUtilTest.GrowBoundary.
+  const int kStackLength = 1024;
+  char_type stack_buf[kStackLength];
+
+  // It's possible for methods that use a va_list to invalidate the data in it
+  // upon use.  The fix is to make a copy of the structure before using it and
+  // use that copy instead. It is not guaranteed that assignment is a copy, and
+  // va_copy is not supported by VC, so the UnitTest tests this capability.
+  va_list backup_ap = ap;
+  int result = vsnprintfT(stack_buf, kStackLength, format, backup_ap);
+  va_end(backup_ap);
+
+  if (result >= 0 && result < kStackLength) {
+    // It fit.
+    dst->append(stack_buf, result);
+    return;
+  }
+
+  int mem_length = result;
+
+  // vsnprintfT may have failed for some reason other than an insufficient
+  // buffer, such as an invalid characer.  Check that the requested buffer
+  // size is smaller than what was already attempted
+  if (mem_length < 0 || mem_length < kStackLength) {
+    DLOG(WARNING) << "Unable to compute size of the requested string.";
+    return;
+  }
+
+  mem_length++;  // Include the NULL terminator.
+  scoped_ptr<char_type> mem_buf(new char_type[mem_length]);
+
+  // Do the printf.
+  result = vsnprintfT(mem_buf.get(), mem_length, format, ap);
+  DCHECK(result < mem_length);
+  if (result < 0) {
+    DLOG(WARNING) << "Unable to printf the requested string.";
+    return;
+  }
+
+  dst->append(mem_buf.get(), result);
+}
+
+std::string Uint64ToString(uint64 value) {
+  return StringPrintf("%llu", value);
+}
+
+std::string Int64ToString(int64 value) {
+  return StringPrintf("%I64d", value);
+}
+
+std::wstring Int64ToWString(int64 value) {
+  return StringPrintf(L"%I64d", value);
+}
+
+std::string IntToString(int value) {
+  return StringPrintf("%d", value);
+}
+
+std::wstring IntToWString(int value) {
+  return StringPrintf(L"%d", value);
+}
+
+inline void StringAppendV(std::string* dst, const char* format, va_list ap) {
+  StringAppendVT<char>(dst, format, ap);
+}
+
+inline void StringAppendV(std::wstring* dst,
+                          const wchar_t* format,
+                          va_list ap) {
+  StringAppendVT<wchar_t>(dst, format, ap);
+}
+
+std::string StringPrintf(const char* format, ...) {
+  va_list ap;
+  va_start(ap, format);
+  std::string result;
+  StringAppendV(&result, format, ap);
+  va_end(ap);
+  return result;
+}
+
+std::wstring StringPrintf(const wchar_t* format, ...) {
+  va_list ap;
+  va_start(ap, format);
+  std::wstring result;
+  StringAppendV(&result, format, ap);
+  va_end(ap);
+  return result;
+}
+
+const std::string& SStringPrintf(std::string* dst, const char* format, ...) {
+  va_list ap;
+  va_start(ap, format);
+  dst->clear();
+  StringAppendV(dst, format, ap);
+  va_end(ap);
+  return *dst;
+}
+
+const std::wstring& SStringPrintf(std::wstring* dst,
+                                  const wchar_t* format, ...) {
+  va_list ap;
+  va_start(ap, format);
+  dst->clear();
+  StringAppendV(dst, format, ap);
+  va_end(ap);
+  return *dst;
+}
+
+void StringAppendF(std::string* dst, const char* format, ...) {
+  va_list ap;
+  va_start(ap, format);
+  StringAppendV(dst, format, ap);
+  va_end(ap);
+}
+
+void StringAppendF(std::wstring* dst, const wchar_t* format, ...) {
+  va_list ap;
+  va_start(ap, format);
+  StringAppendV(dst, format, ap);
+  va_end(ap);
+}
+
+template<typename STR>
+static void SplitStringT(const STR& str,
+                         const typename STR::value_type s,
+                         bool trim_whitespace,
+                         std::vector<STR>* r) {
+  size_t last = 0;
+  size_t i;
+  size_t c = str.size();
+  for (i = 0; i <= c; ++i) {
+    if (i == c || str[i] == s) {
+      size_t len = i - last;
+      STR tmp = str.substr(last, len);
+      if (trim_whitespace) {
+        STR t_tmp;
+        TrimWhitespace(tmp, TRIM_ALL, &t_tmp);
+        r->push_back(t_tmp);
+      } else {
+        r->push_back(tmp);
+      }
+      last = i + 1;
+    }
+  }
+}
+
+void SplitString(const std::wstring& str,
+                 wchar_t s,
+                 std::vector<std::wstring>* r) {
+  SplitStringT(str, s, true, r);
+}
+
+void SplitString(const std::string& str,
+                 char s,
+                 std::vector<std::string>* r) {
+  SplitStringT(str, s, true, r);
+}
+
+void SplitStringDontTrim(const std::wstring& str,
+                         wchar_t s,
+                         std::vector<std::wstring>* r) {
+  SplitStringT(str, s, false, r);
+}
+
+void SplitStringDontTrim(const std::string& str,
+                         char s,
+                         std::vector<std::string>* r) {
+  SplitStringT(str, s, false, r);
+}
+
+void SplitStringAlongWhitespace(const std::wstring& str,
+                                std::vector<std::wstring>* result) {
+  const size_t length = str.length();
+  if (!length)
+    return;
+
+  bool last_was_ws = false;
+  size_t last_non_ws_start = 0;
+  for (size_t i = 0; i < length; ++i) {
+    switch(str[i]) {
+      // HTML 5 defines whitespace as: space, tab, LF, line tab, FF, or CR.
+      case L' ':
+      case L'\t':
+      case L'\xA':
+      case L'\xB':
+      case L'\xC':
+      case L'\xD':
+        if (!last_was_ws) {
+          if (i > 0) {
+            result->push_back(
+                str.substr(last_non_ws_start, i - last_non_ws_start));
+          }
+          last_was_ws = true;
+        }
+        break;
+
+      default:  // Not a space character.
+        if (last_was_ws) {
+          last_was_ws = false;
+          last_non_ws_start = i;
+        }
+        break;
+    }
+  }
+  if (!last_was_ws) {
+    result->push_back(
+              str.substr(last_non_ws_start, length - last_non_ws_start));
+  }
+}
+
+std::wstring ReplaceStringPlaceholders(const std::wstring& format_string,
+                                       const std::wstring& a,
+                                       size_t* offset) {
+  std::vector<size_t> offsets;
+  std::wstring result = ReplaceStringPlaceholders(format_string, a,
+                                                  std::wstring(),
+                                                  std::wstring(),
+                                                  std::wstring(), &offsets);
+  DCHECK(offsets.size() == 1);
+  if (offset) {
+    *offset = offsets[0];
+  }
+  return result;
+}
+
+std::wstring ReplaceStringPlaceholders(const std::wstring& format_string,
+                                       const std::wstring& a,
+                                       const std::wstring& b,
+                                       std::vector<size_t>* offsets) {
+  return ReplaceStringPlaceholders(format_string, a, b, std::wstring(),
+                                   std::wstring(), offsets);
+}
+
+std::wstring ReplaceStringPlaceholders(const std::wstring& format_string,
+                                       const std::wstring& a,
+                                       const std::wstring& b,
+                                       const std::wstring& c,
+                                       std::vector<size_t>* offsets) {
+  return ReplaceStringPlaceholders(format_string, a, b, c, std::wstring(),
+                                   offsets);
+}
+
+std::wstring ReplaceStringPlaceholders(const std::wstring& format_string,
+                                       const std::wstring& a,
+                                       const std::wstring& b,
+                                       const std::wstring& c,
+                                       const std::wstring& d,
+                                       std::vector<size_t>* offsets) {
+  // We currently only support up to 4 place holders ($1 through $4), although
+  // it's easy enough to add more.
+  const std::wstring* subst_texts[] = { &a, &b, &c, &d };
+
+  std::wstring formatted;
+  formatted.reserve(format_string.length() + a.length() +
+      b.length() + c.length() + d.length());
+
+  std::vector<ReplacementOffset> r_offsets;
+
+  // Replace $$ with $ and $1-$4 with placeholder text if it exists.
+  for (std::wstring::const_iterator i = format_string.begin();
+       i != format_string.end(); ++i) {
+    if ('$' == *i) {
+      if (i + 1 != format_string.end()) {
+        ++i;
+        DCHECK('$' == *i || ('1' <= *i && *i <= '4')) <<
+            "Invalid placeholder: " << *i;
+        if ('$' == *i) {
+          formatted.push_back('$');
+        } else {
+          int index = *i - '1';
+          if (offsets) {
+            ReplacementOffset r_offset(index,
+                                       static_cast<int>(formatted.size()));
+            r_offsets.insert(std::lower_bound(r_offsets.begin(),
+                                              r_offsets.end(), r_offset,
+                                              &CompareParameter),
+                             r_offset);
+          }
+          formatted.append(*subst_texts[index]);
+        }
+      }
+    } else {
+      formatted.push_back(*i);
+    }
+  }
+  if (offsets) {
+    for (std::vector<ReplacementOffset>::const_iterator i = r_offsets.begin();
+         i != r_offsets.end(); ++i) {
+      offsets->push_back(i->offset);
+    }
+  }
+  return formatted;
+}
+
+template <class CHAR>
+static bool IsWildcard(CHAR character) {
+  return character == '*' || character == '?';
+}
+
+// Move the strings pointers to the point where they start to differ.
+template <class CHAR>
+static void EatSameChars(const CHAR** pattern, const CHAR** string) {
+  bool escaped = false;
+  while (**pattern && **string) {
+    if (!escaped && IsWildcard(**pattern)) {
+      // We don't want to match wildcard here, except if it's escaped.
+      return;
+    }
+
+    // Check if the escapement char is found. If so, skip it and move to the
+    // next character.
+    if (!escaped && **pattern == L'\\') {
+      escaped = true;
+      (*pattern)++;
+      continue;
+    }
+
+    // Check if the chars match, if so, increment the ptrs.
+    if (**pattern == **string) {
+      (*pattern)++;
+      (*string)++;
+    } else {
+      // Uh ho, it did not match, we are done. If the last char was an
+      // escapement, that means that it was an error to advance the ptr here,
+      // let's put it back where it was. This also mean that the MatchPattern
+      // function will return false because if we can't match an escape char
+      // here, then no one will.
+      if (escaped) {
+        (*pattern)--;
+      }
+      return;
+    }
+
+    escaped = false;
+  }
+}
+
+template <class CHAR>
+static void EatWildcard(const CHAR** pattern) {
+  while(**pattern) {
+    if (!IsWildcard(**pattern))
+      return;
+    (*pattern)++;
+  }
+}
+
+template <class CHAR>
+static bool MatchPatternT(const CHAR* eval, const CHAR* pattern) {
+  // Eat all the matching chars.
+  EatSameChars(&pattern, &eval);
+
+  // If the string is empty, then the pattern must be empty too, or contains
+  // only wildcards.
+  if (*eval == 0) {
+    EatWildcard(&pattern);
+    if (*pattern)
+      return false;
+    return true;
+  }
+
+  // Pattern is empty but not string, this is not a match.
+  if (*pattern == 0)
+    return false;
+
+  // If this is a question mark, then we need to compare the rest with
+  // the current string or the string with one character eaten.
+  if (pattern[0] == '?') {
+    if (MatchPatternT(eval, pattern + 1) ||
+        MatchPatternT(eval + 1, pattern + 1))
+      return true;
+  }
+
+  // This is a *, try to match all the possible substrings with the remainder
+  // of the pattern.
+  if (pattern[0] == '*') {
+    while (*eval) {
+      if (MatchPatternT(eval, pattern + 1))
+        return true;
+      eval++;
+    }
+
+    // We reached the end of the string, let see if the pattern contains only
+    // wildcards.
+    if (*eval == 0) {
+      EatWildcard(&pattern);
+      if (*pattern)
+        return false;
+      return true;
+    }
+  }
+
+  return false;
+}
+
+bool MatchPattern(const std::wstring& eval, const std::wstring& pattern) {
+  return MatchPatternT(eval.c_str(), pattern.c_str());
+}
+
+bool MatchPattern(const std::string& eval, const std::string& pattern) {
+  return MatchPatternT(eval.c_str(), pattern.c_str());
+}
diff --git a/base/string_util.h b/base/string_util.h
new file mode 100644
index 0000000..e5fd147
--- /dev/null
+++ b/base/string_util.h
@@ -0,0 +1,504 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// This file defines utility functions for working with strings.
+
+#ifndef BASE_STRING_UTIL_H__
+#define BASE_STRING_UTIL_H__
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+
+// Safe standard library wrappers for all platforms.  The Str* variants
+// operate on NUL-terminated char* strings, like the standard library's str*
+// functions.
+
+// Copy at most (dst_size - 1) characters from src to dest, guaranteeing dst
+// will be NUL-terminated.  If the string is copied without truncation,
+// returns true.  dst is undefined if the string cannot be copied without
+// truncation, and the function will either return false or cause termination.
+bool StrCpy(char* dest, const char* src, size_t dst_size);
+
+// As with StrCpy, but copies at most the minimum of (dst_size - 1) and
+// src_size characters.
+bool StrNCpy(char* dest, const char* src, size_t dst_size, size_t src_size);
+
+// Compare up to count characters of s1 and s2 without regard to case using
+// the current locale; returns 0 if they are equal, 1 if s1 > s2, and -1 if
+// s2 > s1 according to a lexicographic comparison.
+int StrNCaseCmp(const char* s1, const char* s2, size_t count);
+
+// Wrapper for vsnprintf, snprintf that always NUL-terminates and always
+// returns the number of characters that would be in an untruncated formatted
+// string, even when truncation occurs.
+int VSNPrintF(char* buffer, size_t size,
+	      const char* format, va_list arguments);
+int SNPrintF(char* buffer, size_t size, const char* format, ...);
+
+// The Wcs* variants operate on NUL-terminated wchar_t* strings, like the
+// standard library's wcs* functions.  Otherwise, these behave the same as
+// the Str* variants above.
+
+bool WcsCpy(wchar_t* dest, const wchar_t* src, size_t dst_size);
+bool WcsNCpy(wchar_t* dest, const wchar_t* src, size_t dst_size);
+
+int VSWPrintF(wchar_t* buffer, size_t size,
+              const wchar_t* format, va_list arguments);
+int SWPrintF(wchar_t* buffer, size_t size, const wchar_t* format, ...);
+
+// Some of these implementations need to be inlined.
+
+#if defined(WIN32)
+#include "base/string_util_win.h"
+#elif defined(__APPLE__)
+#include "base/string_util_mac.h"
+#else
+#error Define string operations appropriately for your platform
+#endif
+
+inline int SNPrintF(char* buffer, size_t size, const char* format, ...) {
+  va_list arguments;
+  va_start(arguments, format);
+  int result = VSNPrintF(buffer, size, format, arguments);
+  va_end(arguments);
+  return result;
+}
+
+inline int SWPrintF(wchar_t* buffer, size_t size, const wchar_t* format, ...) {
+  va_list arguments;
+  va_start(arguments, format);
+  int result = VSWPrintF(buffer, size, format, arguments);
+  va_end(arguments);
+  return result;
+}
+
+// Returns a reference to a globally unique empty string that functions can
+// return.  Use this to avoid static construction of strings, not to replace
+// any and all uses of "std::string()" as nicer-looking sugar.
+// These functions are threadsafe.
+const std::string& EmptyString();
+const std::wstring& EmptyWString();
+
+extern const wchar_t kWhitespaceWide[];
+extern const char kWhitespaceASCII[];
+
+// Names of codepages (charsets) understood by icu.
+extern const char* const kCodepageUTF8;
+
+// Removes characters in trim_chars from the beginning and end of input.
+// NOTE: Safe to use the same variable for both input and output.
+bool TrimString(const std::wstring& input,
+                wchar_t trim_chars[],
+                std::wstring* output);
+bool TrimString(const std::string& input,
+                char trim_chars[],
+                std::string* output);
+
+// Trims any whitespace from either end of the input string.  Returns where
+// whitespace was found.  The non-wide version of this function only looks for
+// ASCII whitespace; UTF-8 code-points are not searched for (use the wide
+// version instead).
+// NOTE: Safe to use the same variable for both input and output.
+enum TrimPositions {
+  TRIM_NONE     = 0,
+  TRIM_LEADING  = 1 << 0,
+  TRIM_TRAILING = 1 << 1,
+  TRIM_ALL      = TRIM_LEADING | TRIM_TRAILING,
+};
+TrimPositions TrimWhitespace(const std::wstring& input,
+                             TrimPositions positions,
+                             std::wstring* output);
+TrimPositions TrimWhitespace(const std::string& input,
+                             TrimPositions positions,
+                             std::string* output);
+
+// Searches  for CR or LF characters.  Removes all contiguous whitespace
+// strings that contain them.  This is useful when trying to deal with text
+// copied from terminals.
+// Returns |text, with the following three transformations:
+// (1) Leading and trailing whitespace is trimmed.
+// (2) If |trim_sequences_with_line_breaks| is true, any other whitespace
+//     sequences containing a CR or LF are trimmed.
+// (3) All other whitespace sequences are converted to single spaces.
+std::wstring CollapseWhitespace(const std::wstring& text,
+                                bool trim_sequences_with_line_breaks);
+
+// These convert between ASCII (7-bit) and UTF16 strings.
+std::string WideToASCII(const std::wstring& wide);
+std::wstring ASCIIToWide(const std::string& ascii);
+
+// These convert between UTF8 and UTF16 strings. They are potentially slow,
+// so avoid unnecessary conversions. Most things should be in UTF16.
+std::string WideToUTF8(const std::wstring& wide);
+std::wstring UTF8ToWide(const std::string& utf8);
+
+// Converts between wide strings and whatever the native multibyte encoding
+// is. The native multibyte encoding on English machines will often Latin-1,
+// but could be ShiftJIS or even UTF-8, among others.
+//
+// These functions can be dangerous. Do not use unless you are sure you are
+// giving them to/getting them from somebody who expects the current platform
+// 8-bit encoding.
+std::string WideToNativeMB(const std::wstring& wide);
+std::wstring NativeMBToWide(const std::string& native_mb);
+
+// Defines the error handling modes of WideToCodepage and CodepageToWide.
+class OnStringUtilConversionError {
+ public:
+  enum Type {
+    // The function will return failure. The output buffer will be empty.
+    FAIL,
+
+    // The offending characters are skipped and the conversion will proceed as
+    // if they did not exist.
+    SKIP,
+  };
+
+ private:
+  OnStringUtilConversionError();
+};
+
+// Converts between wide strings and the encoding specified.  If the
+// encoding doesn't exist or the encoding fails (when on_error is FAIL),
+// returns false.
+bool WideToCodepage(const std::wstring& wide,
+                    const char* codepage_name,
+                    OnStringUtilConversionError::Type on_error,
+                    std::string* encoded);
+bool CodepageToWide(const std::string& encoded,
+                    const char* codepage_name,
+                    OnStringUtilConversionError::Type on_error,
+                    std::wstring* wide);
+
+// Converts the given wide string to the corresponding Latin1. This will fail
+// (return false) if any characters are more than 255.
+bool WideToLatin1(const std::wstring& wide, std::string* latin1);
+
+// Returns true if the specified string matches the criteria. How can a wide
+// string be 8-bit or UTF8? It contains only characters that are < 256 (in the
+// first case) or characters that use only 8-bits and whose 8-bit
+// representation looks like a UTF-8 string (the second case).
+bool IsString8Bit(const std::wstring& str);
+bool IsStringUTF8(const char* str);
+bool IsStringWideUTF8(const wchar_t* str);
+bool IsStringASCII(const std::wstring& str);
+bool IsStringASCII(const std::string& str);
+
+// ASCII-specific tolower.  The standard library's tolower is locale sensitive,
+// so we don't want to use it here.
+template <class Char> inline Char ToLowerASCII(Char c) {
+  return (c >= 'A' && c <= 'Z') ? (c + ('a' - 'A')) : c;
+}
+
+// Converts the elements of the given string.  This version uses a pointer to
+// clearly differentiate it from the non-pointer variant.
+template <class str> inline void StringToLowerASCII(str* s) {
+  for (typename str::iterator i = s->begin(); i != s->end(); ++i)
+    *i = ToLowerASCII(*i);
+}
+
+template <class str> inline str StringToLowerASCII(const str& s) {
+  // for std::string and std::wstring
+  str output(s);
+  StringToLowerASCII(&output);
+  return output;
+}
+
+// Compare the lower-case form of the given string against the given ASCII
+// string.  This is useful for doing checking if an input string matches some
+// token, and it is optimized to avoid intermediate string copies.  This API is
+// borrowed from the equivalent APIs in Mozilla.
+bool LowerCaseEqualsASCII(const std::string& a, const char* b);
+bool LowerCaseEqualsASCII(const std::wstring& a, const char* b);
+
+// Same thing, but with string iterators instead.
+bool LowerCaseEqualsASCII(std::string::const_iterator a_begin,
+                          std::string::const_iterator a_end,
+                          const char* b);
+bool LowerCaseEqualsASCII(std::wstring::const_iterator a_begin,
+                          std::wstring::const_iterator a_end,
+                          const char* b);
+bool LowerCaseEqualsASCII(const char* a_begin,
+                          const char* a_end,
+                          const char* b);
+bool LowerCaseEqualsASCII(const wchar_t* a_begin,
+                          const wchar_t* a_end,
+                          const char* b);
+
+// Returns true if str starts with search, or false otherwise.
+// This only works on ASCII strings.
+bool StartsWithASCII(const std::string& str,
+                     const std::string& search,
+                     bool case_sensitive);
+
+// Determines the type of ASCII character, independent of locale (the C
+// library versions will change based on locale).
+template <typename Char>
+inline bool IsAsciiWhitespace(Char c) {
+  return c == ' ' || c == '\r' || c == '\n' || c == '\t';
+}
+template <typename Char>
+inline bool IsAsciiAlpha(Char c) {
+  return ((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z'));
+}
+template <typename Char>
+inline bool IsAsciiDigit(Char c) {
+  return c >= '0' && c <= '9';
+}
+
+// Returns true if it's a whitespace character.
+inline bool IsWhitespace(wchar_t c) {
+  return wcschr(kWhitespaceWide, c) != NULL;
+}
+
+// TODO(mpcomplete): Decide if we should change these names to KIBI, etc,
+// or if we should actually use metric units, or leave as is.
+enum DataUnits {
+  DATA_UNITS_BYTE = 0,
+  DATA_UNITS_KILOBYTE,
+  DATA_UNITS_MEGABYTE,
+  DATA_UNITS_GIGABYTE,
+};
+
+// Return the unit type that is appropriate for displaying the amount of bytes
+// passed in.
+DataUnits GetByteDisplayUnits(int64 bytes);
+
+// Return a byte string in human-readable format, displayed in units appropriate
+// specified by 'units', with an optional unit suffix.
+// Ex: FormatBytes(512, DATA_UNITS_KILOBYTE, true) => "0.5 KB"
+// Ex: FormatBytes(10*1024, DATA_UNITS_MEGABYTE, false) => "0.1"
+std::wstring FormatBytes(int64 bytes, DataUnits units, bool show_units);
+
+// As above, but with "/s" units.
+// Ex: FormatSpeed(512, DATA_UNITS_KILOBYTE, true) => "0.5 KB/s"
+// Ex: FormatSpeed(10*1024, DATA_UNITS_MEGABYTE, false) => "0.1"
+std::wstring FormatSpeed(int64 bytes, DataUnits units, bool show_units);
+
+// Return a number formated with separators in the user's locale way.
+// Ex: FormatNumber(1234567) => 1,234,567
+std::wstring FormatNumber(int64 number);
+
+// Starting at |start_offset| (usually 0), look through |str| and replace all
+// instances of |find_this| with |replace_with|.
+//
+// This does entire substrings; use std::replace in <algorithm> for single
+// characters, for example:
+//   std::replace(str.begin(), str.end(), 'a', 'b');
+void ReplaceSubstringsAfterOffset(std::wstring* str,
+                                  std::wstring::size_type start_offset,
+                                  const std::wstring& find_this,
+                                  const std::wstring& replace_with);
+void ReplaceSubstringsAfterOffset(std::string* str,
+                                  std::string::size_type start_offset,
+                                  const std::string& find_this,
+                                  const std::string& replace_with);
+
+// Specialized string-conversion functions.
+std::string Uint64ToString(uint64 value);
+std::string IntToString(int value);
+std::string Int64ToString(int64 value);
+std::wstring Int64ToWString(int64 value);
+std::wstring IntToWString(int value);
+int64 StringToInt64(const std::string& value);
+int64 StringToInt64(const std::wstring& value);
+
+// Return a C++ string given printf-like input.
+std::string StringPrintf(const char* format, ...);
+std::wstring StringPrintf(const wchar_t* format, ...);
+
+// Store result into a supplied string and return it
+const std::string& SStringPrintf(std::string* dst, const char* format, ...);
+const std::wstring& SStringPrintf(std::wstring* dst,
+                                  const wchar_t* format, ...);
+
+// Append result to a supplied string
+void StringAppendF(std::string* dst, const char* format, ...);
+void StringAppendF(std::wstring* dst, const wchar_t* format, ...);
+
+// Lower-level routine that takes a va_list and appends to a specified
+// string.  All other routines are just convenience wrappers around it.
+void StringAppendV(std::string* dst, const char* format, va_list ap);
+void StringAppendV(std::wstring* dst, const wchar_t* format, va_list ap);
+
+// This is mpcomplete's pattern for saving a string copy when dealing with
+// a function that writes results into a wchar_t[] and wanting the result to
+// end up in a std::wstring.  It ensures that the std::wstring's internal
+// buffer has enough room to store the characters to be written into it, and
+// sets its .length() attribute to the right value.
+//
+// The reserve() call allocates the memory required to hold the string
+// plus a terminating null.  This is done because resize() isn't
+// guaranteed to reserve space for the null.  The resize() call is
+// simply the only way to change the string's 'length' member.
+//
+// XXX-performance: the call to wide.resize() takes linear time, since it fills
+// the string's buffer with nulls.  I call it to change the length of the
+// string (needed because writing directly to the buffer doesn't do this).
+// Perhaps there's a constant-time way to change the string's length.
+template <class char_type>
+inline char_type* WriteInto(
+    std::basic_string<char_type, std::char_traits<char_type>,
+                      std::allocator<char_type> >* str,
+    size_t length_including_null) {
+  str->reserve(length_including_null);
+  str->resize(length_including_null - 1);
+  return &((*str)[0]);
+}
+
+//-----------------------------------------------------------------------------
+// CharTraits is provides wrappers with common function names for char/wchar_t
+// specific CRT functions.
+
+template <class CharT> struct CharTraits {
+};
+
+template <>
+struct CharTraits<char> {
+  static inline size_t length(const char* s) {
+    return strlen(s);
+  }
+  static inline bool copy(char* dst, size_t dst_size, const char* s) {
+    return StrCpy(dst, s, dst_size);
+  }
+  static inline bool copy_num(char* dst, size_t dst_size, const char* s,
+                              size_t s_len) {
+    if (dst_size < (s_len + 1))
+      return false;
+    memcpy(dst, s, s_len);
+    dst[s_len] = '\0';
+    return true;
+  }
+};
+
+template <>
+struct CharTraits<wchar_t> {
+  static inline size_t length(const wchar_t* s) {
+    return wcslen(s);
+  }
+  static inline bool copy(wchar_t* dst, size_t dst_size, const wchar_t* s) {
+    return WcsCpy(dst, s, dst_size);
+  }
+  static inline bool copy_num(wchar_t* dst, size_t dst_size, const wchar_t* s,
+                              size_t s_len) {
+    if (dst_size < (s_len + 1))
+      return false;
+    memcpy(dst, s, s_len * sizeof(wchar_t));
+    dst[s_len] = '\0';
+    return true;
+  }
+};
+
+//-----------------------------------------------------------------------------
+
+// Function objects to aid in comparing/searching strings.
+
+template<typename Char> struct CaseInsensitiveCompare {
+ public:
+  bool operator()(Char x, Char y) const {
+    return tolower(x) == tolower(y);
+  }
+};
+
+template<typename Char> struct CaseInsensitiveCompareASCII {
+ public:
+  bool operator()(Char x, Char y) const {
+    return ToLowerASCII(x) == ToLowerASCII(y);
+  }
+};
+
+//-----------------------------------------------------------------------------
+
+// Splits |str| into a vector of strings delimited by |s|. Append the results
+// into |r| as they appear. If several instances of |s| are contiguous, or if
+// |str| begins with or ends with |s|, then an empty string is inserted.
+//
+// Every substring is trimmed of any leading or trailing white space.
+void SplitString(const std::wstring& str,
+                 wchar_t s,
+                 std::vector<std::wstring>* r);
+void SplitString(const std::string& str,
+                 char s,
+                 std::vector<std::string>* r);
+
+// The same as SplitString, but don't trim white space.
+void SplitStringDontTrim(const std::wstring& str,
+                         wchar_t s,
+                         std::vector<std::wstring>* r);
+void SplitStringDontTrim(const std::string& str,
+                         char s,
+                         std::vector<std::string>* r);
+
+// WARNING: this uses whitespace as defined by the HTML5 spec. If you need
+// a function similar to this but want to trim all types of whitespace, then
+// factor this out into a function that takes a string containing the characters
+// that are treated as whitespace.
+//
+// Splits the string along whitespace (where whitespace is the five space
+// characters defined by HTML 5). Each contiguous block of non-whitespace
+// characters is added to result.
+void SplitStringAlongWhitespace(const std::wstring& str,
+                                std::vector<std::wstring>* result);
+
+// Replace $1-$2-$3 in the format string with |a| and |b| respectively.
+// Additionally, $$ is replaced by $. The offset/offsets parameter here can be
+// NULL.
+std::wstring ReplaceStringPlaceholders(const std::wstring& format_string,
+                                       const std::wstring& a,
+                                       size_t* offset);
+
+std::wstring ReplaceStringPlaceholders(const std::wstring& format_string,
+                                       const std::wstring& a,
+                                       const std::wstring& b,
+                                       std::vector<size_t>* offsets);
+
+std::wstring ReplaceStringPlaceholders(const std::wstring& format_string,
+                                       const std::wstring& a,
+                                       const std::wstring& b,
+                                       const std::wstring& c,
+                                       std::vector<size_t>* offsets);
+
+std::wstring ReplaceStringPlaceholders(const std::wstring& format_string,
+                                       const std::wstring& a,
+                                       const std::wstring& b,
+                                       const std::wstring& c,
+                                       const std::wstring& d,
+                                       std::vector<size_t>* offsets);
+
+// Returns true if the string passed in matches the pattern. The pattern
+// string can contain wildcards like * and ?
+// TODO(iyengar) This function may not work correctly for CJK strings as
+// it does individual character matches.
+// The backslash character (\) is an escape character for * and ?
+bool MatchPattern(const std::wstring& string, const std::wstring& pattern);
+bool MatchPattern(const std::string& string, const std::string& pattern);
+
+#endif  // BASE_STRING_UTIL_H__
diff --git a/base/string_util_icu.cc b/base/string_util_icu.cc
new file mode 100644
index 0000000..797ccbd
--- /dev/null
+++ b/base/string_util_icu.cc
@@ -0,0 +1,201 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#include "base/string_util.h"
+
+#include <string.h>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/singleton.h"
+#include "unicode/ucnv.h"
+#include "unicode/numfmt.h"
+#include "unicode/ustring.h"
+
+// Codepage <-> Wide -----------------------------------------------------------
+
+// Convert a unicode string into the specified codepage_name.  If the codepage
+// isn't found, return false.
+bool WideToCodepage(const std::wstring& wide,
+                    const char* codepage_name,
+                    OnStringUtilConversionError::Type on_error,
+                    std::string* encoded) {
+  encoded->clear();
+
+  UErrorCode status = U_ZERO_ERROR;
+  UConverter* converter = ucnv_open(codepage_name, &status);
+  if (!U_SUCCESS(status))
+    return false;
+
+  const UChar* uchar_src;
+  int uchar_len;
+#ifdef U_WCHAR_IS_UTF16
+  uchar_src = wide.c_str();
+  uchar_len = static_cast<int>(wide.length());
+#else  // U_WCHAR_IS_UTF16
+  // When wchar_t is wider than UChar (16 bits), transform |wide| into a
+  // UChar* string.  Size the UChar* buffer to be large enough to hold twice
+  // as many UTF-16 code points as there are UCS-4 characters, in case each
+  // character translates to a UTF-16 surrogate pair, and leave room for a NUL
+  // terminator.
+  std::vector<UChar> wide_uchar(wide.length() * 2 + 1);
+  u_strFromWCS(&wide_uchar[0], wide_uchar.size(), &uchar_len,
+               wide.c_str(), wide.length(), &status);
+  uchar_src = &wide_uchar[0];
+  DCHECK(U_SUCCESS(status)) << "failed to convert wstring to UChar*";
+#endif  // U_WCHAR_IS_UTF16
+
+  int encoded_max_length = UCNV_GET_MAX_BYTES_FOR_STRING(uchar_len,
+    ucnv_getMaxCharSize(converter));
+  encoded->resize(encoded_max_length);
+
+  // Setup our error handler.
+  switch (on_error) {
+    case OnStringUtilConversionError::FAIL:
+      ucnv_setFromUCallBack(converter, UCNV_FROM_U_CALLBACK_STOP, 0,
+                            NULL, NULL, &status);
+      break;
+    case OnStringUtilConversionError::SKIP:
+      ucnv_setFromUCallBack(converter, UCNV_FROM_U_CALLBACK_SKIP, 0,
+                            NULL, NULL, &status);
+      break;
+    default:
+      NOTREACHED();
+  }
+
+  // ucnv_fromUChars returns size not including terminating null
+  int actual_size = ucnv_fromUChars(converter, &(*encoded)[0],
+    encoded_max_length, uchar_src, uchar_len, &status);
+  encoded->resize(actual_size);
+  ucnv_close(converter);
+  if (U_SUCCESS(status))
+    return true;
+  encoded->clear();  // Make sure the output is empty on error.
+  return false;
+}
+
+// Converts a string of the given codepage into unicode.
+// If the codepage isn't found, return false.
+bool CodepageToWide(const std::string& encoded,
+                    const char* codepage_name,
+                    OnStringUtilConversionError::Type on_error,
+                    std::wstring* wide) {
+  wide->clear();
+
+  UErrorCode status = U_ZERO_ERROR;
+  UConverter* converter = ucnv_open(codepage_name, &status);
+  if (!U_SUCCESS(status))
+    return false;
+
+  // The worst case is all the input characters are non-BMP (32-bit) ones.
+  size_t uchar_max_length = encoded.length() * 2 + 1;
+
+  UChar* uchar_dst;
+#ifdef U_WCHAR_IS_UTF16
+  uchar_dst = WriteInto(wide, uchar_max_length);
+#else
+  // When wchar_t is wider than UChar (16 bits), convert into a temporary
+  // UChar* buffer.
+  std::vector<UChar> wide_uchar(uchar_max_length);
+  uchar_dst = &wide_uchar[0];
+#endif  // U_WCHAR_IS_UTF16
+
+  // Setup our error handler.
+  switch (on_error) {
+    case OnStringUtilConversionError::FAIL:
+      ucnv_setToUCallBack(converter, UCNV_TO_U_CALLBACK_STOP, 0,
+                          NULL, NULL, &status);
+      break;
+    case OnStringUtilConversionError::SKIP:
+      ucnv_setToUCallBack(converter, UCNV_TO_U_CALLBACK_SKIP, 0,
+                          NULL, NULL, &status);
+      break;
+    default:
+      NOTREACHED();
+  }
+
+  int actual_size = ucnv_toUChars(converter,
+                                  uchar_dst,
+                                  static_cast<int>(uchar_max_length),
+                                  encoded.data(),
+                                  static_cast<int>(encoded.length()),
+                                  &status);
+  ucnv_close(converter);
+  if (!U_SUCCESS(status)) {
+    wide->clear();  // Make sure the output is empty on error.
+    return false;
+  }
+
+#ifndef U_WCHAR_IS_UTF16
+  // When wchar_t is wider than UChar (16 bits), it's not possible to wind up
+  // with any more wchar_t elements than UChar elements.  ucnv_toUChars
+  // returns the number of UChar elements not including the NUL terminator, so
+  // leave extra room for that.
+  u_strToWCS(WriteInto(wide, actual_size + 1), actual_size + 1, &actual_size,
+             uchar_dst, actual_size, &status);
+  DCHECK(U_SUCCESS(status)) << "failed to convert UChar* to wstring";
+#endif  // U_WCHAR_IS_UTF16
+
+  wide->resize(actual_size);
+  return true;
+}
+
+// Number formatting -----------------------------------------------------------
+
+// TODO: http://b/id=1092584 Come up with a portable pthread_once, and use
+// that to keep a singleton instead of putting it in the platform-dependent
+// file.
+NumberFormat* NumberFormatSingleton();
+
+std::wstring FormatNumber(int64 number) {
+  NumberFormat* number_format = NumberFormatSingleton();
+  if (!number_format) {
+    // As a fallback, just return the raw number in a string.
+    return StringPrintf(L"%lld", number);
+  }
+  UnicodeString ustr;
+  number_format->format(number, ustr);
+
+#ifdef U_WCHAR_IS_UTF16
+  return std::wstring(ustr.getBuffer(),
+                      static_cast<std::wstring::size_type>(ustr.length()));
+#else  // U_WCHAR_IS_UTF16
+  wchar_t buffer[64];  // A int64 is less than 20 chars long,  so 64 chars
+                       // leaves plenty of room for formating stuff.
+  int length = 0;
+  UErrorCode error = U_ZERO_ERROR;
+  u_strToWCS(buffer, 64, &length, ustr.getBuffer(), ustr.length() , &error);
+  if (U_FAILURE(error)) {
+    NOTREACHED();
+    // As a fallback, just return the raw number in a string.
+    return StringPrintf(L"%lld", number);
+  }
+  return std::wstring(buffer, static_cast<std::wstring::size_type>(length));
+#endif  // U_WCHAR_IS_UTF16
+}
diff --git a/base/string_util_unittest.cc b/base/string_util_unittest.cc
new file mode 100644
index 0000000..c6ff622
--- /dev/null
+++ b/base/string_util_unittest.cc
@@ -0,0 +1,848 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <sstream>
+#include <stdarg.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+}
+
+static const struct trim_case {
+  const wchar_t* input;
+  const TrimPositions positions;
+  const wchar_t* output;
+  const TrimPositions return_value;
+} trim_cases[] = {
+  {L" Google Video ", TRIM_LEADING, L"Google Video ", TRIM_LEADING},
+  {L" Google Video ", TRIM_TRAILING, L" Google Video", TRIM_TRAILING},
+  {L" Google Video ", TRIM_ALL, L"Google Video", TRIM_ALL},
+  {L"Google Video", TRIM_ALL, L"Google Video", TRIM_NONE},
+  {L"", TRIM_ALL, L"", TRIM_NONE},
+  {L"  ", TRIM_LEADING, L"", TRIM_LEADING},
+  {L"  ", TRIM_TRAILING, L"", TRIM_TRAILING},
+  {L"  ", TRIM_ALL, L"", TRIM_ALL},
+  {L"\t\rTest String\n", TRIM_ALL, L"Test String", TRIM_ALL},
+  {L"\x2002Test String\x00A0\x3000", TRIM_ALL, L"Test String", TRIM_ALL},
+};
+
+static const struct trim_case_ascii {
+  const char* input;
+  const TrimPositions positions;
+  const char* output;
+  const TrimPositions return_value;
+} trim_cases_ascii[] = {
+  {" Google Video ", TRIM_LEADING, "Google Video ", TRIM_LEADING},
+  {" Google Video ", TRIM_TRAILING, " Google Video", TRIM_TRAILING},
+  {" Google Video ", TRIM_ALL, "Google Video", TRIM_ALL},
+  {"Google Video", TRIM_ALL, "Google Video", TRIM_NONE},
+  {"", TRIM_ALL, "", TRIM_NONE},
+  {"  ", TRIM_LEADING, "", TRIM_LEADING},
+  {"  ", TRIM_TRAILING, "", TRIM_TRAILING},
+  {"  ", TRIM_ALL, "", TRIM_ALL},
+  {"\t\rTest String\n", TRIM_ALL, "Test String", TRIM_ALL},
+  {"\x85Test String\xa0\x20", TRIM_ALL, "Test String", TRIM_ALL},
+};
+
+TEST(StringUtilTest, TrimWhitespace) {
+  std::wstring output;  // Allow contents to carry over to next testcase
+  for (int i = 0; i < arraysize(trim_cases); ++i) {
+    const trim_case& value = trim_cases[i];
+    EXPECT_EQ(value.return_value,
+              TrimWhitespace(value.input, value.positions, &output));
+    EXPECT_EQ(value.output, output);
+  }
+
+  // Test that TrimWhitespace() can take the same string for input and output
+  output = L"  This is a test \r\n";
+  EXPECT_EQ(TRIM_ALL, TrimWhitespace(output, TRIM_ALL, &output));
+  EXPECT_EQ(L"This is a test", output);
+
+  // Once more, but with a string of whitespace
+  output = L"  \r\n";
+  EXPECT_EQ(TRIM_ALL, TrimWhitespace(output, TRIM_ALL, &output));
+  EXPECT_EQ(L"", output);
+
+  std::string output_ascii;
+  for (int i = 0; i < arraysize(trim_cases_ascii); ++i) {
+    const trim_case_ascii& value = trim_cases_ascii[i];
+    EXPECT_EQ(value.return_value,
+              TrimWhitespace(value.input, value.positions, &output_ascii));
+    EXPECT_EQ(value.output, output_ascii);
+  }
+}
+
+static const struct collapse_case {
+  const wchar_t* input;
+  const bool trim;
+  const wchar_t* output;
+} collapse_cases[] = {
+  {L" Google Video ", false, L"Google Video"},
+  {L"Google Video", false, L"Google Video"},
+  {L"", false, L""},
+  {L"  ", false, L""},
+  {L"\t\rTest String\n", false, L"Test String"},
+  {L"\x2002Test String\x00A0\x3000", false, L"Test String"},
+  {L"    Test     \n  \t String    ", false, L"Test String"},
+  {L"\x2002Test\x1680 \x2028 \tString\x00A0\x3000", false, L"Test String"},
+  {L"   Test String", false, L"Test String"},
+  {L"Test String    ", false, L"Test String"},
+  {L"Test String", false, L"Test String"},
+  {L"", true, L""},
+  {L"\n", true, L""},
+  {L"  \r  ", true, L""},
+  {L"\nFoo", true, L"Foo"},
+  {L"\r  Foo  ", true, L"Foo"},
+  {L" Foo bar ", true, L"Foo bar"},
+  {L"  \tFoo  bar  \n", true, L"Foo bar"},
+  {L" a \r b\n c \r\n d \t\re \t f \n ", true, L"abcde f"},
+};
+
+TEST(StringUtilTest, CollapseWhitespace) {
+  for (int i = 0; i < arraysize(collapse_cases); ++i) {
+    const collapse_case& value = collapse_cases[i];
+    EXPECT_EQ(value.output, CollapseWhitespace(value.input, value.trim));
+  }
+}
+
+static const wchar_t* const kConvertRoundtripCases[] = {
+  L"Google Video",
+  // "网页 图片 资讯更多 »"
+  L"\x7f51\x9875\x0020\x56fe\x7247\x0020\x8d44\x8baf\x66f4\x591a\x0020\x00bb",
+  //  "Παγκόσμιος Ιστός"
+  L"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9"
+  L"\x03bf\x03c2\x0020\x0399\x03c3\x03c4\x03cc\x03c2",
+  // "Поиск страниц на русском"
+  L"\x041f\x043e\x0438\x0441\x043a\x0020\x0441\x0442"
+  L"\x0440\x0430\x043d\x0438\x0446\x0020\x043d\x0430"
+  L"\x0020\x0440\x0443\x0441\x0441\x043a\x043e\x043c",
+  // "전체서비스"
+  L"\xc804\xccb4\xc11c\xbe44\xc2a4",
+  // ?????  (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 : A,B,C,D,E)
+  L"\xd807\xdd40\xd807\xdd41\xd807\xdd42\xd807\xdd43\xd807\xdd44",
+
+  // Test a character that takes more than 16-bits. This will depend on whether
+  // wchar_t is 16 or 32 bits.
+  #ifdef WIN32
+    L"\xd800\xdf00",
+  #else
+    "\x10300,
+  #endif
+};
+
+TEST(StringUtilTest, ConvertUTF8AndWide) {
+  // we round-trip all the wide strings through UTF-8 to make sure everything
+  // agrees on the conversion. This uses the stream operators to test them
+  // simultaneously.
+  for (int i = 0; i < arraysize(kConvertRoundtripCases); ++i) {
+    std::ostringstream utf8;
+    utf8 << WideToUTF8(kConvertRoundtripCases[i]);
+    std::wostringstream wide;
+    wide << UTF8ToWide(utf8.str());
+
+    EXPECT_EQ(kConvertRoundtripCases[i], wide.str());
+  }
+}
+
+TEST(StringUtilTest, ConvertUTF8AndWideEmptyString) {
+  // An empty std::wstring should be converted to an empty std::string,
+  // and vice versa.
+  std::wstring wempty;
+  std::string empty;
+  EXPECT_EQ(empty, WideToUTF8(wempty));
+  EXPECT_EQ(wempty, UTF8ToWide(empty));
+}
+
+TEST(StringUtilTest, ConvertMultiString) {
+  static wchar_t wmulti[] = {
+    L'f', L'o', L'o', L'\0',
+    L'b', L'a', L'r', L'\0',
+    L'b', L'a', L'z', L'\0',
+    L'\0'
+  };
+  static char multi[] = {
+    'f', 'o', 'o', '\0',
+    'b', 'a', 'r', '\0',
+    'b', 'a', 'z', '\0',
+    '\0'
+  };
+  std::wstring wmultistring;
+  memcpy(WriteInto(&wmultistring, arraysize(wmulti)), wmulti, sizeof(wmulti));
+  EXPECT_EQ(arraysize(wmulti) - 1, wmultistring.length());
+  std::string expected;
+  memcpy(WriteInto(&expected, arraysize(multi)), multi, sizeof(multi));
+  EXPECT_EQ(arraysize(multi) - 1, expected.length());
+  const std::string& converted = WideToUTF8(wmultistring);
+  EXPECT_EQ(arraysize(multi) - 1, converted.length());
+  EXPECT_EQ(expected, converted);
+}
+
+TEST(StringUtilTest, ConvertCodepageUTF8) {
+  // Make sure WideToCodepage works like WideToUTF8.
+  for (int i = 0; i < arraysize(kConvertRoundtripCases); ++i) {
+    std::string expected(WideToUTF8(kConvertRoundtripCases[i]));
+    std::string utf8;
+    EXPECT_TRUE(WideToCodepage(kConvertRoundtripCases[i], kCodepageUTF8,
+                               OnStringUtilConversionError::SKIP, &utf8));
+    EXPECT_EQ(expected, utf8);
+  }
+}
+
+TEST(StringUtilTest, ConvertBetweenCodepageAndWide) {
+  static const struct {
+    const char* codepage_name;
+    const char* encoded;
+    OnStringUtilConversionError::Type on_error;
+    bool success;
+    const wchar_t* wide;
+  } kConvertCodepageCases[] = {
+    // Test a case where the input can no be decoded, using both SKIP and FAIL
+    // error handling rules. "A7 41" is valid, but "A6" isn't.
+    {"big5",
+     "\xA7\x41\xA6",
+     OnStringUtilConversionError::FAIL,
+     false,
+     L""},
+    {"big5",
+     "\xA7\x41\xA6",
+     OnStringUtilConversionError::SKIP,
+     true,
+     L"\x4F60"},
+    // Arabic (ISO-8859)
+    {"iso-8859-6",
+     "\xC7\xEE\xE4\xD3\xF1\xEE\xE4\xC7\xE5\xEF" " "
+     "\xD9\xEE\xE4\xEE\xEA\xF2\xE3\xEF\xE5\xF2",
+     OnStringUtilConversionError::FAIL,
+     true,
+     L"\x0627\x064E\x0644\x0633\x0651\x064E\x0644\x0627\x0645\x064F" L" "
+     L"\x0639\x064E\x0644\x064E\x064A\x0652\x0643\x064F\x0645\x0652"},
+    // Chinese Simplified (GB2312)
+    {"gb2312",
+     "\xC4\xE3\xBA\xC3",
+     OnStringUtilConversionError::FAIL,
+     true,
+     L"\x4F60\x597D"},
+    // Chinese Traditional (BIG5)
+    {"big5",
+     "\xA7\x41\xA6\x6E",
+     OnStringUtilConversionError::FAIL,
+     true,
+     L"\x4F60\x597D"},
+    // Greek (ISO-8859)
+    {"iso-8859-7",
+     "\xE3\xE5\xE9\xDC" " " "\xF3\xEF\xF5",
+     OnStringUtilConversionError::FAIL,
+     true,
+     L"\x03B3\x03B5\x03B9\x03AC" L" " L"\x03C3\x03BF\x03C5"},
+    // Hebrew (Windows)
+    {"windows-1255", /* to be replaced with "iso-8859-8-I"? */
+     "\xF9\xD1\xC8\xEC\xE5\xC9\xED",
+     OnStringUtilConversionError::FAIL,
+     true,
+     L"\x05E9\x05C1\x05B8\x05DC\x05D5\x05B9\x05DD"},
+    // Hindi Devanagari (ISCII)
+    {"iscii-dev",
+     "\xEF\x42" "\xC6\xCC\xD7\xE8\xB3\xDA\xCF",
+     OnStringUtilConversionError::FAIL,
+     true,
+     L"\x0928\x092E\x0938\x094D\x0915\x093E\x0930"},
+    // Korean (EUC)
+    {"euc-kr",
+     "\xBE\xC8\xB3\xE7\xC7\xCF\xBC\xBC\xBF\xE4",
+     OnStringUtilConversionError::FAIL,
+     true,
+     L"\xC548\xB155\xD558\xC138\xC694"},
+    // Japanese (EUC)
+    {"euc-jp",
+     "\xA4\xB3\xA4\xF3\xA4\xCB\xA4\xC1\xA4\xCF",
+     OnStringUtilConversionError::FAIL,
+     true,
+     L"\x3053\x3093\x306B\x3061\x306F"},
+    // Japanese (ISO-2022)
+    {"iso-2022-jp",
+     "\x1B\x24\x42" "\x24\x33\x24\x73\x24\x4B\x24\x41\x24\x4F" "\x1B\x28\x42",
+     OnStringUtilConversionError::FAIL,
+     true,
+     L"\x3053\x3093\x306B\x3061\x306F"},
+    // Japanese (Shift-JIS)
+    {"sjis",
+     "\x82\xB1\x82\xF1\x82\xC9\x82\xBF\x82\xCD",
+     OnStringUtilConversionError::FAIL,
+     true,
+     L"\x3053\x3093\x306B\x3061\x306F"},
+    // Russian (KOI8)
+    {"koi8-r",
+     "\xDA\xC4\xD2\xC1\xD7\xD3\xD4\xD7\xD5\xCA\xD4\xC5",
+     OnStringUtilConversionError::FAIL,
+     true,
+     L"\x0437\x0434\x0440\x0430\x0432\x0441\x0442\x0432"
+     L"\x0443\x0439\x0442\x0435"},
+    // Thai (ISO-8859)
+    {"windows-874", /* to be replaced with "iso-8859-11". */
+     "\xCA\xC7\xD1\xCA\xB4\xD5" "\xA4\xC3\xD1\xBA",
+     OnStringUtilConversionError::FAIL,
+     true,
+     L"\x0E2A\x0E27\x0E31\x0E2A\x0E14\x0E35"
+     L"\x0E04\x0E23\x0e31\x0E1A"},
+  };
+
+  for (int i = 0; i < arraysize(kConvertCodepageCases); ++i) {
+    std::wstring wide;
+    bool success = CodepageToWide(kConvertCodepageCases[i].encoded,
+                                  kConvertCodepageCases[i].codepage_name,
+                                  kConvertCodepageCases[i].on_error,
+                                  &wide);
+    EXPECT_EQ(kConvertCodepageCases[i].success, success);
+    EXPECT_EQ(kConvertCodepageCases[i].wide, wide);
+
+    // When decoding was successful and nothing was skipped, we also check the
+    // reverse conversion.
+    if (success &&
+        kConvertCodepageCases[i].on_error ==
+            OnStringUtilConversionError::FAIL) {
+      std::string encoded;
+      success = WideToCodepage(wide, kConvertCodepageCases[i].codepage_name,
+                               kConvertCodepageCases[i].on_error, &encoded);
+      EXPECT_EQ(kConvertCodepageCases[i].success, success);
+      EXPECT_EQ(kConvertCodepageCases[i].encoded, encoded);
+    }
+  }
+
+  // The above cases handled codepage->wide errors, but not wide->codepage.
+  // Test that here.
+  std::string encoded("Temp data");  // Make sure the string gets cleared.
+
+  // First test going to an encoding that can not represent that character.
+  EXPECT_FALSE(WideToCodepage(L"Chinese\xff27", "iso-8859-1",
+                              OnStringUtilConversionError::FAIL, &encoded));
+  EXPECT_TRUE(encoded.empty());
+  EXPECT_TRUE(WideToCodepage(L"Chinese\xff27", "iso-8859-1",
+                             OnStringUtilConversionError::SKIP, &encoded));
+  EXPECT_STREQ("Chinese", encoded.c_str());
+
+#ifdef WIN32
+  // When we're in UTF-16 mode, test an invalid UTF-16 character in the input.
+  EXPECT_FALSE(WideToCodepage(L"a\xd800z", "iso-8859-1",
+                              OnStringUtilConversionError::FAIL, &encoded));
+  EXPECT_TRUE(encoded.empty());
+  EXPECT_TRUE(WideToCodepage(L"a\xd800z", "iso-8859-1",
+                             OnStringUtilConversionError::SKIP, &encoded));
+  EXPECT_STREQ("az", encoded.c_str());
+#endif
+
+  // Invalid characters should fail.
+  EXPECT_TRUE(WideToCodepage(L"a\xffffz", "iso-8859-1",
+                             OnStringUtilConversionError::SKIP, &encoded));
+  EXPECT_STREQ("az", encoded.c_str());
+
+  // Invalid codepages should fail.
+  EXPECT_FALSE(WideToCodepage(L"Hello, world", "awesome-8571-2",
+                              OnStringUtilConversionError::SKIP, &encoded));
+}
+
+TEST(StringUtilTest, ConvertASCII) {
+  static const char* char_cases[] = {
+    "Google Video",
+    "Hello, world\n",
+    "0123ABCDwxyz \a\b\t\r\n!+,.~"
+  };
+
+  static const wchar_t* const wchar_cases[] = {
+    L"Google Video",
+    L"Hello, world\n",
+    L"0123ABCDwxyz \a\b\t\r\n!+,.~"
+  };
+
+  for (int i = 0; i < arraysize(char_cases); ++i) {
+    EXPECT_TRUE(IsStringASCII(char_cases[i]));
+    std::wstring wide = ASCIIToWide(char_cases[i]);
+    EXPECT_EQ(wchar_cases[i], wide);
+
+    EXPECT_TRUE(IsStringASCII(wchar_cases[i]));
+    std::string ascii = WideToASCII(wchar_cases[i]);
+    EXPECT_EQ(char_cases[i], ascii);
+  }
+
+  EXPECT_FALSE(IsStringASCII("Google \x80Video"));
+  EXPECT_FALSE(IsStringASCII(L"Google \x80Video"));
+
+  // Convert empty strings.
+  std::wstring wempty;
+  std::string empty;
+  EXPECT_EQ(empty, WideToASCII(wempty));
+  EXPECT_EQ(wempty, ASCIIToWide(empty));
+}
+
+static const struct {
+  const wchar_t* src_w;
+  const char*    src_a;
+  const char*    dst;
+} lowercase_cases[] = {
+  {L"FoO", "FoO", "foo"},
+  {L"foo", "foo", "foo"},
+  {L"FOO", "FOO", "foo"},
+};
+
+TEST(StringUtilTest, LowerCaseEqualsASCII) {
+  for (int i = 0; i < arraysize(lowercase_cases); ++i) {
+    EXPECT_TRUE(LowerCaseEqualsASCII(lowercase_cases[i].src_w,
+                                     lowercase_cases[i].dst));
+    EXPECT_TRUE(LowerCaseEqualsASCII(lowercase_cases[i].src_a,
+                                     lowercase_cases[i].dst));
+  }
+}
+
+TEST(StringUtilTest, GetByteDisplayUnits) {
+  static const struct {
+    int64 bytes;
+    DataUnits expected;
+  } cases[] = {
+    {0, DATA_UNITS_BYTE},
+    {512, DATA_UNITS_BYTE},
+    {10*1024, DATA_UNITS_KILOBYTE},
+    {10*1024*1024, DATA_UNITS_MEGABYTE},
+    {10LL*1024*1024*1024, DATA_UNITS_GIGABYTE},
+    {~(1LL<<63), DATA_UNITS_GIGABYTE},
+#ifdef NDEBUG
+    {-1, DATA_UNITS_BYTE},
+#endif
+  };
+
+  for (int i = 0; i < arraysize(cases); ++i)
+    EXPECT_EQ(cases[i].expected, GetByteDisplayUnits(cases[i].bytes));
+}
+
+TEST(StringUtilTest, FormatBytes) {
+  static const struct {
+    int64 bytes;
+    DataUnits units;
+    const wchar_t* expected;
+    const wchar_t* expected_with_units;
+  } cases[] = {
+    {0, DATA_UNITS_BYTE, L"0", L"0 B"},
+    {512, DATA_UNITS_BYTE, L"512", L"512 B"},
+    {512, DATA_UNITS_KILOBYTE, L"0.5", L"0.5 kB"},
+    {1024*1024, DATA_UNITS_KILOBYTE, L"1024", L"1024 kB"},
+    {1024*1024, DATA_UNITS_MEGABYTE, L"1", L"1 MB"},
+    {1024*1024*1024, DATA_UNITS_GIGABYTE, L"1", L"1 GB"},
+    {10LL*1024*1024*1024, DATA_UNITS_GIGABYTE, L"10", L"10 GB"},
+    {~(1LL<<63), DATA_UNITS_GIGABYTE, L"8589934592", L"8589934592 GB"},
+    // Make sure the first digit of the fractional part works.
+    {1024*1024 + 103, DATA_UNITS_KILOBYTE, L"1024.1", L"1024.1 kB"},
+    {1024*1024 + 205 * 1024, DATA_UNITS_MEGABYTE, L"1.2", L"1.2 MB"},
+    {1024*1024*1024 + (927 * 1024*1024), DATA_UNITS_GIGABYTE,
+     L"1.9", L"1.9 GB"},
+    {10LL*1024*1024*1024, DATA_UNITS_GIGABYTE, L"10", L"10 GB"},
+#ifdef NDEBUG
+    {-1, DATA_UNITS_BYTE, L"", L""},
+#endif
+  };
+
+  for (int i = 0; i < arraysize(cases); ++i) {
+    EXPECT_EQ(cases[i].expected,
+              FormatBytes(cases[i].bytes, cases[i].units, false));
+    EXPECT_EQ(cases[i].expected_with_units,
+              FormatBytes(cases[i].bytes, cases[i].units, true));
+  }
+}
+
+TEST(StringUtilTest, ReplaceSubstringsAfterOffset) {
+  static const struct {
+    wchar_t* str;
+    std::wstring::size_type start_offset;
+    wchar_t* find_this;
+    wchar_t* replace_with;
+    wchar_t* expected;
+  } cases[] = {
+    {L"aaa", 0, L"a", L"b", L"bbb"},
+    {L"abb", 0, L"ab", L"a", L"ab"},
+    {L"Removing some substrings inging", 0, L"ing", L"", L"Remov some substrs "},
+    {L"Not found", 0, L"x", L"0", L"Not found"},
+    {L"Not found again", 5, L"x", L"0", L"Not found again"},
+    {L" Making it much longer ", 0, L" ", L"Four score and seven years ago",
+     L"Four score and seven years agoMakingFour score and seven years agoit"
+     L"Four score and seven years agomuchFour score and seven years agolonger"
+     L"Four score and seven years ago"},
+    {L"Invalid offset", 9999, L"t", L"foobar", L"Invalid offset"},
+    {L"Replace me only me once", 9, L"me ", L"", L"Replace me only once"},
+    {L"abababab", 2, L"ab", L"c", L"abccc"},
+  };
+
+  for (int i = 0; i < arraysize(cases); i++) {
+    std::wstring str(cases[i].str);
+    ReplaceSubstringsAfterOffset(&str, cases[i].start_offset,
+                                 cases[i].find_this, cases[i].replace_with);
+    EXPECT_EQ(cases[i].expected, str);
+  }
+}
+
+TEST(StringUtilTest, IntToString) {
+  static const struct {
+    int input;
+    std::string output;
+  } cases[] = {
+    {0, "0"},
+    {42, "42"},
+    {-42, "-42"},
+    {INT_MAX, "2147483647"},
+    {INT_MIN, "-2147483648"},
+  };
+
+  for (int i = 0; i < arraysize(cases); ++i)
+    EXPECT_EQ(cases[i].output, IntToString(cases[i].input));
+}
+
+TEST(StringUtilTest, Uint64ToString) {
+  static const struct {
+    uint64 input;
+    std::string output;
+  } cases[] = {
+    {0, "0"},
+    {42, "42"},
+    {INT_MAX, "2147483647"},
+    {kuint64max, "18446744073709551615"},
+  };
+
+  for (int i = 0; i < arraysize(cases); ++i)
+    EXPECT_EQ(cases[i].output, Uint64ToString(cases[i].input));
+}
+
+// This checks where we can use the assignment operator for a va_list. We need
+// a way to do this since Visual C doesn't support va_copy, but assignment on
+// va_list is not guaranteed to be a copy. See StringAppendVT which uses this
+// capability.
+static void VariableArgsFunc(const char* format, ...) {
+  va_list org;
+  va_start(org, format);
+
+  va_list dup = org;
+  int i1 = va_arg(org, int);
+  int j1 = va_arg(org, int);
+  char* s1 = va_arg(org, char*);
+  double d1 = va_arg(org, double);
+  va_end(org);
+
+  int i2 = va_arg(dup, int);
+  int j2 = va_arg(dup, int);
+  char* s2 = va_arg(dup, char*);
+  double d2 = va_arg(dup, double);
+
+  EXPECT_EQ(i1, i2);
+  EXPECT_EQ(j1, j2);
+  EXPECT_STREQ(s1, s2);
+  EXPECT_EQ(d1, d2);
+
+  va_end(dup);
+}
+
+TEST(StringUtilTest, VAList) {
+  VariableArgsFunc("%d %d %s %lf", 45, 92, "This is interesting", 9.21);
+}
+
+TEST(StringUtilTest, StringPrintfEmptyFormat) {
+  const char* empty = "";
+  EXPECT_EQ("", StringPrintf(empty));
+  EXPECT_EQ("", StringPrintf("%s", ""));
+}
+
+TEST(StringUtilTest, StringPrintfMisc) {
+  EXPECT_EQ("123hello w", StringPrintf("%3d%2s %1c", 123, "hello", 'w'));
+  EXPECT_EQ(L"123hello w", StringPrintf(L"%3d%2s %1c", 123, L"hello", 'w'));
+}
+
+TEST(StringUtilTest, StringAppendfStringEmptyParam) {
+  std::string value("Hello");
+  StringAppendF(&value, "");
+  EXPECT_EQ("Hello", value);
+
+  std::wstring valuew(L"Hello");
+  StringAppendF(&valuew, L"");
+  EXPECT_EQ(L"Hello", valuew);
+}
+
+TEST(StringUtilTest, StringAppendfEmptyString) {
+  std::string value("Hello");
+  StringAppendF(&value, "%s", "");
+  EXPECT_EQ("Hello", value);
+
+  std::wstring valuew(L"Hello");
+  StringAppendF(&valuew, L"%s", L"");
+  EXPECT_EQ(L"Hello", valuew);
+}
+
+TEST(StringUtilTest, StringAppendfString) {
+  std::string value("Hello");
+  StringAppendF(&value, " %s", "World");
+  EXPECT_EQ("Hello World", value);
+
+  std::wstring valuew(L"Hello");
+  StringAppendF(&valuew, L" %s", L"World");
+  EXPECT_EQ(L"Hello World", valuew);
+}
+
+TEST(StringUtilTest, StringAppendfInt) {
+  std::string value("Hello");
+  StringAppendF(&value, " %d", 123);
+  EXPECT_EQ("Hello 123", value);
+
+  std::wstring valuew(L"Hello");
+  StringAppendF(&valuew, L" %d", 123);
+  EXPECT_EQ(L"Hello 123", valuew);
+}
+
+// Make sure that lengths exactly around the initial buffer size are handled
+// correctly.
+TEST(StringUtilTest, StringPrintfBounds) {
+  const int src_len = 1026;
+  char src[src_len];
+  for (int i = 0; i < arraysize(src); i++)
+    src[i] = 'A';
+
+  wchar_t srcw[src_len];
+  for (int i = 0; i < arraysize(srcw); i++)
+    srcw[i] = 'A';
+
+  for (int i = 1; i < 3; i++) {
+    src[src_len - i] = 0;
+    std::string out;
+    SStringPrintf(&out, "%s", src);
+    EXPECT_STREQ(src, out.c_str());
+
+    srcw[src_len - i] = 0;
+    std::wstring outw;
+    SStringPrintf(&outw, L"%s", srcw);
+    EXPECT_STREQ(srcw, outw.c_str());
+  }
+}
+
+// Test very large sprintfs that will cause the buffer to grow.
+TEST(StringUtilTest, Grow) {
+  char src[1026];
+  for (int i = 0; i < arraysize(src); i++)
+    src[i] = 'A';
+  src[1025] = 0;
+
+  char* fmt = "%sB%sB%sB%sB%sB%sB%s";
+
+  std::string out;
+  SStringPrintf(&out, fmt, src, src, src, src, src, src, src);
+
+  char* ref = new char[320000];
+  sprintf_s(ref, 320000, fmt, src, src, src, src, src, src, src);
+
+  EXPECT_STREQ(ref, out.c_str());
+  delete ref;
+}
+
+// Test the boundary condition for the size of the string_util's
+// internal buffer.
+TEST(StringUtilTest, GrowBoundary) {
+  const int string_util_buf_len = 1024;
+  // Our buffer should be one larger than the size of StringAppendVT's stack
+  // buffer.
+  const int buf_len = string_util_buf_len + 1;
+  char src[buf_len + 1];  // Need extra one for NULL-terminator.
+  for (int i = 0; i < buf_len; ++i)
+    src[i] = 'a';
+  src[buf_len] = 0;
+
+  std::string out;
+  SStringPrintf(&out, "%s", src);
+
+  EXPECT_STREQ(src, out.c_str());
+}
+
+// sprintf in Visual Studio fails when given U+FFFF. This tests that the
+// failure case is gracefuly handled.
+TEST(StringUtilTest, Invalid) {
+  wchar_t invalid[2];
+  invalid[0] = 0xffff;
+  invalid[1] = 0;
+
+  std::wstring out;
+  SStringPrintf(&out, L"%s", invalid);
+  EXPECT_STREQ(L"", out.c_str());
+}
+
+// Test for SplitString
+TEST(StringUtilTest, SplitString) {
+  std::vector<std::wstring> r;
+
+  SplitString(L"a,b,c", L',', &r);
+  EXPECT_EQ(r.size(), 3);
+  EXPECT_EQ(r[0], L"a");
+  EXPECT_EQ(r[1], L"b");
+  EXPECT_EQ(r[2], L"c");
+  r.clear();
+
+  SplitString(L"a, b, c", L',', &r);
+  EXPECT_EQ(r.size(), 3);
+  EXPECT_EQ(r[0], L"a");
+  EXPECT_EQ(r[1], L"b");
+  EXPECT_EQ(r[2], L"c");
+  r.clear();
+
+  SplitString(L"a,,c", L',', &r);
+  EXPECT_EQ(r.size(), 3);
+  EXPECT_EQ(r[0], L"a");
+  EXPECT_EQ(r[1], L"");
+  EXPECT_EQ(r[2], L"c");
+  r.clear();
+
+  SplitString(L"", L'*', &r);
+  EXPECT_EQ(r.size(), 1);
+  EXPECT_EQ(r[0], L"");
+  r.clear();
+
+  SplitString(L"foo", L'*', &r);
+  EXPECT_EQ(r.size(), 1);
+  EXPECT_EQ(r[0], L"foo");
+  r.clear();
+
+  SplitString(L"foo ,", L',', &r);
+  EXPECT_EQ(r.size(), 2);
+  EXPECT_EQ(r[0], L"foo");
+  EXPECT_EQ(r[1], L"");
+  r.clear();
+
+  SplitString(L",", L',', &r);
+  EXPECT_EQ(r.size(), 2);
+  EXPECT_EQ(r[0], L"");
+  EXPECT_EQ(r[1], L"");
+  r.clear();
+
+  SplitString(L"\t\ta\t", L'\t', &r);
+  EXPECT_EQ(r.size(), 4);
+  EXPECT_EQ(r[0], L"");
+  EXPECT_EQ(r[1], L"");
+  EXPECT_EQ(r[2], L"a");
+  EXPECT_EQ(r[3], L"");
+  r.clear();
+
+  SplitStringDontTrim(L"\t\ta\t", L'\t', &r);
+  EXPECT_EQ(r.size(), 4);
+  EXPECT_EQ(r[0], L"");
+  EXPECT_EQ(r[1], L"");
+  EXPECT_EQ(r[2], L"a");
+  EXPECT_EQ(r[3], L"");
+  r.clear();
+
+  SplitString(L"\ta\t\nb\tcc", L'\n', &r);
+  EXPECT_EQ(r.size(), 2);
+  EXPECT_EQ(r[0], L"a");
+  EXPECT_EQ(r[1], L"b\tcc");
+  r.clear();
+
+  SplitStringDontTrim(L"\ta\t\nb\tcc", L'\n', &r);
+  EXPECT_EQ(r.size(), 2);
+  EXPECT_EQ(r[0], L"\ta\t");
+  EXPECT_EQ(r[1], L"b\tcc");
+  r.clear();
+}
+
+TEST(StringUtilTest, StartsWith) {
+  EXPECT_EQ(true, StartsWithASCII("javascript:url", "javascript", true));
+  EXPECT_EQ(true, StartsWithASCII("javascript:url", "javascript", false));
+  EXPECT_EQ(true, StartsWithASCII("JavaScript:url", "javascript", false));
+  EXPECT_EQ(false, StartsWithASCII("java", "javascript", true));
+  EXPECT_EQ(false, StartsWithASCII("java", "javascript", false));
+}
+
+TEST(StringUtilTest, GetStringFWithOffsets) {
+  std::vector<size_t> offsets;
+
+  ReplaceStringPlaceholders(L"Hello, $1. Your number is $2.", L"1", L"2",
+                            &offsets);
+  EXPECT_EQ(2, offsets.size());
+  EXPECT_EQ(7, offsets[0]);
+  EXPECT_EQ(25, offsets[1]);
+  offsets.clear();
+
+  ReplaceStringPlaceholders(L"Hello, $2. Your number is $1.", L"1", L"2",
+                            &offsets);
+  EXPECT_EQ(2, offsets.size());
+  EXPECT_EQ(25, offsets[0]);
+  EXPECT_EQ(7, offsets[1]);
+  offsets.clear();
+}
+
+TEST(StringUtilTest, SplitStringAlongWhitespace) {
+  struct TestData {
+    const std::wstring input;
+    const int expected_result_count;
+    const std::wstring output1;
+    const std::wstring output2;
+  } data[] = {
+    { L"a",       1, L"a",  L""   },
+    { L" ",       0, L"",   L""   },
+    { L" a",      1, L"a",  L""   },
+    { L" ab ",    1, L"ab", L""   },
+    { L" ab c",   2, L"ab", L"c"  },
+    { L" ab c ",  2, L"ab", L"c"  },
+    { L" ab cd",  2, L"ab", L"cd" },
+    { L" ab cd ", 2, L"ab", L"cd" },
+    { L" \ta\t",  1, L"a",  L""   },
+    { L" b\ta\t", 2, L"b",  L"a"  },
+    { L" b\tat",  2, L"b",  L"at" },
+    { L"b\tat",   2, L"b",  L"at" },
+    { L"b\t at",  2, L"b",  L"at" },
+  };
+  for (size_t i = 0; i < arraysize(data); ++i) {
+    std::vector<std::wstring> results;
+    SplitStringAlongWhitespace(data[i].input, &results);
+    ASSERT_EQ(data[i].expected_result_count, results.size());
+    if (data[i].expected_result_count > 0)
+      ASSERT_EQ(data[i].output1, results[0]);
+    if (data[i].expected_result_count > 1)
+      ASSERT_EQ(data[i].output2, results[1]);
+  }
+}
+
+TEST(StringUtilTest, MatchPatternTest) {
+  EXPECT_EQ(MatchPattern(L"www.google.com", L"*.com"), true);
+  EXPECT_EQ(MatchPattern(L"www.google.com", L"*"), true);
+  EXPECT_EQ(MatchPattern(L"www.google.com", L"www*.g*.org"), false);
+  EXPECT_EQ(MatchPattern(L"Hello", L"H?l?o"), true);
+  EXPECT_EQ(MatchPattern(L"www.google.com", L"http://*)"), false);
+  EXPECT_EQ(MatchPattern(L"www.msn.com", L"*.COM"), false);
+  EXPECT_EQ(MatchPattern(L"Hello*1234", L"He??o\\*1*"), true);
+  EXPECT_EQ(MatchPattern(L"", L"*.*"), false);
+  EXPECT_EQ(MatchPattern(L"", L"*"), true);
+  EXPECT_EQ(MatchPattern(L"", L"?"), true);
+  EXPECT_EQ(MatchPattern(L"", L""), true);
+  EXPECT_EQ(MatchPattern(L"Hello", L""), false);
+  EXPECT_EQ(MatchPattern(L"Hello*", L"Hello*"), true);
+  EXPECT_EQ(MatchPattern("Hello*", "Hello*"), true);  // narrow string
+}
+
+
diff --git a/base/task.h b/base/task.h
new file mode 100644
index 0000000..a964a17
--- /dev/null
+++ b/base/task.h
@@ -0,0 +1,725 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_TASK_H__
+#define BASE_TASK_H__
+
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/non_thread_safe.h"
+#include "base/revocable_store.h"
+#include "base/tracked.h"
+#include "base/tuple.h"
+
+//------------------------------------------------------------------------------
+// Base class of Task, where we store info to help MessageLoop handle PostTask()
+// elements of Task processing.
+
+class Task;
+
+class MessageLoopOwnable : public tracked_objects::Tracked {
+ public:
+  MessageLoopOwnable() { Reset(); }
+  virtual ~MessageLoopOwnable() {}
+
+  // Use this method to adjust the priority given to a task by MessageLoop.
+  void set_priority(int priority) { priority_ = priority; }
+  int priority() const { return priority_; }
+
+  // Change whether this task will run in nested message loops.
+  void set_nestable(bool nestable) { nestable_ = nestable; }
+  bool nestable() { return nestable_; }
+
+
+ protected:
+  // If a derived class wishes to re-use this instance, then it should override
+  // this method.  This method is called by MessageLoop after processing a task
+  // that was submitted to PostTask() or PostDelayedTask().  As seen, by default
+  // it deletes the task, but the derived class can change this behaviour and
+  // recycle (re-use) it.  Be sure to call Reset() if you recycle it!
+  virtual void RecycleOrDelete() { delete this; }
+
+  // Call this method if you are trying to recycle a Task.  Note that only
+  // derived classes should attempt this feat, as a replacement for creating a
+  // new instance.
+  void Reset() {
+    posted_task_delay_ = -1;
+    priority_ = 0;
+    next_task_ = NULL;
+    nestable_ = true;
+  }
+
+ private:
+  friend class TimerManager;  // To check is_owned_by_message_loop().
+  friend class MessageLoop;   // To maintain posted_task_delay().
+  friend class WorkerPool;    // To release the task.
+
+  // Access methods used ONLY by friends in MessageLoop and TimerManager
+  int posted_task_delay() const { return posted_task_delay_; }
+  bool is_owned_by_message_loop() const { return 0 <= posted_task_delay_; }
+  void set_posted_task_delay(int delay) { posted_task_delay_ = delay; }
+
+  Task* next_task() const { return next_task_; }
+  void set_next_task(Task* next) { next_task_ = next; }
+
+  // Priority for execution by MessageLoop. 0 is default. Higher means run
+  // sooner, and lower (including negative) means run less soon.
+  int priority_;
+
+  // Slot to hold delay if the task was passed to PostTask().  If it was not
+  // passed to PostTask, then the delay is negative (the default).
+  int posted_task_delay_;
+
+  // When tasks are collected into a queue by MessageLoop, this member is used
+  // to form a null terminated list.
+  Task* next_task_;
+
+  // A nestable task will run in nested message loops, otherwise it will run
+  // only in the top level message loop.
+  bool nestable_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(MessageLoopOwnable);
+};
+
+
+// Task ------------------------------------------------------------------------
+//
+// A task is a generic runnable thingy, usually used for running code on a
+// different thread or for scheduling future tasks off of the message loop.
+
+class Task : public MessageLoopOwnable {
+ public:
+  Task() {}
+  virtual ~Task() {}
+
+  // Tasks are automatically deleted after Run is called.
+  virtual void Run() = 0;
+};
+
+class CancelableTask : public Task {
+ public:
+  // Not all tasks support cancellation.
+  virtual void Cancel() = 0;
+};
+
+// Scoped Factories ------------------------------------------------------------
+//
+// These scoped factory objects can be used by non-refcounted objects to safely
+// place tasks in a message loop.  Each factory guarantees that the tasks it
+// produces will not run after the factory is destroyed.  Commonly, factories
+// are declared as class members, so the class' tasks will automatically cancel
+// when the class instance is destroyed.
+//
+// Exampe Usage:
+//
+// class MyClass {
+//  private:
+//   // This factory will be used to schedule invocations of SomeMethod.
+//   ScopedRunnableMethodFactory<MyClass> some_method_factory_;
+//
+//  public:
+//   // It is safe to suppress warning 4355 here.
+//   MyClass() : some_method_factory_(this) { }
+//
+//   void SomeMethod() {
+//     // If this function might be called directly, you might want to revoke
+//     // any outstanding runnable methods scheduled to call it.  If it's not
+//     // referenced other than by the factory, this is unnecessary.
+//     some_method_factory_.RevokeAll();
+//     ...
+//   }
+//
+//   void ScheduleSomeMethod() {
+//     // If you'd like to only only have one pending task at a time, test for
+//     // |empty| before manufacturing another task.
+//     if (!some_method_factory_.empty())
+//       return;
+//
+//     // The factories are not thread safe, so always invoke on
+//     // |MessageLoop::current()|.
+//     MessageLoop::current()->PostTask(FROM_HERE,
+//         some_method_factory_.NewRunnableMethod(&MyClass::SomeMethod),
+//         kSomeMethodDelayMS);
+//   }
+// };
+
+// A ScopedTaskFactory produces tasks of type |TaskType| and prevents them from
+// running after it is destroyed.
+template<class TaskType>
+class ScopedTaskFactory : public RevocableStore {
+ public:
+  ScopedTaskFactory() { }
+
+  // Create a new task.
+  inline TaskType* NewTask() {
+    return new TaskWrapper(this);
+  }
+
+  class TaskWrapper : public TaskType, public NonThreadSafe {
+   public:
+    explicit TaskWrapper(RevocableStore* store) : revocable_(store) { }
+
+    virtual void Run() {
+      if (!revocable_.revoked())
+        TaskType::Run();
+    }
+
+   private:
+    Revocable revocable_;
+
+    DISALLOW_EVIL_CONSTRUCTORS(TaskWrapper);
+  };
+
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(ScopedTaskFactory);
+};
+
+// A ScopedRunnableMethodFactory creates runnable methods for a specified
+// object.  This is particularly useful for generating callbacks for
+// non-reference counted objects when the factory is a member of the object.
+template<class T>
+class ScopedRunnableMethodFactory : public RevocableStore {
+ public:
+  explicit ScopedRunnableMethodFactory(T* object) : object_(object) { }
+
+  template <class Method>
+  inline Task* NewRunnableMethod(Method method) {
+    typedef typename ScopedTaskFactory<RunnableMethod<
+        Method, Tuple0> >::TaskWrapper TaskWrapper;
+
+    TaskWrapper* task = new TaskWrapper(this);
+    task->Init(object_, method, MakeTuple());
+    return task;
+  }
+
+  template <class Method, class A>
+  inline Task* NewRunnableMethod(Method method, const A& a) {
+    typedef typename ScopedTaskFactory<RunnableMethod<
+        Method, Tuple1<A> > >::TaskWrapper TaskWrapper;
+
+    TaskWrapper* task = new TaskWrapper(this);
+    task->Init(object_, method, MakeTuple(a));
+    return task;
+  }
+
+  template <class Method, class A, class B>
+  inline Task* NewRunnableMethod(Method method, const A& a, const B& b) {
+    typedef typename ScopedTaskFactory<RunnableMethod<
+        Method, Tuple2<A, B> > >::TaskWrapper TaskWrapper;
+
+    TaskWrapper* task = new TaskWrapper(this);
+    task->Init(object_, method, MakeTuple(a, b));
+    return task;
+  }
+
+  template <class Method, class A, class B, class C>
+  inline Task* NewRunnableMethod(Method method,
+                                 const A& a,
+                                 const B& b,
+                                 const C& c) {
+    typedef typename ScopedTaskFactory<RunnableMethod<
+        Method, Tuple3<A, B, C> > >::TaskWrapper TaskWrapper;
+
+    TaskWrapper* task = new TaskWrapper(this);
+    task->Init(object_, method, MakeTuple(a, b, c));
+    return task;
+  }
+
+  template <class Method, class A, class B, class C, class D>
+  inline Task* NewRunnableMethod(Method method,
+                                 const A& a,
+                                 const B& b,
+                                 const C& c,
+                                 const D& d) {
+    typedef typename ScopedTaskFactory<RunnableMethod<
+        Method, Tuple4<A, B, C, D> > >::TaskWrapper TaskWrapper;
+
+    TaskWrapper* task = new TaskWrapper(this);
+    task->Init(object_, method, MakeTuple(a, b, c, d));
+    return task;
+  }
+
+  template <class Method, class A, class B, class C, class D, class E>
+  inline Task* NewRunnableMethod(Method method,
+                                 const A& a,
+                                 const B& b,
+                                 const C& c,
+                                 const D& d,
+                                 const E& e) {
+    typedef typename ScopedTaskFactory<RunnableMethod<
+        Method, Tuple5<A, B, C, D, E> > >::TaskWrapper TaskWrapper;
+
+    TaskWrapper* task = new TaskWrapper(this);
+    task->Init(object_, method, MakeTuple(a, b, c, d, e));
+    return task;
+  }
+
+ protected:
+  template <class Method, class Params>
+  class RunnableMethod : public Task {
+   public:
+    RunnableMethod() { }
+
+    void Init(T* obj, Method meth, const Params& params) {
+      obj_ = obj;
+      meth_ = meth;
+      params_ = params;
+    }
+
+    virtual void Run() { DispatchToMethod(obj_, meth_, params_); }
+
+   private:
+    T* obj_;
+    Method meth_;
+    Params params_;
+
+    DISALLOW_EVIL_CONSTRUCTORS(RunnableMethod);
+  };
+
+ private:
+  T* object_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(ScopedRunnableMethodFactory);
+};
+
+// General task implementations ------------------------------------------------
+
+// Task to delete an object
+template<class T>
+class DeleteTask : public CancelableTask {
+ public:
+  explicit DeleteTask(T* obj) : obj_(obj) {
+    set_nestable(false);
+  }
+  virtual void Run() {
+    delete obj_;
+  }
+  virtual void Cancel() {
+    obj_ = NULL;
+  }
+ private:
+  T* obj_;
+};
+
+// Task to Release() an object
+template<class T>
+class ReleaseTask : public CancelableTask {
+ public:
+  explicit ReleaseTask(T* obj) : obj_(obj) {
+    set_nestable(false);
+  }
+  virtual void Run() {
+    if (obj_)
+      obj_->Release();
+  }
+  virtual void Cancel() {
+    obj_ = NULL;
+  }
+ private:
+  T* obj_;
+};
+
+// RunnableMethodTraits --------------------------------------------------------
+//
+// This traits-class is used by RunnableMethod to manage the lifetime of the
+// callee object.  By default, it is assumed that the callee supports AddRef
+// and Release methods.  A particular class can specialize this template to
+// define other lifetime management.  For example, if the callee is known to
+// live longer than the RunnableMethod object, then a RunnableMethodTraits
+// struct could be defined with empty RetainCallee and ReleaseCallee methods.
+
+template <class T>
+struct RunnableMethodTraits {
+  static void RetainCallee(T* obj) {
+    obj->AddRef();
+  }
+  static void ReleaseCallee(T* obj) {
+    obj->Release();
+  }
+};
+
+// RunnableMethod and RunnableFunction -----------------------------------------
+//
+// Runnable methods are a type of task that call a function on an object when
+// they are run. We implement both an object and a set of NewRunnableMethod and
+// NewRunnableFunction functions for convenience. These functions are
+// overloaded and will infer the template types, simplifying calling code.
+//
+// The template definitions all use the following names:
+// T                - the class type of the object you're supplying
+//                    this is not needed for the Static version of the call
+// Method/Function  - the signature of a pointer to the method or function you
+//                    want to call
+// Param            - the parameter(s) to the method, possibly packed as a Tuple
+// A                - the first parameter (if any) to the method
+// B                - the second parameter (if any) to the mathod
+//
+// Put these all together and you get an object that can call a method whose
+// signature is:
+//   R T::MyFunction([A[, B]])
+//
+// Usage:
+// PostTask(FROM_HERE, NewRunnableMethod(object, &Object::method[, a[, b]])
+// PostTask(FROM_HERE, NewRunnableFunction(&function[, a[, b]])
+
+// RunnableMethod and NewRunnableMethod implementation -------------------------
+
+template <class T, class Method, class Params>
+class RunnableMethod : public CancelableTask,
+                       public RunnableMethodTraits<T> {
+ public:
+  RunnableMethod(T* obj, Method meth, const Params& params)
+      : obj_(obj), meth_(meth), params_(params) {
+    RetainCallee(obj_);
+  }
+  ~RunnableMethod() {
+    ReleaseCallee();
+  }
+
+  virtual void Run() {
+    if (obj_)
+      DispatchToMethod(obj_, meth_, params_);
+  }
+
+  virtual void Cancel() {
+    ReleaseCallee();
+  }
+
+ private:
+  void ReleaseCallee() {
+    if (obj_) {
+      RunnableMethodTraits<T>::ReleaseCallee(obj_);
+      obj_ = NULL;
+    }
+  }
+
+  T* obj_;
+  Method meth_;
+  Params params_;
+};
+
+template <class T, class Method>
+inline CancelableTask* NewRunnableMethod(T* object, Method method) {
+  return new RunnableMethod<T, Method, Tuple0>(object, method, MakeTuple());
+}
+
+template <class T, class Method, class A>
+inline CancelableTask* NewRunnableMethod(T* object, Method method, const A& a) {
+  return new RunnableMethod<T, Method, Tuple1<A> >(object, method, MakeTuple(a));
+}
+
+template <class T, class Method, class A, class B>
+inline CancelableTask* NewRunnableMethod(T* object, Method method,
+const A& a, const B& b) {
+  return new RunnableMethod<T, Method, Tuple2<A, B> >(object, method,
+                                                      MakeTuple(a, b));
+}
+
+template <class T, class Method, class A, class B, class C>
+inline CancelableTask* NewRunnableMethod(T* object, Method method,
+                                          const A& a, const B& b, const C& c) {
+  return new RunnableMethod<T, Method, Tuple3<A, B, C> >(object, method,
+                                                         MakeTuple(a, b, c));
+}
+
+template <class T, class Method, class A, class B, class C, class D>
+inline CancelableTask* NewRunnableMethod(T* object, Method method,
+                                          const A& a, const B& b,
+                                          const C& c, const D& d) {
+  return new RunnableMethod<T, Method, Tuple4<A, B, C, D> >(object, method,
+                                                            MakeTuple(a, b,
+                                                                      c, d));
+}
+
+template <class T, class Method, class A, class B, class C, class D, class E>
+inline CancelableTask* NewRunnableMethod(T* object, Method method,
+                                          const A& a, const B& b,
+                                          const C& c, const D& d, const E& e) {
+  return new RunnableMethod<T,
+                            Method,
+                            Tuple5<A, B, C, D, E> >(object,
+                                                    method,
+                                                    MakeTuple(a, b, c, d, e));
+}
+
+// RunnableFunction and NewRunnableFunction implementation ---------------------
+
+template <class Function, class Params>
+class RunnableFunction : public CancelableTask {
+ public:
+  RunnableFunction(Function function, const Params& params)
+      : function_(function), params_(params) {
+  }
+
+  ~RunnableFunction() {
+  }
+
+  virtual void Run() {
+    if (function_)
+      DispatchToFunction(function_, params_);
+  }
+
+  virtual void Cancel() {
+  }
+
+ private:
+  Function function_;
+  Params params_;
+};
+
+template <class Function>
+inline CancelableTask* NewRunnableFunction(Function function) {
+  return new RunnableFunction<Function, Tuple0>(function, MakeTuple());
+}
+
+template <class Function, class A>
+inline CancelableTask* NewRunnableFunction(Function function, const A& a) {
+  return new RunnableFunction<Function, Tuple1<A> >(function, MakeTuple(a));
+}
+
+template <class Function, class A, class B>
+inline CancelableTask* NewRunnableFunction(Function function,
+                                           const A& a, const B& b) {
+  return new RunnableFunction<Function, Tuple2<A, B> >(function, MakeTuple(a, b));
+}
+
+template <class Function, class A, class B, class C>
+inline CancelableTask* NewRunnableFunction(Function function,
+                                           const A& a, const B& b,
+                                           const C& c) {
+  return new RunnableFunction<Function, Tuple3<A, B, C> >(function,
+                                                          MakeTuple(a, b, c));
+}
+
+template <class Function, class A, class B, class C, class D>
+inline CancelableTask* NewRunnableFunction(Function function,
+                                           const A& a, const B& b,
+                                           const C& c, const D& d) {
+  return new RunnableFunction<Function, Tuple4<A, B, C, D> >(function,
+                                                             MakeTuple(a, b,
+                                                                       c, d));
+}
+
+template <class Function, class A, class B, class C, class D, class E>
+inline CancelableTask* NewRunnableFunction(Function function,
+                                           const A& a, const B& b,
+                                           const C& c, const D& d,
+                                           const E& e) {
+  return new RunnableFunction<Function, Tuple5<A, B, C, D, E> >(function,
+                                                                MakeTuple(a, b,
+                                                                          c, d,
+                                                                          e));
+}
+
+// Callback --------------------------------------------------------------------
+//
+// A Callback is like a Task but with unbound parameters. It is basically an
+// object-oriented function pointer.
+//
+// Callbacks are designed to work with Tuples.  A set of helper functions and
+// classes is provided to hide the Tuple details from the consumer.  Client
+// code will generally work with the CallbackRunner base class, which merely
+// provides a Run method and is returned by the New* functions. This allows
+// users to not care which type of class implements the callback, only that it
+// has a certain number and type of arguments.
+//
+// The implementation of this is done by CallbackImpl, which inherits
+// CallbackStorage to store the data. This allows the storage of the data
+// (requiring the class type T) to be hidden from users, who will want to call
+// this regardless of the implementor's type T.
+//
+// Note that callbacks currently have no facility for cancelling or abandoning
+// them. We currently handle this at a higher level for cases where this is
+// necessary. The pointer in a callback must remain valid until the callback
+// is made.
+//
+// Like Task, the callback executor is responsible for deleting the callback
+// pointer once the callback has executed.
+//
+// Example client usage:
+//   void Object::DoStuff(int, string);
+//   Callback2<int, string>::Type* callback =
+//       NewCallback(obj, &Object::DoStuff);
+//   callback->Run(5, string("hello"));
+//   delete callback;
+// or, equivalently, using tuples directly:
+//   CallbackRunner<Tuple2<int, string> >* callback =
+//       NewCallback(obj, &Object::DoStuff);
+//   callback->RunWithParams(MakeTuple(5, string("hello")));
+
+// Base for all Callbacks that handles storage of the pointers.
+template <class T, typename Method>
+class CallbackStorage {
+ public:
+  CallbackStorage(T* obj, Method meth) : obj_(obj), meth_(meth) {
+  }
+
+ protected:
+  T* obj_;
+  Method meth_;
+};
+
+// Interface that is exposed to the consumer, that does the actual calling
+// of the method.
+template <typename Params>
+class CallbackRunner {
+ public:
+  typedef Params TupleType;
+
+  virtual ~CallbackRunner() {}
+  virtual void RunWithParams(const Params& params) = 0;
+
+  // Convenience functions so callers don't have to deal with Tuples.
+  inline void Run() {
+    RunWithParams(Tuple0());
+  }
+
+  template <typename Arg1>
+  inline void Run(const Arg1& a) {
+    RunWithParams(Params(a));
+  }
+
+  template <typename Arg1, typename Arg2>
+  inline void Run(const Arg1& a, const Arg2& b) {
+    RunWithParams(Params(a, b));
+  }
+
+  template <typename Arg1, typename Arg2, typename Arg3>
+  inline void Run(const Arg1& a, const Arg2& b, const Arg3& c) {
+    RunWithParams(Params(a, b, c));
+  }
+
+  template <typename Arg1, typename Arg2, typename Arg3, typename Arg4>
+  inline void Run(const Arg1& a, const Arg2& b, const Arg3& c, const Arg4& d) {
+    RunWithParams(Params(a, b, c, d));
+  }
+
+  template <typename Arg1, typename Arg2, typename Arg3,
+            typename Arg4, typename Arg5>
+  inline void Run(const Arg1& a, const Arg2& b, const Arg3& c,
+                  const Arg4& d, const Arg5& e) {
+    RunWithParams(Params(a, b, c, d, e));
+  }
+};
+
+template <class T, typename Method, typename Params>
+class CallbackImpl : public CallbackStorage<T, Method>,
+                     public CallbackRunner<Params> {
+ public:
+  CallbackImpl(T* obj, Method meth) : CallbackStorage<T, Method>(obj, meth) {
+  }
+  virtual void RunWithParams(const Params& params) {
+    // use "this->" to force C++ to look inside our templatized base class; see
+    // Effective C++, 3rd Ed, item 43, p210 for details.
+    DispatchToMethod(this->obj_, this->meth_, params);
+  }
+};
+
+// 0-arg implementation
+struct Callback0 {
+  typedef CallbackRunner<Tuple0> Type;
+};
+
+template <class T>
+typename Callback0::Type* NewCallback(T* object, void (T::*method)()) {
+  return new CallbackImpl<T, void (T::*)(), Tuple0 >(object, method);
+}
+
+// 1-arg implementation
+template <typename Arg1>
+struct Callback1 {
+  typedef CallbackRunner<Tuple1<Arg1> > Type;
+};
+
+template <class T, typename Arg1>
+typename Callback1<Arg1>::Type* NewCallback(T* object, void (T::*method)(Arg1)) {
+  return new CallbackImpl<T, void (T::*)(Arg1), Tuple1<Arg1> >(object, method);
+}
+
+// 2-arg implementation
+template <typename Arg1, typename Arg2>
+struct Callback2 {
+  typedef CallbackRunner<Tuple2<Arg1, Arg2> > Type;
+};
+
+template <class T, typename Arg1, typename Arg2>
+typename Callback2<Arg1, Arg2>::Type* NewCallback(
+    T* object,
+    void (T::*method)(Arg1, Arg2)) {
+  return new CallbackImpl<T, void (T::*)(Arg1, Arg2),
+      Tuple2<Arg1, Arg2> >(object, method);
+}
+
+// 3-arg implementation
+template <typename Arg1, typename Arg2, typename Arg3>
+struct Callback3 {
+  typedef CallbackRunner<Tuple3<Arg1, Arg2, Arg3> > Type;
+};
+
+template <class T, typename Arg1, typename Arg2, typename Arg3>
+typename Callback3<Arg1, Arg2, Arg3>::Type* NewCallback(
+    T* object,
+    void (T::*method)(Arg1, Arg2, Arg3)) {
+  return new CallbackImpl<T,  void (T::*)(Arg1, Arg2, Arg3),
+      Tuple3<Arg1, Arg2, Arg3> >(object, method);
+}
+
+// 4-arg implementation
+template <typename Arg1, typename Arg2, typename Arg3, typename Arg4>
+struct Callback4 {
+  typedef CallbackRunner<Tuple4<Arg1, Arg2, Arg3, Arg4> > Type;
+};
+
+template <class T, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
+typename Callback4<Arg1, Arg2, Arg3, Arg4>::Type* NewCallback(
+    T* object,
+    void (T::*method)(Arg1, Arg2, Arg3, Arg4)) {
+  return new CallbackImpl<T, void (T::*)(Arg1, Arg2, Arg3, Arg4),
+      Tuple4<Arg1, Arg2, Arg3, Arg4> >(object, method);
+}
+
+// 5-arg implementation
+template <typename Arg1, typename Arg2, typename Arg3,
+          typename Arg4, typename Arg5>
+struct Callback5 {
+  typedef CallbackRunner<Tuple5<Arg1, Arg2, Arg3, Arg4, Arg5> > Type;
+};
+
+template <class T, typename Arg1, typename Arg2,
+          typename Arg3, typename Arg4, typename Arg5>
+typename Callback5<Arg1, Arg2, Arg3, Arg4, Arg5>::Type* NewCallback(
+    T* object,
+    void (T::*method)(Arg1, Arg2, Arg3, Arg4, Arg5)) {
+  return new CallbackImpl<T, void (T::*)(Arg1, Arg2, Arg3, Arg4, Arg5),
+      Tuple5<Arg1, Arg2, Arg3, Arg4, Arg5> >(object, method);
+}
+
+#endif  // BASE_TASK_H__
diff --git a/base/test_suite.h b/base/test_suite.h
new file mode 100644
index 0000000..a021339
--- /dev/null
+++ b/base/test_suite.h
@@ -0,0 +1,115 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_TEST_SUITE_H__
+#define BASE_TEST_SUITE_H__
+
+// Defines a basic test suite framework for running gtest based tests.  You can
+// instantiate this class in your main function and call its Run method to run
+// any gtest based tests that are linked into your executable.
+
+#include <windows.h>
+
+#include "base/command_line.h"
+#include "base/debug_on_start.h"
+#include "base/icu_util.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/multiprocess_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class TestSuite {
+ public:
+  TestSuite(int argc, char** argv) {
+    testing::InitGoogleTest(&argc, argv);
+  }
+
+  virtual ~TestSuite() {
+    // Flush any remaining messages.  This ensures that any accumulated Task
+    // objects get destroyed before we exit, which avoids noise in purify
+    // leak-test results.
+    message_loop_.Quit();
+    message_loop_.Run();
+  }
+
+  int Run() {
+    Initialize();
+
+    // Check to see if we are being run as a client process.
+    std::wstring client_func =
+        parsed_command_line_.GetSwitchValue(kRunClientProcess);
+    if (!client_func.empty()) {
+      // Convert our function name to a usable string for GetProcAddress.
+      std::string func_name(client_func.begin(), client_func.end());
+
+      // Get our module handle and search for an exported function
+      // which we can use as our client main.
+      MultiProcessTest::ChildFunctionPtr func =
+          reinterpret_cast<MultiProcessTest::ChildFunctionPtr>(
+              GetProcAddress(GetModuleHandle(NULL), func_name.c_str()));
+      if (func)
+        return func();
+      return -1;
+    }
+    return RUN_ALL_TESTS();
+  }
+
+ protected:
+  // All fatal log messages (e.g. DCHECK failures) imply unit test failures
+  static void UnitTestAssertHandler(const std::string& str) {
+    FAIL() << str;
+  }
+
+  // Disable crash dialogs so that it doesn't gum up the buildbot
+  virtual void SuppressErrorDialogs() {
+    UINT new_flags = SEM_FAILCRITICALERRORS |
+                     SEM_NOGPFAULTERRORBOX |
+                     SEM_NOOPENFILEERRORBOX;
+
+    // Preserve existing error mode, as discussed at http://t/dmea
+    UINT existing_flags = SetErrorMode(new_flags);
+    SetErrorMode(existing_flags | new_flags);
+  }
+
+  virtual void Initialize() {
+    // In some cases, we do not want to see standard error dialogs.
+    if (!IsDebuggerPresent() &&
+        !parsed_command_line_.HasSwitch(L"show-error-dialogs")) {
+      SuppressErrorDialogs();
+      logging::SetLogAssertHandler(UnitTestAssertHandler);
+    }
+
+    icu_util::Initialize();
+  }
+
+  CommandLine parsed_command_line_;
+  MessageLoop message_loop_;
+};
+
+#endif  // BASE_TEST_SUITE_H__
diff --git a/base/third_party/nspr/README.google b/base/third_party/nspr/README.google
new file mode 100644
index 0000000..7775d60
--- /dev/null
+++ b/base/third_party/nspr/README.google
@@ -0,0 +1,2 @@
+The original code is the Netscape Portable Runtime (NSPR), licensed under
+the MPL/GPL/LGPL tri-license (http://www.mozilla.org/MPL/).
diff --git a/base/third_party/nspr/prcpucfg.h b/base/third_party/nspr/prcpucfg.h
new file mode 100644
index 0000000..ad1aec4
--- /dev/null
+++ b/base/third_party/nspr/prcpucfg.h
@@ -0,0 +1,41 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_THIRD_PARTY_NSPR_PRCPUCFG_H__
+#define BASE_THIRD_PARTY_NSPR_PRCPUCFG_H__
+
+#if defined(WIN32)
+#include "base/third_party/nspr/prcpucfg_win.h"
+#elif defined(__APPLE__)
+#include "base/third_party/nspr/prcpucfg_mac.h"
+#else
+#error Provide a prcpucfg.h appropriate for your platform
+#endif
+
+#endif  // BASE_THIRD_PARTY_NSPR_PRCPUCFG_H__
diff --git a/base/third_party/nspr/prtime.cc b/base/third_party/nspr/prtime.cc
new file mode 100644
index 0000000..a5e851c
--- /dev/null
+++ b/base/third_party/nspr/prtime.cc
@@ -0,0 +1,838 @@
+/*
+* Portions are Copyright (C) 2007 Google Inc
+*
+* ***** BEGIN LICENSE BLOCK *****
+* Version: MPL 1.1/GPL 2.0/LGPL 2.1
+*
+* The contents of this file are subject to the Mozilla Public License Version
+* 1.1 (the "License"); you may not use this file except in compliance with
+* the License. You may obtain a copy of the License at
+* http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS IS" basis,
+* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+* for the specific language governing rights and limitations under the
+* License.
+*
+* The Original Code is the Netscape Portable Runtime (NSPR).
+*
+* The Initial Developer of the Original Code is
+* Netscape Communications Corporation.
+* Portions created by the Initial Developer are Copyright (C) 1998-2000
+* the Initial Developer. All Rights Reserved.
+*
+* Contributor(s):
+*
+* Alternatively, the contents of this file may be used under the terms of
+* either the GNU General Public License Version 2 or later (the "GPL"), or
+* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+* in which case the provisions of the GPL or the LGPL are applicable instead
+* of those above. If you wish to allow use of your version of this file only
+* under the terms of either the GPL or the LGPL, and not to allow others to
+* use your version of this file under the terms of the MPL, indicate your
+* decision by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL or the LGPL. If you do not delete
+* the provisions above, a recipient may use your version of this file under
+* the terms of any one of the MPL, the GPL or the LGPL.
+*
+* ***** END LICENSE BLOCK *****
+*
+*/
+
+/*
+ * prtime.cc --
+ * NOTE: The original nspr file name is prtime.c
+ *
+ *     NSPR date and time functions
+ *
+ * CVS revision 3.31
+ */
+
+/*
+* The following functions were copied from the NSPR prtime.c file.
+* PR_ParseTimeString
+* PR_ImplodeTime
+* This was modified to use the Win32 SYSTEMTIME/FILETIME structures
+* and the timezone offsets are applied to the FILETIME structure.
+* All types and defines have been defined in the base/third_party/prtime.h file
+* These have been copied from the following nspr files. We have only copied over
+* the types we need.
+* 1. prtime.h
+* 2. prtypes.h
+* 3. prlong.h
+*/
+
+#include <windows.h>
+#include <time.h>
+#include "base/third_party/nspr/prtime.h"
+
+/*
+ *------------------------------------------------------------------------
+ *
+ * PR_ImplodeTime --
+ *
+ *     Cf. time_t mktime(struct tm *tp)
+ *     Note that 1 year has < 2^25 seconds.  So an PRInt32 is large enough.
+ *
+ *------------------------------------------------------------------------
+ */
+PRTime
+PR_ImplodeTime(const PRExplodedTime *exploded)
+{
+   // Create the system struct representing our exploded time.
+    SYSTEMTIME st = {0};
+    FILETIME ft = {0};
+    ULARGE_INTEGER uli = {0};
+
+    st.wYear = exploded->tm_year;
+    st.wMonth = exploded->tm_month + 1;
+    st.wDayOfWeek = exploded->tm_wday;
+    st.wDay = exploded->tm_mday;
+    st.wHour = exploded->tm_hour;
+    st.wMinute = exploded->tm_min;
+    st.wSecond = exploded->tm_sec;
+    st.wMilliseconds = exploded->tm_usec/1000;
+     // Convert to FILETIME.
+    if (!SystemTimeToFileTime(&st, &ft)) {
+      NOTREACHED() << "Unable to convert time";
+      return 0;
+    }
+    // Apply offsets.
+    uli.LowPart = ft.dwLowDateTime;
+    uli.HighPart = ft.dwHighDateTime;
+    // From second to 100-ns
+    uli.QuadPart -=
+        (exploded->tm_params.tp_gmt_offset +
+         exploded->tm_params.tp_dst_offset) * 10000000i64;  // 7 zeros
+    // Convert to PRTime
+    uli.QuadPart -= 116444736000000000i64; // from Windows epoch to NSPR epoch
+    uli.QuadPart /= 10;  // from 100-nanosecond to microsecond
+    return (PRTime)uli.QuadPart;
+}
+
+/*
+ * The following code implements PR_ParseTimeString().  It is based on
+ * ns/lib/xp/xp_time.c, revision 1.25, by Jamie Zawinski <jwz@netscape.com>.
+ */
+
+/*
+ * We only recognize the abbreviations of a small subset of time zones
+ * in North America, Europe, and Japan.
+ *
+ * PST/PDT: Pacific Standard/Daylight Time
+ * MST/MDT: Mountain Standard/Daylight Time
+ * CST/CDT: Central Standard/Daylight Time
+ * EST/EDT: Eastern Standard/Daylight Time
+ * AST: Atlantic Standard Time
+ * NST: Newfoundland Standard Time
+ * GMT: Greenwich Mean Time
+ * BST: British Summer Time
+ * MET: Middle Europe Time
+ * EET: Eastern Europe Time
+ * JST: Japan Standard Time
+ */
+
+typedef enum
+{
+  TT_UNKNOWN,
+
+  TT_SUN, TT_MON, TT_TUE, TT_WED, TT_THU, TT_FRI, TT_SAT,
+
+  TT_JAN, TT_FEB, TT_MAR, TT_APR, TT_MAY, TT_JUN,
+  TT_JUL, TT_AUG, TT_SEP, TT_OCT, TT_NOV, TT_DEC,
+
+  TT_PST, TT_PDT, TT_MST, TT_MDT, TT_CST, TT_CDT, TT_EST, TT_EDT,
+  TT_AST, TT_NST, TT_GMT, TT_BST, TT_MET, TT_EET, TT_JST
+} TIME_TOKEN;
+
+/*
+ * This parses a time/date string into a PRTime
+ * (microseconds after "1-Jan-1970 00:00:00 GMT").
+ * It returns PR_SUCCESS on success, and PR_FAILURE
+ * if the time/date string can't be parsed.
+ *
+ * Many formats are handled, including:
+ *
+ *   14 Apr 89 03:20:12
+ *   14 Apr 89 03:20 GMT
+ *   Fri, 17 Mar 89 4:01:33
+ *   Fri, 17 Mar 89 4:01 GMT
+ *   Mon Jan 16 16:12 PDT 1989
+ *   Mon Jan 16 16:12 +0130 1989
+ *   6 May 1992 16:41-JST (Wednesday)
+ *   22-AUG-1993 10:59:12.82
+ *   22-AUG-1993 10:59pm
+ *   22-AUG-1993 12:59am
+ *   22-AUG-1993 12:59 PM
+ *   Friday, August 04, 1995 3:54 PM
+ *   06/21/95 04:24:34 PM
+ *   20/06/95 21:07
+ *   95-06-08 19:32:48 EDT
+ *
+ * If the input string doesn't contain a description of the timezone,
+ * we consult the `default_to_gmt' to decide whether the string should
+ * be interpreted relative to the local time zone (PR_FALSE) or GMT (PR_TRUE).
+ * The correct value for this argument depends on what standard specified
+ * the time string which you are parsing.
+ */
+
+PRStatus
+PR_ParseTimeString(
+        const char *string,
+        PRBool default_to_gmt,
+        PRTime *result)
+{
+  PRExplodedTime tm;
+  TIME_TOKEN dotw = TT_UNKNOWN;
+  TIME_TOKEN month = TT_UNKNOWN;
+  TIME_TOKEN zone = TT_UNKNOWN;
+  int zone_offset = -1;
+  int date = -1;
+  PRInt32 year = -1;
+  int hour = -1;
+  int min = -1;
+  int sec = -1;
+
+  const char *rest = string;
+
+#ifdef DEBUG
+  int iterations = 0;
+#endif
+
+  PR_ASSERT(string && result);
+  if (!string || !result) return PR_FAILURE;
+
+  while (*rest)
+        {
+
+#ifdef DEBUG
+          if (iterations++ > 1000)
+                {
+                  PR_ASSERT(0);
+                  return PR_FAILURE;
+                }
+#endif
+
+          switch (*rest)
+                {
+                case 'a': case 'A':
+                  if (month == TT_UNKNOWN &&
+                          (rest[1] == 'p' || rest[1] == 'P') &&
+                          (rest[2] == 'r' || rest[2] == 'R'))
+                        month = TT_APR;
+                  else if (zone == TT_UNKNOWN &&
+                                   (rest[1] == 's' || rest[1] == 's') &&
+                                   (rest[2] == 't' || rest[2] == 'T'))
+                        zone = TT_AST;
+                  else if (month == TT_UNKNOWN &&
+                                   (rest[1] == 'u' || rest[1] == 'U') &&
+                                   (rest[2] == 'g' || rest[2] == 'G'))
+                        month = TT_AUG;
+                  break;
+                case 'b': case 'B':
+                  if (zone == TT_UNKNOWN &&
+                          (rest[1] == 's' || rest[1] == 'S') &&
+                          (rest[2] == 't' || rest[2] == 'T'))
+                        zone = TT_BST;
+                  break;
+                case 'c': case 'C':
+                  if (zone == TT_UNKNOWN &&
+                          (rest[1] == 'd' || rest[1] == 'D') &&
+                          (rest[2] == 't' || rest[2] == 'T'))
+                        zone = TT_CDT;
+                  else if (zone == TT_UNKNOWN &&
+                                   (rest[1] == 's' || rest[1] == 'S') &&
+                                   (rest[2] == 't' || rest[2] == 'T'))
+                        zone = TT_CST;
+                  break;
+                case 'd': case 'D':
+                  if (month == TT_UNKNOWN &&
+                          (rest[1] == 'e' || rest[1] == 'E') &&
+                          (rest[2] == 'c' || rest[2] == 'C'))
+                        month = TT_DEC;
+                  break;
+                case 'e': case 'E':
+                  if (zone == TT_UNKNOWN &&
+                          (rest[1] == 'd' || rest[1] == 'D') &&
+                          (rest[2] == 't' || rest[2] == 'T'))
+                        zone = TT_EDT;
+                  else if (zone == TT_UNKNOWN &&
+                                   (rest[1] == 'e' || rest[1] == 'E') &&
+                                   (rest[2] == 't' || rest[2] == 'T'))
+                        zone = TT_EET;
+                  else if (zone == TT_UNKNOWN &&
+                                   (rest[1] == 's' || rest[1] == 'S') &&
+                                   (rest[2] == 't' || rest[2] == 'T'))
+                        zone = TT_EST;
+                  break;
+                case 'f': case 'F':
+                  if (month == TT_UNKNOWN &&
+                          (rest[1] == 'e' || rest[1] == 'E') &&
+                          (rest[2] == 'b' || rest[2] == 'B'))
+                        month = TT_FEB;
+                  else if (dotw == TT_UNKNOWN &&
+                                   (rest[1] == 'r' || rest[1] == 'R') &&
+                                   (rest[2] == 'i' || rest[2] == 'I'))
+                        dotw = TT_FRI;
+                  break;
+                case 'g': case 'G':
+                  if (zone == TT_UNKNOWN &&
+                          (rest[1] == 'm' || rest[1] == 'M') &&
+                          (rest[2] == 't' || rest[2] == 'T'))
+                        zone = TT_GMT;
+                  break;
+                case 'j': case 'J':
+                  if (month == TT_UNKNOWN &&
+                          (rest[1] == 'a' || rest[1] == 'A') &&
+                          (rest[2] == 'n' || rest[2] == 'N'))
+                        month = TT_JAN;
+                  else if (zone == TT_UNKNOWN &&
+                                   (rest[1] == 's' || rest[1] == 'S') &&
+                                   (rest[2] == 't' || rest[2] == 'T'))
+                        zone = TT_JST;
+                  else if (month == TT_UNKNOWN &&
+                                   (rest[1] == 'u' || rest[1] == 'U') &&
+                                   (rest[2] == 'l' || rest[2] == 'L'))
+                        month = TT_JUL;
+                  else if (month == TT_UNKNOWN &&
+                                   (rest[1] == 'u' || rest[1] == 'U') &&
+                                   (rest[2] == 'n' || rest[2] == 'N'))
+                        month = TT_JUN;
+                  break;
+                case 'm': case 'M':
+                  if (month == TT_UNKNOWN &&
+                          (rest[1] == 'a' || rest[1] == 'A') &&
+                          (rest[2] == 'r' || rest[2] == 'R'))
+                        month = TT_MAR;
+                  else if (month == TT_UNKNOWN &&
+                                   (rest[1] == 'a' || rest[1] == 'A') &&
+                                   (rest[2] == 'y' || rest[2] == 'Y'))
+                        month = TT_MAY;
+                  else if (zone == TT_UNKNOWN &&
+                                   (rest[1] == 'd' || rest[1] == 'D') &&
+                                   (rest[2] == 't' || rest[2] == 'T'))
+                        zone = TT_MDT;
+                  else if (zone == TT_UNKNOWN &&
+                                   (rest[1] == 'e' || rest[1] == 'E') &&
+                                   (rest[2] == 't' || rest[2] == 'T'))
+                        zone = TT_MET;
+                  else if (dotw == TT_UNKNOWN &&
+                                   (rest[1] == 'o' || rest[1] == 'O') &&
+                                   (rest[2] == 'n' || rest[2] == 'N'))
+                        dotw = TT_MON;
+                  else if (zone == TT_UNKNOWN &&
+                                   (rest[1] == 's' || rest[1] == 'S') &&
+                                   (rest[2] == 't' || rest[2] == 'T'))
+                        zone = TT_MST;
+                  break;
+                case 'n': case 'N':
+                  if (month == TT_UNKNOWN &&
+                          (rest[1] == 'o' || rest[1] == 'O') &&
+                          (rest[2] == 'v' || rest[2] == 'V'))
+                        month = TT_NOV;
+                  else if (zone == TT_UNKNOWN &&
+                                   (rest[1] == 's' || rest[1] == 'S') &&
+                                   (rest[2] == 't' || rest[2] == 'T'))
+                        zone = TT_NST;
+                  break;
+                case 'o': case 'O':
+                  if (month == TT_UNKNOWN &&
+                          (rest[1] == 'c' || rest[1] == 'C') &&
+                          (rest[2] == 't' || rest[2] == 'T'))
+                        month = TT_OCT;
+                  break;
+                case 'p': case 'P':
+                  if (zone == TT_UNKNOWN &&
+                          (rest[1] == 'd' || rest[1] == 'D') &&
+                          (rest[2] == 't' || rest[2] == 'T'))
+                        zone = TT_PDT;
+                  else if (zone == TT_UNKNOWN &&
+                                   (rest[1] == 's' || rest[1] == 'S') &&
+                                   (rest[2] == 't' || rest[2] == 'T'))
+                        zone = TT_PST;
+                  break;
+                case 's': case 'S':
+                  if (dotw == TT_UNKNOWN &&
+                          (rest[1] == 'a' || rest[1] == 'A') &&
+                          (rest[2] == 't' || rest[2] == 'T'))
+                        dotw = TT_SAT;
+                  else if (month == TT_UNKNOWN &&
+                                   (rest[1] == 'e' || rest[1] == 'E') &&
+                                   (rest[2] == 'p' || rest[2] == 'P'))
+                        month = TT_SEP;
+                  else if (dotw == TT_UNKNOWN &&
+                                   (rest[1] == 'u' || rest[1] == 'U') &&
+                                   (rest[2] == 'n' || rest[2] == 'N'))
+                        dotw = TT_SUN;
+                  break;
+                case 't': case 'T':
+                  if (dotw == TT_UNKNOWN &&
+                          (rest[1] == 'h' || rest[1] == 'H') &&
+                          (rest[2] == 'u' || rest[2] == 'U'))
+                        dotw = TT_THU;
+                  else if (dotw == TT_UNKNOWN &&
+                                   (rest[1] == 'u' || rest[1] == 'U') &&
+                                   (rest[2] == 'e' || rest[2] == 'E'))
+                        dotw = TT_TUE;
+                  break;
+                case 'u': case 'U':
+                  if (zone == TT_UNKNOWN &&
+                          (rest[1] == 't' || rest[1] == 'T') &&
+                          !(rest[2] >= 'A' && rest[2] <= 'Z') &&
+                          !(rest[2] >= 'a' && rest[2] <= 'z'))
+                        /* UT is the same as GMT but UTx is not. */
+                        zone = TT_GMT;
+                  break;
+                case 'w': case 'W':
+                  if (dotw == TT_UNKNOWN &&
+                          (rest[1] == 'e' || rest[1] == 'E') &&
+                          (rest[2] == 'd' || rest[2] == 'D'))
+                        dotw = TT_WED;
+                  break;
+
+                case '+': case '-':
+                  {
+                        const char *end;
+                        int sign;
+                        if (zone_offset != -1)
+                          {
+                                /* already got one... */
+                                rest++;
+                                break;
+                          }
+                        if (zone != TT_UNKNOWN && zone != TT_GMT)
+                          {
+                                /* GMT+0300 is legal, but PST+0300 is not. */
+                                rest++;
+                                break;
+                          }
+
+                        sign = ((*rest == '+') ? 1 : -1);
+                        rest++; /* move over sign */
+                        end = rest;
+                        while (*end >= '0' && *end <= '9')
+                          end++;
+                        if (rest == end) /* no digits here */
+                          break;
+
+                        if ((end - rest) == 4)
+                          /* offset in HHMM */
+                          zone_offset = (((((rest[0]-'0')*10) + (rest[1]-'0')) * 60) +
+                                                         (((rest[2]-'0')*10) + (rest[3]-'0')));
+                        else if ((end - rest) == 2)
+                          /* offset in hours */
+                          zone_offset = (((rest[0]-'0')*10) + (rest[1]-'0')) * 60;
+                        else if ((end - rest) == 1)
+                          /* offset in hours */
+                          zone_offset = (rest[0]-'0') * 60;
+                        else
+                          /* 3 or >4 */
+                          break;
+
+                        zone_offset *= sign;
+                        zone = TT_GMT;
+                        break;
+                  }
+
+                case '0': case '1': case '2': case '3': case '4':
+                case '5': case '6': case '7': case '8': case '9':
+                  {
+                        int tmp_hour = -1;
+                        int tmp_min = -1;
+                        int tmp_sec = -1;
+                        const char *end = rest + 1;
+                        while (*end >= '0' && *end <= '9')
+                          end++;
+
+                        /* end is now the first character after a range of digits. */
+
+                        if (*end == ':')
+                          {
+                                if (hour >= 0 && min >= 0) /* already got it */
+                                  break;
+
+                                /* We have seen "[0-9]+:", so this is probably HH:MM[:SS] */
+                                if ((end - rest) > 2)
+                                  /* it is [0-9][0-9][0-9]+: */
+                                  break;
+                                else if ((end - rest) == 2)
+                                  tmp_hour = ((rest[0]-'0')*10 +
+                                                          (rest[1]-'0'));
+                                else
+                                  tmp_hour = (rest[0]-'0');
+
+                                /* move over the colon, and parse minutes */
+
+                                rest = ++end;
+                                while (*end >= '0' && *end <= '9')
+                                  end++;
+
+                                if (end == rest)
+                                  /* no digits after first colon? */
+                                  break;
+                                else if ((end - rest) > 2)
+                                  /* it is [0-9][0-9][0-9]+: */
+                                  break;
+                                else if ((end - rest) == 2)
+                                  tmp_min = ((rest[0]-'0')*10 +
+                                                         (rest[1]-'0'));
+                                else
+                                  tmp_min = (rest[0]-'0');
+
+                                /* now go for seconds */
+                                rest = end;
+                                if (*rest == ':')
+                                  rest++;
+                                end = rest;
+                                while (*end >= '0' && *end <= '9')
+                                  end++;
+
+                                if (end == rest)
+                                  /* no digits after second colon - that's ok. */
+                                  ;
+                                else if ((end - rest) > 2)
+                                  /* it is [0-9][0-9][0-9]+: */
+                                  break;
+                                else if ((end - rest) == 2)
+                                  tmp_sec = ((rest[0]-'0')*10 +
+                                                         (rest[1]-'0'));
+                                else
+                                  tmp_sec = (rest[0]-'0');
+
+                                /* If we made it here, we've parsed hour and min,
+                                   and possibly sec, so it worked as a unit. */
+
+                                /* skip over whitespace and see if there's an AM or PM
+                                   directly following the time.
+                                 */
+                                if (tmp_hour <= 12)
+                                  {
+                                        const char *s = end;
+                                        while (*s && (*s == ' ' || *s == '\t'))
+                                          s++;
+                                        if ((s[0] == 'p' || s[0] == 'P') &&
+                                                (s[1] == 'm' || s[1] == 'M'))
+                                          /* 10:05pm == 22:05, and 12:05pm == 12:05 */
+                                          tmp_hour = (tmp_hour == 12 ? 12 : tmp_hour + 12);
+                                        else if (tmp_hour == 12 &&
+                                                         (s[0] == 'a' || s[0] == 'A') &&
+                                                         (s[1] == 'm' || s[1] == 'M'))
+                                          /* 12:05am == 00:05 */
+                                          tmp_hour = 0;
+                                  }
+
+                                hour = tmp_hour;
+                                min = tmp_min;
+                                sec = tmp_sec;
+                                rest = end;
+                                break;
+                          }
+                        else if ((*end == '/' || *end == '-') &&
+                                         end[1] >= '0' && end[1] <= '9')
+                          {
+                                /* Perhaps this is 6/16/95, 16/6/95, 6-16-95, or 16-6-95
+                                   or even 95-06-05...
+                                   #### But it doesn't handle 1995-06-22.
+                                 */
+                                int n1, n2, n3;
+                                const char *s;
+
+                                if (month != TT_UNKNOWN)
+                                  /* if we saw a month name, this can't be. */
+                                  break;
+
+                                s = rest;
+
+                                n1 = (*s++ - '0');                                /* first 1 or 2 digits */
+                                if (*s >= '0' && *s <= '9')
+                                  n1 = n1*10 + (*s++ - '0');
+
+                                if (*s != '/' && *s != '-')                /* slash */
+                                  break;
+                                s++;
+
+                                if (*s < '0' || *s > '9')                /* second 1 or 2 digits */
+                                  break;
+                                n2 = (*s++ - '0');
+                                if (*s >= '0' && *s <= '9')
+                                  n2 = n2*10 + (*s++ - '0');
+
+                                if (*s != '/' && *s != '-')                /* slash */
+                                  break;
+                                s++;
+
+                                if (*s < '0' || *s > '9')                /* third 1, 2, or 4 digits */
+                                  break;
+                                n3 = (*s++ - '0');
+                                if (*s >= '0' && *s <= '9')
+                                  n3 = n3*10 + (*s++ - '0');
+
+                                if (*s >= '0' && *s <= '9')                /* optional digits 3 and 4 */
+                                  {
+                                        n3 = n3*10 + (*s++ - '0');
+                                        if (*s < '0' || *s > '9')
+                                          break;
+                                        n3 = n3*10 + (*s++ - '0');
+                                  }
+
+                                if ((*s >= '0' && *s <= '9') ||        /* followed by non-alphanum */
+                                        (*s >= 'A' && *s <= 'Z') ||
+                                        (*s >= 'a' && *s <= 'z'))
+                                  break;
+
+                                /* Ok, we parsed three 1-2 digit numbers, with / or -
+                                   between them.  Now decide what the hell they are
+                                   (DD/MM/YY or MM/DD/YY or YY/MM/DD.)
+                                 */
+
+                                if (n1 > 31 || n1 == 0)  /* must be YY/MM/DD */
+                                  {
+                                        if (n2 > 12) break;
+                                        if (n3 > 31) break;
+                                        year = n1;
+                                        if (year < 70)
+                                            year += 2000;
+                                        else if (year < 100)
+                                            year += 1900;
+                                        month = (TIME_TOKEN)(n2 + ((int)TT_JAN) - 1);
+                                        date = n3;
+                                        rest = s;
+                                        break;
+                                  }
+
+                                if (n1 > 12 && n2 > 12)  /* illegal */
+                                  {
+                                        rest = s;
+                                        break;
+                                  }
+
+                                if (n3 < 70)
+                                    n3 += 2000;
+                                else if (n3 < 100)
+                                    n3 += 1900;
+
+                                if (n1 > 12)  /* must be DD/MM/YY */
+                                  {
+                                        date = n1;
+                                        month = (TIME_TOKEN)(n2 + ((int)TT_JAN) - 1);
+                                        year = n3;
+                                  }
+                                else                  /* assume MM/DD/YY */
+                                  {
+                                        /* #### In the ambiguous case, should we consult the
+                                           locale to find out the local default? */
+                                        month = (TIME_TOKEN)(n1 + ((int)TT_JAN) - 1);
+                                        date = n2;
+                                        year = n3;
+                                  }
+                                rest = s;
+                          }
+                        else if ((*end >= 'A' && *end <= 'Z') ||
+                                         (*end >= 'a' && *end <= 'z'))
+                          /* Digits followed by non-punctuation - what's that? */
+                          ;
+                        else if ((end - rest) == 4)                /* four digits is a year */
+                          year = (year < 0
+                                          ? ((rest[0]-'0')*1000L +
+                                                 (rest[1]-'0')*100L +
+                                                 (rest[2]-'0')*10L +
+                                                 (rest[3]-'0'))
+                                          : year);
+                        else if ((end - rest) == 2)                /* two digits - date or year */
+                          {
+                                int n = ((rest[0]-'0')*10 +
+                                                 (rest[1]-'0'));
+                                /* If we don't have a date (day of the month) and we see a number
+                                     less than 32, then assume that is the date.
+
+                                         Otherwise, if we have a date and not a year, assume this is the
+                                         year.  If it is less than 70, then assume it refers to the 21st
+                                         century.  If it is two digits (>= 70), assume it refers to this
+                                         century.  Otherwise, assume it refers to an unambiguous year.
+
+                                         The world will surely end soon.
+                                   */
+                                if (date < 0 && n < 32)
+                                  date = n;
+                                else if (year < 0)
+                                  {
+                                        if (n < 70)
+                                          year = 2000 + n;
+                                        else if (n < 100)
+                                          year = 1900 + n;
+                                        else
+                                          year = n;
+                                  }
+                                /* else what the hell is this. */
+                          }
+                        else if ((end - rest) == 1)                /* one digit - date */
+                          date = (date < 0 ? (rest[0]-'0') : date);
+                        /* else, three or more than four digits - what's that? */
+
+                        break;
+                  }
+                }
+
+          /* Skip to the end of this token, whether we parsed it or not.
+                 Tokens are delimited by whitespace, or ,;-/
+                 But explicitly not :+-.
+           */
+          while (*rest &&
+                         *rest != ' ' && *rest != '\t' &&
+                         *rest != ',' && *rest != ';' &&
+                         *rest != '-' && *rest != '+' &&
+                         *rest != '/' &&
+                         *rest != '(' && *rest != ')' && *rest != '[' && *rest != ']')
+                rest++;
+          /* skip over uninteresting chars. */
+        SKIP_MORE:
+          while (*rest &&
+                         (*rest == ' ' || *rest == '\t' ||
+                          *rest == ',' || *rest == ';' || *rest == '/' ||
+                          *rest == '(' || *rest == ')' || *rest == '[' || *rest == ']'))
+                rest++;
+
+          /* "-" is ignored at the beginning of a token if we have not yet
+                 parsed a year (e.g., the second "-" in "30-AUG-1966"), or if
+                 the character after the dash is not a digit. */
+          if (*rest == '-' && ((rest > string && isalpha(rest[-1]) && year < 0)
+              || rest[1] < '0' || rest[1] > '9'))
+                {
+                  rest++;
+                  goto SKIP_MORE;
+                }
+
+        }
+
+  if (zone != TT_UNKNOWN && zone_offset == -1)
+        {
+          switch (zone)
+                {
+                case TT_PST: zone_offset = -8 * 60; break;
+                case TT_PDT: zone_offset = -7 * 60; break;
+                case TT_MST: zone_offset = -7 * 60; break;
+                case TT_MDT: zone_offset = -6 * 60; break;
+                case TT_CST: zone_offset = -6 * 60; break;
+                case TT_CDT: zone_offset = -5 * 60; break;
+                case TT_EST: zone_offset = -5 * 60; break;
+                case TT_EDT: zone_offset = -4 * 60; break;
+                case TT_AST: zone_offset = -4 * 60; break;
+                case TT_NST: zone_offset = -3 * 60 - 30; break;
+                case TT_GMT: zone_offset =  0 * 60; break;
+                case TT_BST: zone_offset =  1 * 60; break;
+                case TT_MET: zone_offset =  1 * 60; break;
+                case TT_EET: zone_offset =  2 * 60; break;
+                case TT_JST: zone_offset =  9 * 60; break;
+                default:
+                  PR_ASSERT (0);
+                  break;
+                }
+        }
+
+  /* If we didn't find a year, month, or day-of-the-month, we can't
+         possibly parse this, and in fact, mktime() will do something random
+         (I'm seeing it return "Tue Feb  5 06:28:16 2036", which is no doubt
+         a numerologically significant date... */
+  if (month == TT_UNKNOWN || date == -1 || year == -1)
+      return PR_FAILURE;
+
+  memset(&tm, 0, sizeof(tm));
+  if (sec != -1)
+        tm.tm_sec = sec;
+  if (min != -1)
+  tm.tm_min = min;
+  if (hour != -1)
+        tm.tm_hour = hour;
+  if (date != -1)
+        tm.tm_mday = date;
+  if (month != TT_UNKNOWN)
+        tm.tm_month = (((int)month) - ((int)TT_JAN));
+  if (year != -1)
+        tm.tm_year = year;
+  if (dotw != TT_UNKNOWN)
+        tm.tm_wday = (((int)dotw) - ((int)TT_SUN));
+
+  if (zone == TT_UNKNOWN && default_to_gmt)
+        {
+          /* No zone was specified, so pretend the zone was GMT. */
+          zone = TT_GMT;
+          zone_offset = 0;
+        }
+
+  if (zone_offset == -1)
+         {
+           /* no zone was specified, and we're to assume that everything
+             is local. */
+          struct tm localTime;
+          time_t secs;
+
+          PR_ASSERT(tm.tm_month > -1
+                                   && tm.tm_mday > 0
+                                   && tm.tm_hour > -1
+                                   && tm.tm_min > -1
+                                   && tm.tm_sec > -1);
+
+            /*
+             * To obtain time_t from a tm structure representing the local
+             * time, we call mktime().  However, we need to see if we are
+             * on 1-Jan-1970 or before.  If we are, we can't call mktime()
+             * because mktime() will crash on win16. In that case, we
+             * calculate zone_offset based on the zone offset at
+             * 00:00:00, 2 Jan 1970 GMT, and subtract zone_offset from the
+             * date we are parsing to transform the date to GMT.  We also
+             * do so if mktime() returns (time_t) -1 (time out of range).
+           */
+
+          /* month, day, hours, mins and secs are always non-negative
+             so we dont need to worry about them. */
+            if(tm.tm_year >= 1970)
+                {
+                  PRInt64 usec_per_sec;
+
+                  localTime.tm_sec = tm.tm_sec;
+                  localTime.tm_min = tm.tm_min;
+                  localTime.tm_hour = tm.tm_hour;
+                  localTime.tm_mday = tm.tm_mday;
+                  localTime.tm_mon = tm.tm_month;
+                  localTime.tm_year = tm.tm_year - 1900;
+                  /* Set this to -1 to tell mktime "I don't care".  If you set
+                     it to 0 or 1, you are making assertions about whether the
+                     date you are handing it is in daylight savings mode or not;
+                     and if you're wrong, it will "fix" it for you. */
+                  localTime.tm_isdst = -1;
+                  secs = mktime(&localTime);
+                  if (secs != (time_t) -1)
+                    {
+#if defined(XP_MAC) && (__MSL__ < 0x6000)
+                      /*
+                       * The mktime() routine in MetroWerks MSL C
+                       * Runtime library returns seconds since midnight,
+                       * 1 Jan. 1900, not 1970 - in versions of MSL (Metrowerks Standard
+                       * Library) prior to version 6.  Only for older versions of
+                       * MSL do we adjust the value of secs to the NSPR epoch
+                       */
+                      secs -= ((365 * 70UL) + 17) * 24 * 60 * 60;
+#endif
+                      LL_I2L(*result, secs);
+                      LL_I2L(usec_per_sec, PR_USEC_PER_SEC);
+                      LL_MUL(*result, *result, usec_per_sec);
+                      return PR_SUCCESS;
+                    }
+                }
+
+                /* So mktime() can't handle this case.  We assume the
+                   zone_offset for the date we are parsing is the same as
+                   the zone offset on 00:00:00 2 Jan 1970 GMT. */
+                secs = 86400;
+                (void) localtime_s(&localTime, &secs);
+                zone_offset = localTime.tm_min
+                              + 60 * localTime.tm_hour
+                              + 1440 * (localTime.tm_mday - 2);
+        }
+
+        tm.tm_params.tp_gmt_offset = zone_offset * 60;
+
+  *result = PR_ImplodeTime(&tm);
+
+  return PR_SUCCESS;
+}
\ No newline at end of file
diff --git a/base/third_party/nspr/prtime.h b/base/third_party/nspr/prtime.h
new file mode 100644
index 0000000..70d25bc
--- /dev/null
+++ b/base/third_party/nspr/prtime.h
@@ -0,0 +1,185 @@
+/*
+* Portions are Copyright (C) 2007 Google Inc
+*
+* ***** BEGIN LICENSE BLOCK *****
+* Version: MPL 1.1/GPL 2.0/LGPL 2.1
+*
+* The contents of this file are subject to the Mozilla Public License Version
+* 1.1 (the "License"); you may not use this file except in compliance with
+* the License. You may obtain a copy of the License at
+* http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS IS" basis,
+* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+* for the specific language governing rights and limitations under the
+* License.
+*
+* The Original Code is the Netscape Portable Runtime (NSPR).
+*
+* The Initial Developer of the Original Code is
+* Netscape Communications Corporation.
+* Portions created by the Initial Developer are Copyright (C) 1998-2000
+* the Initial Developer. All Rights Reserved.
+*
+* Contributor(s):
+*
+* Alternatively, the contents of this file may be used under the terms of
+* either the GNU General Public License Version 2 or later (the "GPL"), or
+* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+* in which case the provisions of the GPL or the LGPL are applicable instead
+* of those above. If you wish to allow use of your version of this file only
+* under the terms of either the GPL or the LGPL, and not to allow others to
+* use your version of this file under the terms of the MPL, indicate your
+* decision by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL or the LGPL. If you do not delete
+* the provisions above, a recipient may use your version of this file under
+* the terms of any one of the MPL, the GPL or the LGPL.
+*
+* ***** END LICENSE BLOCK *****
+*/
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * prtime.h --
+ *
+ *     NSPR date and time functions
+ * CVS revision 3.11
+ * This file contains definitions of NSPR's basic types required by
+ * prtime.cc. These types have been copied over from the following NSPR
+ * files prtime.h, prtypes.h(CVS revision 3.35), prlong.h(CVS revision 3.13)
+ *
+ *---------------------------------------------------------------------------
+ */
+
+#ifndef BASE_PRTIME_H__
+#define BASE_PRTIME_H__
+
+#include "base/logging.h"
+#include "base/third_party/nspr/prtypes.h"
+
+#define PR_ASSERT DCHECK
+
+#define LL_I2L(l, i)    ((l) = (PRInt64)(i))
+#define LL_MUL(r, a, b) ((r) = (a) * (b))
+
+/**********************************************************************/
+/************************* TYPES AND CONSTANTS ************************/
+/**********************************************************************/
+
+#define PR_MSEC_PER_SEC		1000UL
+#define PR_USEC_PER_SEC		1000000UL
+#define PR_NSEC_PER_SEC		1000000000UL
+#define PR_USEC_PER_MSEC	1000UL
+#define PR_NSEC_PER_MSEC	1000000UL
+
+/*
+ * PRTime --
+ *
+ *     NSPR represents basic time as 64-bit signed integers relative
+ *     to midnight (00:00:00), January 1, 1970 Greenwich Mean Time (GMT).
+ *     (GMT is also known as Coordinated Universal Time, UTC.)
+ *     The units of time are in microseconds. Negative times are allowed
+ *     to represent times prior to the January 1970 epoch. Such values are
+ *     intended to be exported to other systems or converted to human
+ *     readable form.
+ *
+ *     Notes on porting: PRTime corresponds to time_t in ANSI C.  NSPR 1.0
+ *     simply uses PRInt64.
+ */
+
+typedef PRInt64 PRTime;
+
+/*
+ * Time zone and daylight saving time corrections applied to GMT to
+ * obtain the local time of some geographic location
+ */
+
+typedef struct PRTimeParameters {
+    PRInt32 tp_gmt_offset;     /* the offset from GMT in seconds */
+    PRInt32 tp_dst_offset;     /* contribution of DST in seconds */
+} PRTimeParameters;
+
+/*
+ * PRExplodedTime --
+ *
+ *     Time broken down into human-readable components such as year, month,
+ *     day, hour, minute, second, and microsecond.  Time zone and daylight
+ *     saving time corrections may be applied.  If they are applied, the
+ *     offsets from the GMT must be saved in the 'tm_params' field so that
+ *     all the information is available to reconstruct GMT.
+ *
+ *     Notes on porting: PRExplodedTime corrresponds to struct tm in
+ *     ANSI C, with the following differences:
+ *       - an additional field tm_usec;
+ *       - replacing tm_isdst by tm_params;
+ *       - the month field is spelled tm_month, not tm_mon;
+ *       - we use absolute year, AD, not the year since 1900.
+ *     The corresponding type in NSPR 1.0 is called PRTime.  Below is
+ *     a table of date/time type correspondence in the three APIs:
+ *         API          time since epoch          time in components
+ *       ANSI C             time_t                  struct tm
+ *       NSPR 1.0           PRInt64                   PRTime
+ *       NSPR 2.0           PRTime                  PRExplodedTime
+ */
+
+typedef struct PRExplodedTime {
+    PRInt32 tm_usec;		    /* microseconds past tm_sec (0-99999)  */
+    PRInt32 tm_sec;             /* seconds past tm_min (0-61, accomodating
+                                   up to two leap seconds) */	
+    PRInt32 tm_min;             /* minutes past tm_hour (0-59) */
+    PRInt32 tm_hour;            /* hours past tm_day (0-23) */
+    PRInt32 tm_mday;            /* days past tm_mon (1-31, note that it
+				                starts from 1) */
+    PRInt32 tm_month;           /* months past tm_year (0-11, Jan = 0) */
+    PRInt16 tm_year;            /* absolute year, AD (note that we do not
+				                count from 1900) */
+
+    PRInt8 tm_wday;		        /* calculated day of the week
+				                (0-6, Sun = 0) */
+    PRInt16 tm_yday;            /* calculated day of the year
+				                (0-365, Jan 1 = 0) */
+
+    PRTimeParameters tm_params;  /* time parameters used by conversion */
+} PRExplodedTime;
+
+NSPR_API(PRTime)
+PR_ImplodeTime(const PRExplodedTime *exploded);
+
+/*
+ * This parses a time/date string into a PRTime
+ * (microseconds after "1-Jan-1970 00:00:00 GMT").
+ * It returns PR_SUCCESS on success, and PR_FAILURE
+ * if the time/date string can't be parsed.
+ *
+ * Many formats are handled, including:
+ *
+ *   14 Apr 89 03:20:12
+ *   14 Apr 89 03:20 GMT
+ *   Fri, 17 Mar 89 4:01:33
+ *   Fri, 17 Mar 89 4:01 GMT
+ *   Mon Jan 16 16:12 PDT 1989
+ *   Mon Jan 16 16:12 +0130 1989
+ *   6 May 1992 16:41-JST (Wednesday)
+ *   22-AUG-1993 10:59:12.82
+ *   22-AUG-1993 10:59pm
+ *   22-AUG-1993 12:59am
+ *   22-AUG-1993 12:59 PM
+ *   Friday, August 04, 1995 3:54 PM
+ *   06/21/95 04:24:34 PM
+ *   20/06/95 21:07
+ *   95-06-08 19:32:48 EDT
+ *
+ * If the input string doesn't contain a description of the timezone,
+ * we consult the `default_to_gmt' to decide whether the string should
+ * be interpreted relative to the local time zone (PR_FALSE) or GMT (PR_TRUE).
+ * The correct value for this argument depends on what standard specified
+ * the time string which you are parsing.
+ */
+
+NSPR_API(PRStatus) PR_ParseTimeString (
+	const char *string,
+	PRBool default_to_gmt,
+	PRTime *result);
+
+#endif  // BASE_PRTIME_H__
diff --git a/base/third_party/nspr/prtypes.h b/base/third_party/nspr/prtypes.h
new file mode 100644
index 0000000..e9e41c2
--- /dev/null
+++ b/base/third_party/nspr/prtypes.h
@@ -0,0 +1,567 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape Portable Runtime (NSPR).
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+** File:                prtypes.h
+** Description: Definitions of NSPR's basic types
+**
+** Prototypes and macros used to make up for deficiencies that we have found
+** in ANSI environments.
+**
+** Since we do not wrap <stdlib.h> and all the other standard headers, authors
+** of portable code will not know in general that they need these definitions.
+** Instead of requiring these authors to find the dependent uses in their code
+** and take the following steps only in those C files, we take steps once here
+** for all C files.
+**/
+
+#ifndef prtypes_h___
+#define prtypes_h___
+
+#ifdef MDCPUCFG
+#include MDCPUCFG
+#else
+#include "base/third_party/nspr/prcpucfg.h"
+#endif
+
+#include <stddef.h>
+
+/***********************************************************************
+** MACROS:      PR_EXTERN
+**              PR_IMPLEMENT
+** DESCRIPTION:
+**      These are only for externally visible routines and globals.  For
+**      internal routines, just use "extern" for type checking and that
+**      will not export internal cross-file or forward-declared symbols.
+**      Define a macro for declaring procedures return types. We use this to
+**      deal with windoze specific type hackery for DLL definitions. Use
+**      PR_EXTERN when the prototype for the method is declared. Use
+**      PR_IMPLEMENT for the implementation of the method.
+**
+** Example:
+**   in dowhim.h
+**     PR_EXTERN( void ) DoWhatIMean( void );
+**   in dowhim.c
+**     PR_IMPLEMENT( void ) DoWhatIMean( void ) { return; }
+**
+**
+***********************************************************************/
+#if defined(WIN32)
+
+#define PR_EXPORT(__type) extern __declspec(dllexport) __type
+#define PR_EXPORT_DATA(__type) extern __declspec(dllexport) __type
+#define PR_IMPORT(__type) extern __type
+#define PR_IMPORT_DATA(__type) __declspec(dllimport) __type
+
+#define PR_EXTERN(__type) extern __declspec(dllexport) __type
+#define PR_IMPLEMENT(__type) __type
+#define PR_EXTERN_DATA(__type) extern __declspec(dllexport) __type
+#define PR_IMPLEMENT_DATA(__type) __declspec(dllexport) __type
+
+#define PR_CALLBACK
+#define PR_CALLBACK_DECL
+#define PR_STATIC_CALLBACK(__x) static __x
+
+#elif defined(XP_BEOS)
+
+#define PR_EXPORT(__type) extern __declspec(dllexport) __type
+#define PR_EXPORT_DATA(__type) extern __declspec(dllexport) __type
+#define PR_IMPORT(__type) extern __declspec(dllexport) __type
+#define PR_IMPORT_DATA(__type) extern __declspec(dllexport) __type
+
+#define PR_EXTERN(__type) extern __declspec(dllexport) __type
+#define PR_IMPLEMENT(__type) __declspec(dllexport) __type
+#define PR_EXTERN_DATA(__type) extern __declspec(dllexport) __type
+#define PR_IMPLEMENT_DATA(__type) __declspec(dllexport) __type
+
+#define PR_CALLBACK
+#define PR_CALLBACK_DECL
+#define PR_STATIC_CALLBACK(__x) static __x
+
+#elif defined(WIN16)
+
+#define PR_CALLBACK_DECL        __cdecl
+
+#if defined(_WINDLL)
+#define PR_EXPORT(__type) extern __type _cdecl _export _loadds
+#define PR_IMPORT(__type) extern __type _cdecl _export _loadds
+#define PR_EXPORT_DATA(__type) extern __type _export
+#define PR_IMPORT_DATA(__type) extern __type _export
+
+#define PR_EXTERN(__type) extern __type _cdecl _export _loadds
+#define PR_IMPLEMENT(__type) __type _cdecl _export _loadds
+#define PR_EXTERN_DATA(__type) extern __type _export
+#define PR_IMPLEMENT_DATA(__type) __type _export
+
+#define PR_CALLBACK             __cdecl __loadds
+#define PR_STATIC_CALLBACK(__x) static __x PR_CALLBACK
+
+#else /* this must be .EXE */
+#define PR_EXPORT(__type) extern __type _cdecl _export
+#define PR_IMPORT(__type) extern __type _cdecl _export
+#define PR_EXPORT_DATA(__type) extern __type _export
+#define PR_IMPORT_DATA(__type) extern __type _export
+
+#define PR_EXTERN(__type) extern __type _cdecl _export
+#define PR_IMPLEMENT(__type) __type _cdecl _export
+#define PR_EXTERN_DATA(__type) extern __type _export
+#define PR_IMPLEMENT_DATA(__type) __type _export
+
+#define PR_CALLBACK             __cdecl __loadds
+#define PR_STATIC_CALLBACK(__x) __x PR_CALLBACK
+#endif /* _WINDLL */
+
+#elif defined(XP_MAC)
+
+#define PR_EXPORT(__type) extern __declspec(export) __type
+#define PR_EXPORT_DATA(__type) extern __declspec(export) __type
+#define PR_IMPORT(__type) extern __declspec(export) __type
+#define PR_IMPORT_DATA(__type) extern __declspec(export) __type
+
+#define PR_EXTERN(__type) extern __declspec(export) __type
+#define PR_IMPLEMENT(__type) __declspec(export) __type
+#define PR_EXTERN_DATA(__type) extern __declspec(export) __type
+#define PR_IMPLEMENT_DATA(__type) __declspec(export) __type
+
+#define PR_CALLBACK
+#define PR_CALLBACK_DECL
+#define PR_STATIC_CALLBACK(__x) static __x
+
+#elif defined(XP_OS2) && defined(__declspec)
+
+#define PR_EXPORT(__type) extern __declspec(dllexport) __type
+#define PR_EXPORT_DATA(__type) extern __declspec(dllexport) __type
+#define PR_IMPORT(__type) extern  __declspec(dllimport) __type
+#define PR_IMPORT_DATA(__type) extern __declspec(dllimport) __type
+
+#define PR_EXTERN(__type) extern __declspec(dllexport) __type
+#define PR_IMPLEMENT(__type) __declspec(dllexport) __type
+#define PR_EXTERN_DATA(__type) extern __declspec(dllexport) __type
+#define PR_IMPLEMENT_DATA(__type) __declspec(dllexport) __type
+
+#define PR_CALLBACK
+#define PR_CALLBACK_DECL
+#define PR_STATIC_CALLBACK(__x) static __x
+
+#elif defined(XP_OS2_VACPP)
+
+#define PR_EXPORT(__type) extern __type
+#define PR_EXPORT_DATA(__type) extern __type
+#define PR_IMPORT(__type) extern __type
+#define PR_IMPORT_DATA(__type) extern __type
+
+#define PR_EXTERN(__type) extern __type
+#define PR_IMPLEMENT(__type) __type
+#define PR_EXTERN_DATA(__type) extern __type
+#define PR_IMPLEMENT_DATA(__type) __type
+#define PR_CALLBACK _Optlink
+#define PR_CALLBACK_DECL
+#define PR_STATIC_CALLBACK(__x) static __x PR_CALLBACK
+
+#else /* Unix */
+
+/* GCC 3.3 and later support the visibility attribute. */
+#if (__GNUC__ >= 4) || \
+    (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)
+#define PR_VISIBILITY_DEFAULT __attribute__((visibility("default")))
+#else
+#define PR_VISIBILITY_DEFAULT
+#endif
+
+#define PR_EXPORT(__type) extern PR_VISIBILITY_DEFAULT __type
+#define PR_EXPORT_DATA(__type) extern PR_VISIBILITY_DEFAULT __type
+#define PR_IMPORT(__type) extern PR_VISIBILITY_DEFAULT __type
+#define PR_IMPORT_DATA(__type) extern PR_VISIBILITY_DEFAULT __type
+
+#define PR_EXTERN(__type) extern PR_VISIBILITY_DEFAULT __type
+#define PR_IMPLEMENT(__type) PR_VISIBILITY_DEFAULT __type
+#define PR_EXTERN_DATA(__type) extern PR_VISIBILITY_DEFAULT __type
+#define PR_IMPLEMENT_DATA(__type) PR_VISIBILITY_DEFAULT __type
+#define PR_CALLBACK
+#define PR_CALLBACK_DECL
+#define PR_STATIC_CALLBACK(__x) static __x
+
+#endif
+
+#if defined(_NSPR_BUILD_)
+#define NSPR_API(__type) PR_EXPORT(__type)
+#define NSPR_DATA_API(__type) PR_EXPORT_DATA(__type)
+#else
+#define NSPR_API(__type) PR_IMPORT(__type)
+#define NSPR_DATA_API(__type) PR_IMPORT_DATA(__type)
+#endif
+
+/***********************************************************************
+** MACROS:      PR_BEGIN_MACRO
+**              PR_END_MACRO
+** DESCRIPTION:
+**      Macro body brackets so that macros with compound statement definitions
+**      behave syntactically more like functions when called.
+***********************************************************************/
+#define PR_BEGIN_MACRO  do {
+#define PR_END_MACRO    } while (0)
+
+/***********************************************************************
+** MACROS:      PR_BEGIN_EXTERN_C
+**              PR_END_EXTERN_C
+** DESCRIPTION:
+**      Macro shorthands for conditional C++ extern block delimiters.
+***********************************************************************/
+#ifdef __cplusplus
+#define PR_BEGIN_EXTERN_C       extern "C" {
+#define PR_END_EXTERN_C         }
+#else
+#define PR_BEGIN_EXTERN_C
+#define PR_END_EXTERN_C
+#endif
+
+/***********************************************************************
+** MACROS:      PR_BIT
+**              PR_BITMASK
+** DESCRIPTION:
+** Bit masking macros.  XXX n must be <= 31 to be portable
+***********************************************************************/
+#define PR_BIT(n)       ((PRUint32)1 << (n))
+#define PR_BITMASK(n)   (PR_BIT(n) - 1)
+
+/***********************************************************************
+** MACROS:      PR_ROUNDUP
+**              PR_MIN
+**              PR_MAX
+**              PR_ABS
+** DESCRIPTION:
+**      Commonly used macros for operations on compatible types.
+***********************************************************************/
+#define PR_ROUNDUP(x,y) ((((x)+((y)-1))/(y))*(y))
+#define PR_MIN(x,y)     ((x)<(y)?(x):(y))
+#define PR_MAX(x,y)     ((x)>(y)?(x):(y))
+#define PR_ABS(x)       ((x)<0?-(x):(x))
+
+PR_BEGIN_EXTERN_C
+
+/************************************************************************
+** TYPES:       PRUint8
+**              PRInt8
+** DESCRIPTION:
+**  The int8 types are known to be 8 bits each. There is no type that
+**      is equivalent to a plain "char".
+************************************************************************/
+#if PR_BYTES_PER_BYTE == 1
+typedef unsigned char PRUint8;
+/*
+** Some cfront-based C++ compilers do not like 'signed char' and
+** issue the warning message:
+**     warning: "signed" not implemented (ignored)
+** For these compilers, we have to define PRInt8 as plain 'char'.
+** Make sure that plain 'char' is indeed signed under these compilers.
+*/
+#if (defined(HPUX) && defined(__cplusplus) \
+        && !defined(__GNUC__) && __cplusplus < 199707L) \
+    || (defined(SCO) && defined(__cplusplus) \
+        && !defined(__GNUC__) && __cplusplus == 1L)
+typedef char PRInt8;
+#else
+typedef signed char PRInt8;
+#endif
+#else
+#error No suitable type for PRInt8/PRUint8
+#endif
+
+/************************************************************************
+ * MACROS:      PR_INT8_MAX
+ *              PR_INT8_MIN
+ *              PR_UINT8_MAX
+ * DESCRIPTION:
+ *  The maximum and minimum values of a PRInt8 or PRUint8.
+************************************************************************/
+
+#define PR_INT8_MAX 127
+#define PR_INT8_MIN (-128)
+#define PR_UINT8_MAX 255U
+
+/************************************************************************
+** TYPES:       PRUint16
+**              PRInt16
+** DESCRIPTION:
+**  The int16 types are known to be 16 bits each.
+************************************************************************/
+#if PR_BYTES_PER_SHORT == 2
+typedef unsigned short PRUint16;
+typedef short PRInt16;
+#else
+#error No suitable type for PRInt16/PRUint16
+#endif
+
+/************************************************************************
+ * MACROS:      PR_INT16_MAX
+ *              PR_INT16_MIN
+ *              PR_UINT16_MAX
+ * DESCRIPTION:
+ *  The maximum and minimum values of a PRInt16 or PRUint16.
+************************************************************************/
+
+#define PR_INT16_MAX 32767
+#define PR_INT16_MIN (-32768)
+#define PR_UINT16_MAX 65535U
+
+/************************************************************************
+** TYPES:       PRUint32
+**              PRInt32
+** DESCRIPTION:
+**  The int32 types are known to be 32 bits each.
+************************************************************************/
+#if PR_BYTES_PER_INT == 4
+typedef unsigned int PRUint32;
+typedef int PRInt32;
+#define PR_INT32(x)  x
+#define PR_UINT32(x) x ## U
+#elif PR_BYTES_PER_LONG == 4
+typedef unsigned long PRUint32;
+typedef long PRInt32;
+#define PR_INT32(x)  x ## L
+#define PR_UINT32(x) x ## UL
+#else
+#error No suitable type for PRInt32/PRUint32
+#endif
+
+/************************************************************************
+ * MACROS:      PR_INT32_MAX
+ *              PR_INT32_MIN
+ *              PR_UINT32_MAX
+ * DESCRIPTION:
+ *  The maximum and minimum values of a PRInt32 or PRUint32.
+************************************************************************/
+
+#define PR_INT32_MAX PR_INT32(2147483647)
+#define PR_INT32_MIN (-PR_INT32_MAX - 1)
+#define PR_UINT32_MAX PR_UINT32(4294967295)
+
+/************************************************************************
+** TYPES:       PRUint64
+**              PRInt64
+** DESCRIPTION:
+**  The int64 types are known to be 64 bits each. Care must be used when
+**      declaring variables of type PRUint64 or PRInt64. Different hardware
+**      architectures and even different compilers have varying support for
+**      64 bit values. The only guaranteed portability requires the use of
+**      the LL_ macros (see prlong.h).
+************************************************************************/
+#ifdef HAVE_LONG_LONG
+#if PR_BYTES_PER_LONG == 8
+typedef long PRInt64;
+typedef unsigned long PRUint64;
+#elif defined(WIN16)
+typedef __int64 PRInt64;
+typedef unsigned __int64 PRUint64;
+#elif defined(WIN32) && !defined(__GNUC__)
+typedef __int64  PRInt64;
+typedef unsigned __int64 PRUint64;
+#else
+typedef long long PRInt64;
+typedef unsigned long long PRUint64;
+#endif /* PR_BYTES_PER_LONG == 8 */
+#else  /* !HAVE_LONG_LONG */
+typedef struct {
+#ifdef IS_LITTLE_ENDIAN
+    PRUint32 lo, hi;
+#else
+    PRUint32 hi, lo;
+#endif
+} PRInt64;
+typedef PRInt64 PRUint64;
+#endif /* !HAVE_LONG_LONG */
+
+/************************************************************************
+** TYPES:       PRUintn
+**              PRIntn
+** DESCRIPTION:
+**  The PRIntn types are most appropriate for automatic variables. They are
+**      guaranteed to be at least 16 bits, though various architectures may
+**      define them to be wider (e.g., 32 or even 64 bits). These types are
+**      never valid for fields of a structure.
+************************************************************************/
+#if PR_BYTES_PER_INT >= 2
+typedef int PRIntn;
+typedef unsigned int PRUintn;
+#else
+#error 'sizeof(int)' not sufficient for platform use
+#endif
+
+/************************************************************************
+** TYPES:       PRFloat64
+** DESCRIPTION:
+**  NSPR's floating point type is always 64 bits.
+************************************************************************/
+typedef double          PRFloat64;
+
+/************************************************************************
+** TYPES:       PRSize
+** DESCRIPTION:
+**  A type for representing the size of objects.
+************************************************************************/
+typedef size_t PRSize;
+
+
+/************************************************************************
+** TYPES:       PROffset32, PROffset64
+** DESCRIPTION:
+**  A type for representing byte offsets from some location.
+************************************************************************/
+typedef PRInt32 PROffset32;
+typedef PRInt64 PROffset64;
+
+/************************************************************************
+** TYPES:       PRPtrDiff
+** DESCRIPTION:
+**  A type for pointer difference. Variables of this type are suitable
+**      for storing a pointer or pointer subtraction.
+************************************************************************/
+typedef ptrdiff_t PRPtrdiff;
+
+/************************************************************************
+** TYPES:       PRUptrdiff
+** DESCRIPTION:
+**  A type for pointer difference. Variables of this type are suitable
+**      for storing a pointer or pointer sutraction.
+************************************************************************/
+#ifdef _WIN64
+typedef unsigned __int64 PRUptrdiff;
+#else
+typedef unsigned long PRUptrdiff;
+#endif
+
+/************************************************************************
+** TYPES:       PRBool
+** DESCRIPTION:
+**  Use PRBool for variables and parameter types. Use PR_FALSE and PR_TRUE
+**      for clarity of target type in assignments and actual arguments. Use
+**      'if (bool)', 'while (!bool)', '(bool) ? x : y' etc., to test booleans
+**      just as you would C int-valued conditions.
+************************************************************************/
+typedef PRIntn PRBool;
+#define PR_TRUE 1
+#define PR_FALSE 0
+
+/************************************************************************
+** TYPES:       PRPackedBool
+** DESCRIPTION:
+**  Use PRPackedBool within structs where bitfields are not desirable
+**      but minimum and consistant overhead matters.
+************************************************************************/
+typedef PRUint8 PRPackedBool;
+
+/*
+** Status code used by some routines that have a single point of failure or
+** special status return.
+*/
+typedef enum { PR_FAILURE = -1, PR_SUCCESS = 0 } PRStatus;
+
+#ifndef __PRUNICHAR__
+#define __PRUNICHAR__
+#if defined(WIN32) || defined(XP_MAC)
+typedef wchar_t PRUnichar;
+#else
+typedef PRUint16 PRUnichar;
+#endif
+#endif
+
+/*
+** WARNING: The undocumented data types PRWord and PRUword are
+** only used in the garbage collection and arena code.  Do not
+** use PRWord and PRUword in new code.
+**
+** A PRWord is an integer that is the same size as a void*.
+** It implements the notion of a "word" in the Java Virtual
+** Machine.  (See Sec. 3.4 "Words", The Java Virtual Machine
+** Specification, Addison-Wesley, September 1996.
+** http://java.sun.com/docs/books/vmspec/index.html.)
+*/
+#ifdef _WIN64
+typedef __int64 PRWord;
+typedef unsigned __int64 PRUword;
+#else
+typedef long PRWord;
+typedef unsigned long PRUword;
+#endif
+
+#if defined(NO_NSPR_10_SUPPORT)
+#else
+/********* ???????????????? FIX ME       ??????????????????????????? *****/
+/********************** Some old definitions until pr=>ds transition is done ***/
+/********************** Also, we are still using NSPR 1.0. GC ******************/
+/*
+** Fundamental NSPR macros, used nearly everywhere.
+*/
+
+#define PR_PUBLIC_API		PR_IMPLEMENT
+
+/*
+** Macro body brackets so that macros with compound statement definitions
+** behave syntactically more like functions when called.
+*/
+#define NSPR_BEGIN_MACRO        do {
+#define NSPR_END_MACRO          } while (0)
+
+/*
+** Macro shorthands for conditional C++ extern block delimiters.
+*/
+#ifdef NSPR_BEGIN_EXTERN_C
+#undef NSPR_BEGIN_EXTERN_C
+#endif
+#ifdef NSPR_END_EXTERN_C
+#undef NSPR_END_EXTERN_C
+#endif
+
+#ifdef __cplusplus
+#define NSPR_BEGIN_EXTERN_C     extern "C" {
+#define NSPR_END_EXTERN_C       }
+#else
+#define NSPR_BEGIN_EXTERN_C
+#define NSPR_END_EXTERN_C
+#endif
+
+/********* ????????????? End Fix me ?????????????????????????????? *****/
+#endif /* NO_NSPR_10_SUPPORT */
+
+PR_END_EXTERN_C
+
+#if !defined(NO_NSPR_10_SUPPORT)
+#include "base/basictypes.h"
+#endif
+
+#endif /* prtypes_h___ */
+
diff --git a/base/thread.cc b/base/thread.cc
new file mode 100644
index 0000000..6908b9f
--- /dev/null
+++ b/base/thread.cc
@@ -0,0 +1,258 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <process.h>
+#include <windows.h>
+
+#include "base/thread.h"
+
+#include "base/message_loop.h"
+#include "base/ref_counted.h"
+#include "base/string_util.h"
+#include "base/win_util.h"
+
+// This class is used when starting a thread.  It passes information to the
+// thread function.  It is referenced counted so we can cleanup the event
+// object used to synchronize thread startup properly.
+class ThreadStartInfo : public base::RefCountedThreadSafe<ThreadStartInfo> {
+ public:
+  Thread* self;
+  HANDLE start_event;
+
+  explicit ThreadStartInfo(Thread* t)
+      : self(t),
+        start_event(CreateEvent(NULL, FALSE, FALSE, NULL)) {
+  }
+
+  ~ThreadStartInfo() {
+    CloseHandle(start_event);
+  }
+};
+
+// This task is used to trigger the message loop to exit.
+class ThreadQuitTask : public Task {
+ public:
+  virtual void Run() {
+    MessageLoop::current()->Quit();
+    Thread::SetThreadWasQuitProperly(true);
+  }
+};
+
+// Once an object is signaled, quits the current inner message loop.
+class QuitOnSignal : public MessageLoop::Watcher {
+ public:
+  explicit QuitOnSignal(HANDLE signal) : signal_(signal) {
+  }
+  virtual void OnObjectSignaled(HANDLE object) {
+    DCHECK_EQ(object, signal_);
+    MessageLoop::current()->WatchObject(signal_, NULL);
+    MessageLoop::current()->Quit();
+  }
+ private:
+  HANDLE signal_;
+  DISALLOW_EVIL_CONSTRUCTORS(QuitOnSignal);
+};
+
+Thread::Thread(const char *name)
+    : thread_(NULL),
+      thread_id_(0),
+      message_loop_(NULL),
+      name_(name) {
+  DCHECK(tls_index_) << "static initializer failed";
+}
+
+Thread::~Thread() {
+  Stop();
+}
+
+// We use this thread-local variable to record whether or not a thread exited
+// because its Stop method was called.  This allows us to catch cases where
+// MessageLoop::Quit() is called directly, which is unexpected when using a
+// Thread to setup and run a MessageLoop.
+// Note that if we start doing complex stuff in other static initializers
+// this could cause problems.
+TLSSlot Thread::tls_index_ = ThreadLocalStorage::Alloc();
+
+void Thread::SetThreadWasQuitProperly(bool flag) {
+#ifndef NDEBUG
+  ThreadLocalStorage::Set(tls_index_, reinterpret_cast<void*>(flag));
+#endif
+}
+
+bool Thread::GetThreadWasQuitProperly() {
+  bool quit_properly = true;
+#ifndef NDEBUG
+  quit_properly = (ThreadLocalStorage::Get(tls_index_) != 0);
+#endif
+  return quit_properly;
+}
+
+// The information on how to set the thread name comes from
+// a MSDN article: http://msdn2.microsoft.com/en-us/library/xcb2z8hs.aspx
+#define MS_VC_EXCEPTION 0x406D1388
+
+typedef struct tagTHREADNAME_INFO {
+  DWORD dwType;  // Must be 0x1000.
+  LPCSTR szName;  // Pointer to name (in user addr space).
+  DWORD dwThreadID;  // Thread ID (-1=caller thread).
+  DWORD dwFlags;  // Reserved for future use, must be zero.
+} THREADNAME_INFO;
+
+
+// On XP, you can only get the ThreadId of the current
+// thread.  So it is expected that you'll call this after the
+// thread starts up; hence, it is static.
+void Thread::SetThreadName(const char* name, DWORD tid) {
+  THREADNAME_INFO info;
+  info.dwType = 0x1000;
+  info.szName = name;
+  info.dwThreadID = tid;
+  info.dwFlags = 0;
+
+  __try {
+    RaiseException(MS_VC_EXCEPTION, 0,
+                   sizeof(info)/sizeof(DWORD),
+                   reinterpret_cast<DWORD*>(&info));
+  } __except(EXCEPTION_CONTINUE_EXECUTION) {
+  }
+}
+
+
+bool Thread::Start() {
+  return StartWithStackSize(0);
+}
+
+bool Thread::StartWithStackSize(size_t stack_size) {
+  DCHECK(!thread_id_ && !thread_);
+  SetThreadWasQuitProperly(false);
+  scoped_refptr<ThreadStartInfo> info = new ThreadStartInfo(this);
+
+  unsigned int flags = 0;
+  if (win_util::GetWinVersion() >= win_util::WINVERSION_XP && stack_size) {
+    flags = STACK_SIZE_PARAM_IS_A_RESERVATION;
+  } else {
+    stack_size = 0;
+  }
+  thread_ = reinterpret_cast<HANDLE>(
+      _beginthreadex(NULL,
+                     static_cast<unsigned int>(stack_size),
+                     ThreadFunc,
+                     info,
+                     flags,
+                     &thread_id_));
+  if (!thread_) {
+    DLOG(ERROR) << "failed to create thread";
+    return false;
+  }
+
+  // Wait for the thread to start and initialize message_loop_
+  WaitForSingleObject(info->start_event, INFINITE);
+  return true;
+}
+
+void Thread::Stop() {
+  InternalStop(false);
+}
+
+void Thread::NonBlockingStop() {
+  InternalStop(true);
+}
+
+void Thread::InternalStop(bool run_message_loop) {
+  if (!thread_)
+    return;
+
+  DCHECK_NE(thread_id_, GetCurrentThreadId()) << "Can't call Stop() on itself";
+
+  // We had better have a message loop at this point!  If we do not, then it
+  // most likely means that the thread terminated unexpectedly, probably due
+  // to someone calling Quit() on our message loop directly.
+  DCHECK(message_loop_);
+
+  message_loop_->PostTask(FROM_HERE, new ThreadQuitTask());
+
+  if (run_message_loop) {
+    QuitOnSignal signal_watcher(thread_);
+    MessageLoop::current()->WatchObject(thread_, &signal_watcher);
+    bool old_state = MessageLoop::current()->NestableTasksAllowed();
+    MessageLoop::current()->SetNestableTasksAllowed(true);
+    MessageLoop::current()->Run();
+    // Restore task state.
+    MessageLoop::current()->SetNestableTasksAllowed(old_state);
+  } else {
+    // Wait for the thread to exit.
+    WaitForSingleObject(thread_, INFINITE);
+  }
+  CloseHandle(thread_);
+
+  // Reset state.
+  thread_ = NULL;
+  message_loop_ = NULL;
+}
+
+/*static*/
+unsigned __stdcall Thread::ThreadFunc(void* param) {
+  // The message loop for this thread.
+  MessageLoop message_loop;
+
+  Thread* self;
+
+  // Complete the initialization of our Thread object.  We grab an owning
+  // reference to the ThreadStartInfo object to ensure that the start_event
+  // object is not prematurely closed.
+  {
+    scoped_refptr<ThreadStartInfo> info = static_cast<ThreadStartInfo*>(param);
+    self = info->self;
+    self->message_loop_ = &message_loop;
+    SetThreadName(self->thread_name().c_str(), GetCurrentThreadId());
+    message_loop.SetThreadName(self->thread_name());
+    SetEvent(info->start_event);
+  }
+
+  // Let the thread do extra initialization.
+  self->Init();
+
+  message_loop.Run();
+
+  // Let the thread do extra cleanup.
+  self->CleanUp();
+
+  // Assert that MessageLoop::Quit was called by ThreadQuitTask.
+  DCHECK(Thread::GetThreadWasQuitProperly());
+
+  // Clearing this here should be unnecessary since we should only be here if
+  // Thread::Stop was called (the above DCHECK asserts that).  However, to help
+  // make it easier to track down problems in release builds, we null out the
+  // message_loop_ pointer.
+  self->message_loop_ = NULL;
+
+  // We can't receive messages anymore.
+  self->thread_id_ = 0;
+  return 0;
+}
diff --git a/base/thread.h b/base/thread.h
new file mode 100644
index 0000000..7ed2402
--- /dev/null
+++ b/base/thread.h
@@ -0,0 +1,135 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_THREAD_H__
+#define BASE_THREAD_H__
+
+#include <wtypes.h>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/thread_local_storage.h"
+
+class MessageLoop;
+
+// A simple thread abstraction that establishes a MessageLoop on a new thread.
+// The consumer uses the MessageLoop of the thread to cause code to execute on
+// the thread.  When this object is destroyed the thread is terminated.  All
+// pending tasks queued on the thread's message loop will run to completion
+// before the thread is terminated.
+//
+class Thread {
+ public:
+  // Constructor.
+  // name is a display string to identify the thread.
+  explicit Thread(const char *name);
+
+  // Destroys the thread, stopping it if necessary.
+  //
+  virtual ~Thread();
+
+  // Starts the thread.  Returns true if the thread was successfully started;
+  // otherwise, returns false.  Upon successful return, the message_loop()
+  // getter will return non-null.
+  //
+  bool Start();
+
+  // Starts the thread. Behaves exactly like Start in addition to allow to
+  // override the default process stack size. This is not the initial stack size
+  // but the maximum stack size that thread is allowed to use.
+  bool StartWithStackSize(size_t stack_size);
+
+  // Signals the thread to exit and returns once the thread has exited.  After
+  // this method returns, the Thread object is completely reset and may be used
+  // as if it were newly constructed (i.e., Start may be called again).
+  //
+  // Stop may be called multiple times and is simply ignored if the thread is
+  // already stopped.
+  //
+  // NOTE: This method is optional.  It is not strictly necessary to call this
+  // method as the Thread's destructor will take care of stopping the thread if
+  // necessary.
+  //
+  void Stop();
+
+  // Signals the thread to exit and returns once the thread has exited.
+  // Meanwhile, a message loop is run. After this method returns, the Thread
+  // object is completely reset and may be used as if it were newly constructed
+  // (i.e., Start may be called again).
+  //
+  // NonBlockingStop may be called multiple times and is simply ignored if the
+  // thread is already stopped.
+  //
+  // NOTE: The behavior is the same as Stop() except that pending tasks are run.
+  void NonBlockingStop();
+
+  // Returns the message loop for this thread.  Use the MessageLoop's
+  // Posttask methods to execute code on the thread.  This only returns
+  // non-null after a successful call to Start.  After Stop has been called,
+  // this will return NULL.
+  //
+  // NOTE: You must not call this MessageLoop's Quit method directly.  Use
+  // the Thread's Stop method instead.
+  //
+  MessageLoop* message_loop() const { return message_loop_; }
+
+  // Set the name of this thread (for display in debugger too).
+  const std::string &thread_name() { return name_; }
+
+  static void SetThreadWasQuitProperly(bool flag);
+  static bool GetThreadWasQuitProperly();
+
+  // Sets the thread name if a debugger is currently attached. Has no effect
+  // otherwise. To set the name of the current thread, pass GetCurrentThreadId()
+  // as the tid parameter.
+  static void SetThreadName(const char* name, DWORD tid);
+
+ protected:
+  // Called just prior to starting the message loop
+  virtual void Init() { }
+
+  // Called just after the message loop ends
+  virtual void CleanUp() { }
+
+  // Stops the thread. Called by either Stop() or NonBlockingStop().
+  void InternalStop(bool run_message_loop);
+
+ private:
+  static unsigned __stdcall ThreadFunc(void* param);
+
+  HANDLE thread_;
+  unsigned thread_id_;
+  MessageLoop* message_loop_;
+  std::string name_;
+  static TLSSlot tls_index_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(Thread);
+};
+
+#endif  // BASE_THREAD_H__
diff --git a/base/thread_local_storage.h b/base/thread_local_storage.h
new file mode 100644
index 0000000..e27edb8
--- /dev/null
+++ b/base/thread_local_storage.h
@@ -0,0 +1,95 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_THREAD_LOCAL_STORAGE_H__
+#define BASE_THREAD_LOCAL_STORAGE_H__
+
+#include "base/basictypes.h"
+
+#ifdef WIN32
+typedef int TLSSlot;
+#else
+typedef pthread_key_t TLSSlot;
+#endif
+
+// Wrapper for thread local storage.  This class doesn't
+// do much except provide an API for portability later.
+class ThreadLocalStorage {
+ public:
+  // Prototype for the TLS destructor function, which can be
+  // optionally used to cleanup thread local storage on
+  // thread exit.  'value' is the data that is stored
+  // in thread local storage.
+  typedef void (*TLSDestructorFunc)(void* value);
+
+  // Allocate a TLS 'slot'.
+  // 'destructor' is a pointer to a function to perform
+  // per-thread cleanup of this object.  If set to NULL,
+  // no cleanup is done for this TLS slot.
+  // Returns an index > 0 on success, or -1 on failure.
+  static TLSSlot Alloc(TLSDestructorFunc destructor = NULL);
+
+  // Free a previously allocated TLS 'slot'.
+  // If a destructor was set for this slot, removes
+  // the destructor so that remaining threads exiting
+  // will not free data.
+  static void Free(TLSSlot slot);
+
+  // Get the thread-local value stored in slot 'slot'.
+  // Values are guaranteed to initially be zero.
+  static void* Get(TLSSlot slot);
+
+  // Set the thread-local value stored in slot 'slot' to
+  // value 'value'.
+  static void Set(TLSSlot slot, void* value);
+
+#ifdef WIN32
+  // Function called when on thread exit to call TLS
+  // destructor functions.  This function is used internally.
+  static void ThreadExit();
+
+ private:
+  // Function to lazily initialize our thread local storage.
+  static void **Initialize();
+
+ private:
+  // The maximum number of 'slots' in our thread local storage stack.
+  // For now, this is fixed.  We could either increase statically, or
+  // we could make it dynamic in the future.
+  static const int kThreadLocalStorageSize = 64;
+
+  static long tls_key_;
+  static long tls_max_;
+  static TLSDestructorFunc tls_destructors_[kThreadLocalStorageSize];
+#endif
+
+  DISALLOW_EVIL_CONSTRUCTORS(ThreadLocalStorage);
+};
+
+#endif  // BASE_THREAD_LOCAL_STORAGE_H__
diff --git a/base/thread_local_storage_unittest.cc b/base/thread_local_storage_unittest.cc
new file mode 100644
index 0000000..fff0d2a
--- /dev/null
+++ b/base/thread_local_storage_unittest.cc
@@ -0,0 +1,109 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+#include <process.h>
+
+#include "base/thread_local_storage.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// ignore warnings about ptr->int conversions that we use when
+// storing ints into ThreadLocalStorage.
+#pragma warning(disable : 4311 4312)
+
+namespace {
+  class ThreadLocalStorageTest : public testing::Test {
+  };
+}
+
+
+TEST(ThreadLocalStorageTest, Basics) {
+  int index = ThreadLocalStorage::Alloc();
+  ThreadLocalStorage::Set(index, reinterpret_cast<void*>(123));
+  int value = reinterpret_cast<int>(ThreadLocalStorage::Get(index));
+  EXPECT_EQ(value, 123);
+}
+
+const int kInitialTlsValue = 0x5555;
+static int tls_index = 0;
+
+unsigned __stdcall TLSTestThreadMain(void* param) {
+  // param contains the thread local storage index.
+  int *index = reinterpret_cast<int*>(param);
+  *index = kInitialTlsValue;
+
+  ThreadLocalStorage::Set(tls_index, index);
+
+  int *ptr = static_cast<int*>(ThreadLocalStorage::Get(tls_index));
+  EXPECT_EQ(ptr, index);
+  EXPECT_EQ(*ptr, kInitialTlsValue);
+  *index = 0;
+
+  ptr = static_cast<int*>(ThreadLocalStorage::Get(tls_index));
+  EXPECT_EQ(ptr, index);
+  EXPECT_EQ(*ptr, 0);
+  return 0;
+}
+
+void ThreadLocalStorageCleanup(void *value) {
+  int *ptr = reinterpret_cast<int*>(value);
+  if (ptr)
+    *ptr = kInitialTlsValue;
+}
+
+
+TEST(ThreadLocalStorageTest, TLSDestructors) {
+  // Create a TLS index with a destructor.  Create a set of
+  // threads that set the TLS, while the destructor cleans it up.
+  // After the threads finish, verify that the value is cleaned up.
+  const int kNumThreads = 5;
+  HANDLE threads[kNumThreads];
+  int values[kNumThreads];
+
+  tls_index = ThreadLocalStorage::Alloc(ThreadLocalStorageCleanup);
+
+  // Spawn the threads.
+  for (int16 index = 0; index < kNumThreads; index++) {
+    values[index] = kInitialTlsValue;
+    void *argument = static_cast<void*>(&(values[index]));
+    unsigned thread_id;
+    threads[index] = reinterpret_cast<HANDLE>(
+      _beginthreadex(NULL, 0, TLSTestThreadMain, argument, 0, &thread_id));
+    EXPECT_NE(threads[index], (HANDLE)NULL);
+  }
+
+  // Wait for the threads to finish.
+  for (int index = 0; index < kNumThreads; index++) {
+    DWORD rv = WaitForSingleObject(threads[index], 60*1000);
+    EXPECT_EQ(rv, WAIT_OBJECT_0);  // verify all threads finished
+
+    // verify that the destructor was called and that we reset.
+    EXPECT_EQ(values[index], kInitialTlsValue);
+  }
+}
diff --git a/base/thread_unittest.cc b/base/thread_unittest.cc
new file mode 100644
index 0000000..ba91005
--- /dev/null
+++ b/base/thread_unittest.cc
@@ -0,0 +1,157 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/lock.h"
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "base/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+  class ThreadTest : public testing::Test {
+  };
+}
+
+class ToggleValue : public Task {
+ public:
+  explicit ToggleValue(bool* value) : value_(value) {
+  }
+  virtual void Run() {
+    *value_ = !*value_;
+  }
+ private:
+  bool* value_;
+};
+
+class SleepSome : public Task {
+ public:
+  explicit SleepSome(DWORD msec) : msec_(msec) {
+  }
+  virtual void Run() {
+    Sleep(msec_);
+  }
+ private:
+  DWORD msec_;
+};
+
+TEST(ThreadTest, BasicTest1) {
+  Thread a("BasicTest1");
+  a.Start();
+  EXPECT_TRUE(a.message_loop());
+  a.Stop();
+  EXPECT_FALSE(a.message_loop());
+  a.Start();
+  EXPECT_TRUE(a.message_loop());
+  a.Stop();
+  EXPECT_FALSE(a.message_loop());
+}
+
+TEST(ThreadTest, BasicTest2) {
+  Thread a("BasicTest2");
+  a.Start();
+  EXPECT_TRUE(a.message_loop());
+
+  bool was_invoked = false;
+  a.message_loop()->PostTask(FROM_HERE, new ToggleValue(&was_invoked));
+
+  // wait for the task to run (we could use a kernel event here
+  // instead to avoid busy waiting, but this is sufficient for
+  // testing purposes).
+  for (int i = 50; i >= 0 && !was_invoked; --i) {
+    Sleep(20);
+  }
+  EXPECT_TRUE(was_invoked);
+}
+
+TEST(ThreadTest, BasicTest3) {
+  bool was_invoked = false;
+  {
+    Thread a("BasicTest3");
+    a.Start();
+    EXPECT_TRUE(a.message_loop());
+
+    // Test that all events are dispatched before the Thread object is
+    // destroyed.  We do this by dispatching a sleep event before the
+    // event that will toggle our sentinel value.
+    a.message_loop()->PostTask(FROM_HERE, new SleepSome(500));
+    a.message_loop()->PostTask(FROM_HERE, new ToggleValue(&was_invoked));
+  }
+  EXPECT_TRUE(was_invoked);
+}
+
+namespace {
+  class DummyTask : public Task {
+   public:
+    explicit DummyTask(int* counter) : counter_(counter) {
+    }
+    void Run() {
+      // Let's make sure that no other thread is running while we
+      // are executing this code.  The sleeps help us assert that.
+      int initial_value = *counter_;
+      Sleep(1);
+      ++(*counter_);
+      Sleep(1);
+      int final_value = *counter_;
+      DCHECK(final_value == initial_value + 1);
+    }
+   private:
+    int* counter_;
+  };
+}
+
+namespace {
+  // This task just continuously posts events to its thread, keeping it well
+  // saturated with work.  If our thread interlocking is not fair, then we will
+  // never exit.
+  class BusyTask : public Task {
+   public:
+    explicit BusyTask(HANDLE quit_event) : quit_event_(quit_event) {
+    }
+    void Run() {
+      if (WaitForSingleObject(quit_event_, 0) != WAIT_OBJECT_0)
+        MessageLoop::current()->PostTask(FROM_HERE, new BusyTask(quit_event_));
+    }
+   private:
+    HANDLE quit_event_;
+  };
+
+  // This task just tries to set the quit sentinel to signal the busy thread
+  // to stop doing work.
+  class InterruptBusyTask : public Task {
+   public:
+    explicit InterruptBusyTask(HANDLE quit_event) : quit_event_(quit_event) {
+    }
+    void Run() {
+      SetEvent(quit_event_);
+    }
+   private:
+    HANDLE quit_event_;
+  };
+}
+
diff --git a/base/time.cc b/base/time.cc
new file mode 100644
index 0000000..aa915e6
--- /dev/null
+++ b/base/time.cc
@@ -0,0 +1,190 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/time.h"
+#include "base/string_util.h"
+#include "base/third_party/nspr/prtime.h"
+
+#include "base/logging.h"
+
+namespace {
+
+// Time between resampling the un-granular clock for this API.  60 seconds.
+const int kMaxMillisecondsToAvoidDrift = 60 * Time::kMillisecondsPerSecond;
+
+}  // namespace
+
+// TimeDelta ------------------------------------------------------------------
+
+// static
+TimeDelta TimeDelta::FromDays(int64 days) {
+  return TimeDelta(days * Time::kMicrosecondsPerDay);
+}
+
+// static
+TimeDelta TimeDelta::FromHours(int64 hours) {
+  return TimeDelta(hours * Time::kMicrosecondsPerHour);
+}
+
+// static
+TimeDelta TimeDelta::FromMinutes(int64 minutes) {
+  return TimeDelta(minutes * Time::kMicrosecondsPerMinute);
+}
+
+// static
+TimeDelta TimeDelta::FromSeconds(int64 secs) {
+  return TimeDelta(secs * Time::kMicrosecondsPerSecond);
+}
+
+// static
+TimeDelta TimeDelta::FromMilliseconds(int64 ms) {
+  return TimeDelta(ms * Time::kMicrosecondsPerMillisecond);
+}
+
+// static
+TimeDelta TimeDelta::FromMicroseconds(int64 us) {
+  return TimeDelta(us);
+}
+
+int TimeDelta::InDays() const {
+  return static_cast<int>(delta_ / Time::kMicrosecondsPerDay);
+}
+
+int TimeDelta::InHours() const {
+  return static_cast<int>(delta_ / Time::kMicrosecondsPerHour);
+}
+
+double TimeDelta::InSecondsF() const {
+  return static_cast<double>(delta_) / Time::kMicrosecondsPerSecond;
+}
+
+int64 TimeDelta::InSeconds() const {
+  return delta_ / Time::kMicrosecondsPerSecond;
+}
+
+double TimeDelta::InMillisecondsF() const {
+  return static_cast<double>(delta_) / Time::kMicrosecondsPerMillisecond;
+}
+
+int64 TimeDelta::InMilliseconds() const {
+  return delta_ / Time::kMicrosecondsPerMillisecond;
+}
+
+int64 TimeDelta::InMicroseconds() const {
+  return delta_;
+}
+
+// Time -----------------------------------------------------------------------
+
+int64 Time::initial_time_ = 0;
+TimeTicks Time::initial_ticks_;
+
+// static
+void Time::InitializeClock()
+{
+    initial_ticks_ = TimeTicks::Now();
+    initial_time_ = CurrentWallclockMicroseconds();
+}
+
+// static
+Time Time::Now() {
+  if (initial_time_ == 0)
+    InitializeClock();
+
+  // We implement time using the high-resolution timers so that we can get
+  // timeouts which are smaller than 10-15ms.  If we just used
+  // CurrentWallclockMicroseconds(), we'd have the less-granular timer.
+  //
+  // To make this work, we initialize the clock (initial_time) and the
+  // counter (initial_ctr).  To compute the initial time, we can check
+  // the number of ticks that have elapsed, and compute the delta.
+  //
+  // To avoid any drift, we periodically resync the counters to the system
+  // clock.
+  while(true) {
+    TimeTicks ticks = TimeTicks::Now();
+
+    // Calculate the time elapsed since we started our timer
+    TimeDelta elapsed = ticks - initial_ticks_;
+
+    // Check if enough time has elapsed that we need to resync the clock.
+    if (elapsed.InMilliseconds() > kMaxMillisecondsToAvoidDrift) {
+      InitializeClock();
+      continue;
+    }
+
+    return elapsed + initial_time_;
+  }
+}
+
+// static
+Time Time::FromTimeT(time_t tt) {
+  if (tt == 0)
+    return Time();  // Preserve 0 so we can tell it doesn't exist.
+  return (tt * kMicrosecondsPerSecond) + kTimeTToMicrosecondsOffset;
+}
+
+time_t Time::ToTimeT() const {
+  if (us_ == 0)
+    return 0;  // Preserve 0 so we can tell it doesn't exist.
+  return (us_ - kTimeTToMicrosecondsOffset) / kMicrosecondsPerSecond;
+}
+
+double Time::ToDoubleT() const {
+  if (us_ == 0)
+    return 0;  // Preserve 0 so we can tell it doesn't exist.
+  return (static_cast<double>(us_ - kTimeTToMicrosecondsOffset) /
+          static_cast<double>(kMicrosecondsPerSecond));
+}
+
+Time Time::LocalMidnight() const {
+  Exploded exploded;
+  LocalExplode(&exploded);
+  exploded.hour = 0;
+  exploded.minute = 0;
+  exploded.second = 0;
+  exploded.millisecond = 0;
+  return FromLocalExploded(exploded);
+}
+
+// static
+bool Time::FromString(const wchar_t* time_string, Time* parsed_time) {
+  DCHECK((time_string != NULL) && (parsed_time != NULL));
+  std::string ascii_time_string = WideToUTF8(time_string);
+  if (ascii_time_string.length() == 0)
+    return false;
+  PRTime result_time = 0;
+  PRStatus result = PR_ParseTimeString(ascii_time_string.c_str(), PR_FALSE,
+                                       &result_time);
+  if (PR_SUCCESS != result)
+    return false;
+  result_time += kTimeTToMicrosecondsOffset;
+  *parsed_time = Time(result_time);
+  return true;
+}
diff --git a/base/time.h b/base/time.h
new file mode 100644
index 0000000..e0c806d
--- /dev/null
+++ b/base/time.h
@@ -0,0 +1,469 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Time represents an absolute point in time, internally represented as
+// microseconds (s/1,000,000) since a platform-dependent epoch.  Each
+// platform's epoch, along with other system-dependent clock interface
+// routines, is defined in time_PLATFORM.cc.
+//
+// TimeDelta represents a duration of time, internally represented in
+// microseconds.
+//
+// TimeTicks represents an abstract time that is always incrementing for use
+// in measuring time durations. It is internally represented in microseconds.
+// It can not be converted to a human-readable time, but is guaranteed not to
+// decrease (if the user changes the computer clock, Time::Now() may actually
+// decrease or jump).
+//
+// These classes are represented as only a 64-bit value, so they can be
+// efficiently passed by value.
+
+#ifndef BASE_TIME_H__
+#define BASE_TIME_H__
+
+#ifdef WIN32
+// For FILETIME in FromFileTime, until it moves to a new converter class.
+// See TODO(iyengar) below.
+#include <windows.h>
+#endif
+
+#include <time.h>
+#include "base/basictypes.h"
+#include "testing/gtest/include/gtest/gtest_prod.h"
+
+class Time;
+class TimeTicks;
+
+// This unit test does a lot of manual time manipulation.
+class PageLoadTrackerUnitTest;
+
+// TimeDelta ------------------------------------------------------------------
+
+class TimeDelta {
+ public:
+  TimeDelta() : delta_(0) {
+  }
+
+  // Converts units of time to TimeDeltas.
+  static TimeDelta FromDays(int64 days);
+  static TimeDelta FromHours(int64 hours);
+  static TimeDelta FromMinutes(int64 minutes);
+  static TimeDelta FromSeconds(int64 secs);
+  static TimeDelta FromMilliseconds(int64 ms);
+  static TimeDelta FromMicroseconds(int64 us);
+
+  // Returns the internal numeric value of the TimeDelta object. Please don't
+  // use this and do arithmetic on it, as it is more error prone than using the
+  // provided operators.
+  int64 ToInternalValue() const {
+    return delta_;
+  }
+
+  // Returns the time delta in some unit. The F versions return a floating
+  // point value, the "regular" versions return a rounded-down value.
+  int InDays() const;
+  int InHours() const;
+  double InSecondsF() const;
+  int64 InSeconds() const;
+  double InMillisecondsF() const;
+  int64 InMilliseconds() const;
+  int64 InMicroseconds() const;
+
+  TimeDelta& operator=(TimeDelta other) {
+    delta_ = other.delta_;
+    return *this;
+  }
+
+  // Computations with other deltas.
+  TimeDelta operator+(TimeDelta other) const {
+    return TimeDelta(delta_ + other.delta_);
+  }
+  TimeDelta operator-(TimeDelta other) const {
+    return TimeDelta(delta_ - other.delta_);
+  }
+
+  TimeDelta& operator+=(TimeDelta other) {
+    delta_ += other.delta_;
+    return *this;
+  }
+  TimeDelta& operator-=(TimeDelta other) {
+    delta_ -= other.delta_;
+    return *this;
+  }
+  TimeDelta operator-() const {
+    return TimeDelta(-delta_);
+  }
+
+  // Computations with ints, note that we only allow multiplicative operations
+  // with ints, and additive operations with other deltas.
+  TimeDelta operator*(int64 a) const {
+    return TimeDelta(delta_ * a);
+  }
+  TimeDelta operator/(int64 a) const {
+    return TimeDelta(delta_ / a);
+  }
+  TimeDelta& operator*=(int64 a) {
+    delta_ *= a;
+    return *this;
+  }
+  TimeDelta& operator/=(int64 a) {
+    delta_ /= a;
+    return *this;
+  }
+  int64 operator/(TimeDelta a) const {
+    return delta_ / a.delta_;
+  }
+
+  // Defined below because it depends on the definition of the other classes.
+  Time operator+(Time t) const;
+  TimeTicks operator+(TimeTicks t) const;
+
+  // Comparison operators.
+  bool operator==(TimeDelta other) const {
+    return delta_ == other.delta_;
+  }
+  bool operator!=(TimeDelta other) const {
+    return delta_ != other.delta_;
+  }
+  bool operator<(TimeDelta other) const {
+    return delta_ < other.delta_;
+  }
+  bool operator<=(TimeDelta other) const {
+    return delta_ <= other.delta_;
+  }
+  bool operator>(TimeDelta other) const {
+    return delta_ > other.delta_;
+  }
+  bool operator>=(TimeDelta other) const {
+    return delta_ >= other.delta_;
+  }
+
+ private:
+  friend class Time;
+  friend class TimeTicks;
+  friend TimeDelta operator*(int64 a, TimeDelta td);
+
+  // Constructs a delta given the duration in microseconds. This is private
+  // to avoid confusion by callers with an integer constructor. Use
+  // FromSeconds, FromMilliseconds, etc. instead.
+  explicit TimeDelta(int64 delta_us) : delta_(delta_us) {
+  }
+
+  // Delta in microseconds.
+  int64 delta_;
+};
+
+inline TimeDelta operator*(int64 a, TimeDelta td) {
+  return TimeDelta(a * td.delta_);
+}
+
+// Time -----------------------------------------------------------------------
+
+// Represents a wall clock time.
+class Time {
+ public:
+  static const int64 kMillisecondsPerSecond = 1000;
+  static const int64 kMicrosecondsPerMillisecond = 1000;
+  static const int64 kMicrosecondsPerSecond = kMicrosecondsPerMillisecond *
+                                              kMillisecondsPerSecond;
+  static const int64 kMicrosecondsPerMinute = kMicrosecondsPerSecond * 60;
+  static const int64 kMicrosecondsPerHour = kMicrosecondsPerMinute * 60;
+  static const int64 kMicrosecondsPerDay = kMicrosecondsPerHour * 24;
+  static const int64 kMicrosecondsPerWeek = kMicrosecondsPerDay * 7;
+
+  // Represents an exploded time that can be formatted nicely. This is kind of
+  // like the Win32 SYSTEMTIME structure or the Unix "struct tm" with a few
+  // additions and changes to prevent errors.
+  struct Exploded {
+    int year;          // Four digit year "2007"
+    int month;         // 1-based month (values 1 = January, etc.)
+    int day_of_week;   // 0-based day of week (0 = Sunday, etc.)
+    int day_of_month;  // 1-based day of month (1-31)
+    int hour;          // Hour within the current day (0-23)
+    int minute;        // Minute within the current hour (0-59)
+    int second;        // Second within the current minute (0-59 plus leap
+                       //   seconds which may take it up to 60).
+    int millisecond;   // Milliseconds within the current second (0-999)
+  };
+
+  // Contains the NULL time. Use Time::Now() to get the current time.
+  explicit Time() : us_(0) {
+  }
+
+  // Returns true if the time object has not been initialized.
+  bool is_null() const {
+    return us_ == 0;
+  }
+
+  // Returns the current time. Watch out, the system might adjust its clock
+  // in which case time will actually go backwards. We don't guarantee that
+  // times are increasing, or that two calls to Now() won't be the same.
+  static Time Now();
+
+  // Converts to/from time_t in UTC and a Time class.
+  // TODO(brettw) this should be removed once everybody starts using the |Time|
+  // class.
+  static Time FromTimeT(time_t tt);
+  time_t ToTimeT() const;
+
+  // Converts time to a double which is the number of seconds since epoch
+  // (Jan 1, 1970).  Webkit uses this format to represent time.
+  double ToDoubleT() const;
+
+#ifdef WIN32
+  static Time FromFileTime(FILETIME ft);
+  FILETIME ToFileTime() const;
+#endif
+
+  // Converts an exploded structure representing either the local time or UTC
+  // into a Time class.
+  static Time FromUTCExploded(const Exploded& exploded) {
+    return FromExploded(false, exploded);
+  }
+  static Time FromLocalExploded(const Exploded& exploded) {
+    return FromExploded(true, exploded);
+  }
+
+  // Converts an integer value representing Time to a class. This is used
+  // when deserializing a |Time| structure, using a value known to be
+  // compatible. It is not provided as a constructor because the integer type
+  // may be unclear from the perspective of a caller.
+  static Time FromInternalValue(int64 us) {
+    return Time(us);
+  }
+
+  // Converts a string representation of time to a Time object.
+  // An example of a time string which is converted is as below:-
+  // "Tue, 15 Nov 1994 12:45:26 GMT". If the timezone is not specified
+  // in the input string, we assume local time.
+  // TODO(iyengar) Move the FromString/FromTimeT/ToTimeT/FromFileTime to
+  // a new time converter class.
+  static bool FromString(const wchar_t* time_string, Time* parsed_time);
+
+  // For serializing, use FromInternalValue to reconstitute. Please don't use
+  // this and do arithmetic on it, as it is more error prone than using the
+  // provided operators.
+  int64 ToInternalValue() const {
+    return us_;
+  }
+
+  // Fills the given exploded structure with either the local time or UTC from
+  // this time structure (containing UTC).
+  void UTCExplode(Exploded* exploded) const {
+    return Explode(false, exploded);
+  }
+  void LocalExplode(Exploded* exploded) const {
+    return Explode(true, exploded);
+  }
+
+  // Rounds this time down to the nearest day in local time. It will represent
+  // midnight on that day.
+  Time LocalMidnight() const;
+
+  Time& operator=(Time other) {
+    us_ = other.us_;
+    return *this;
+  }
+
+  // Compute the difference between two times.
+  TimeDelta operator-(Time other) const {
+    return TimeDelta(us_ - other.us_);
+  }
+
+  // Modify by some time delta.
+  Time& operator+=(TimeDelta delta) {
+    us_ += delta.delta_;
+    return *this;
+  }
+  Time& operator-=(TimeDelta delta) {
+    us_ -= delta.delta_;
+    return *this;
+  }
+
+  // Return a new time modified by some delta.
+  Time operator+(TimeDelta delta) const {
+    return us_ + delta.delta_;
+  }
+  Time operator-(TimeDelta delta) const {
+    return us_ - delta.delta_;
+  }
+
+  // Comparison operators
+  bool operator==(Time other) const {
+    return us_ == other.us_;
+  }
+  bool operator!=(Time other) const {
+    return us_ != other.us_;
+  }
+  bool operator<(Time other) const {
+    return us_ < other.us_;
+  }
+  bool operator<=(Time other) const {
+    return us_ <= other.us_;
+  }
+  bool operator>(Time other) const {
+    return us_ > other.us_;
+  }
+  bool operator>=(Time other) const {
+    return us_ >= other.us_;
+  }
+
+ private:
+  friend class TimeDelta;
+
+  // Platform-dependent wall clock interface
+  static int64 CurrentWallclockMicroseconds();
+
+  // Initialize or resynchronize the clock.
+  static void InitializeClock();
+
+  // Explodes the given time to either local time |is_local = true| or UTC
+  // |is_local = false|.
+  void Explode(bool is_local, Exploded* exploded) const;
+
+  // Unexplodes a given time assuming the source is either local time
+  // |is_local = true| or UTC |is_local = false|.
+  static Time FromExploded(bool is_local, const Exploded& exploded);
+
+  Time(int64 us) : us_(us) {
+  }
+
+  // The representation of Jan 1, 1970 UTC in microseconds since the
+  // platform-dependent epoch.
+  static const int64 kTimeTToMicrosecondsOffset;
+
+  // Time in microseconds in UTC.
+  int64 us_;
+
+  // The initial time sampled via this API.
+  static int64 initial_time_;
+
+  // The initial clock counter sampled via this API.
+  static TimeTicks initial_ticks_;
+};
+
+inline Time TimeDelta::operator+(Time t) const {
+  return Time(t.us_ + delta_);
+}
+
+// TimeTicks ------------------------------------------------------------------
+
+class TimeTicks {
+ public:
+  TimeTicks() : ticks_(0) {
+  }
+
+  // Platform-dependent tick count representing "right now."
+  // The resolution of this clock is ~1-5ms.  Resolution varies depending
+  // on hardware/operating system configuration.
+  static TimeTicks Now();
+
+  // Returns a platform-dependent high-resolution tick count. IT IS BROKEN ON
+  // SOME HARDWARE and is designed to be used for profiling and perf testing
+  // only (see the impl for more information).
+  static TimeTicks UnreliableHighResNow();
+
+
+  // Returns true if this object has not been initialized.
+  bool is_null() const {
+    return ticks_ == 0;
+  }
+
+  TimeTicks& operator=(TimeTicks other) {
+    ticks_ = other.ticks_;
+    return *this;
+  }
+
+  // Compute the difference between two times.
+  TimeDelta operator-(TimeTicks other) const {
+    return TimeDelta(ticks_ - other.ticks_);
+  }
+
+  // Modify by some time delta.
+  TimeTicks& operator+=(TimeDelta delta) {
+    ticks_ += delta.delta_;
+    return *this;
+  }
+  TimeTicks& operator-=(TimeDelta delta) {
+    ticks_ -= delta.delta_;
+    return *this;
+  }
+
+  // Return a new TimeTicks modified by some delta.
+  TimeTicks operator+(TimeDelta delta) const {
+    return TimeTicks(ticks_ + delta.delta_);
+  }
+  TimeTicks operator-(TimeDelta delta) const {
+    return TimeTicks(ticks_ - delta.delta_);
+  }
+
+  // Comparison operators
+  bool operator==(TimeTicks other) const {
+    return ticks_ == other.ticks_;
+  }
+  bool operator!=(TimeTicks other) const {
+    return ticks_ != other.ticks_;
+  }
+  bool operator<(TimeTicks other) const {
+    return ticks_ < other.ticks_;
+  }
+  bool operator<=(TimeTicks other) const {
+    return ticks_ <= other.ticks_;
+  }
+  bool operator>(TimeTicks other) const {
+    return ticks_ > other.ticks_;
+  }
+  bool operator>=(TimeTicks other) const {
+    return ticks_ >= other.ticks_;
+  }
+
+ protected:
+  friend class TimeDelta;
+  friend class PageLoadTrackerUnitTest;
+
+  // Please use Now() to create a new object. This is for internal use
+  // and testing. Ticks is in microseconds.
+  explicit TimeTicks(int64 ticks) : ticks_(ticks) {
+  }
+
+  // Tick count in microseconds.
+  int64 ticks_;
+
+#ifdef WIN32
+  // The function to use for counting ticks.
+  typedef int (__stdcall *TickFunction)(void);
+  static TickFunction tick_function_;
+#endif
+};
+
+inline TimeTicks TimeDelta::operator+(TimeTicks t) const {
+  return TimeTicks(t.ticks_ + delta_);
+}
+
+#endif  // BASE_TIME_H__
diff --git a/base/time_unittest.cc b/base/time_unittest.cc
new file mode 100644
index 0000000..214ec50
--- /dev/null
+++ b/base/time_unittest.cc
@@ -0,0 +1,195 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <process.h>
+#include <time.h>
+
+#include "base/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Test conversions to/from time_t and exploding/unexploding.
+TEST(Time, TimeT) {
+  // C library time and exploded time.
+  time_t now_t_1 = time(NULL);
+  struct tm tms;
+  localtime_s(&tms, &now_t_1);
+
+  // Convert to ours.
+  Time our_time_1 = Time::FromTimeT(now_t_1);
+  Time::Exploded exploded;
+  our_time_1.LocalExplode(&exploded);
+
+  // This will test both our exploding and our time_t -> Time conversion.
+  EXPECT_EQ(tms.tm_year + 1900, exploded.year);
+  EXPECT_EQ(tms.tm_mon + 1, exploded.month);
+  EXPECT_EQ(tms.tm_mday, exploded.day_of_month);
+  EXPECT_EQ(tms.tm_hour, exploded.hour);
+  EXPECT_EQ(tms.tm_min, exploded.minute);
+  EXPECT_EQ(tms.tm_sec, exploded.second);
+
+  // Convert exploded back to the time struct.
+  Time our_time_2 = Time::FromLocalExploded(exploded);
+  EXPECT_TRUE(our_time_1 == our_time_2);
+
+  time_t now_t_2 = our_time_2.ToTimeT();
+  EXPECT_EQ(now_t_1, now_t_2);
+
+  // Conversions of 0 should stay 0.
+  EXPECT_EQ(0, Time().ToTimeT());
+  EXPECT_EQ(0, Time::FromTimeT(0).ToInternalValue());
+}
+
+TEST(Time, ZeroIsSymmetric) {
+  Time zeroTime(Time::FromTimeT(0));
+  EXPECT_EQ(0, zeroTime.ToTimeT());
+}
+
+TEST(Time, LocalExplode) {
+  Time a = Time::Now();
+  Time::Exploded exploded;
+  a.LocalExplode(&exploded);
+
+  Time b = Time::FromLocalExploded(exploded);
+
+  // The exploded structure doesn't have microseconds, so the result will be
+  // rounded to the nearest millisecond.
+  EXPECT_TRUE((a - b) < TimeDelta::FromMilliseconds(1));
+}
+
+TEST(Time, UTCExplode) {
+  Time a = Time::Now();
+  Time::Exploded exploded;
+  a.UTCExplode(&exploded);
+
+  Time b = Time::FromUTCExploded(exploded);
+  EXPECT_TRUE((a - b) < TimeDelta::FromMilliseconds(1));
+}
+
+TEST(TimeTicks, Deltas) {
+  TimeTicks ticks_start = TimeTicks::Now();
+  Sleep(10);
+  TimeTicks ticks_stop = TimeTicks::Now();
+  TimeDelta delta = ticks_stop - ticks_start;
+  EXPECT_GE(delta.InMilliseconds(), 10);
+  EXPECT_GE(delta.InMicroseconds(), 10000);
+  EXPECT_EQ(delta.InSeconds(), 0);
+}
+
+namespace {
+
+class MockTimeTicks : public TimeTicks {
+ public:
+  static int Ticker() {
+    return static_cast<int>(InterlockedIncrement(&ticker_));
+  }
+
+  static void InstallTicker() {
+    old_tick_function_ = tick_function_;
+    tick_function_ = reinterpret_cast<TickFunction>(&Ticker);
+    ticker_ = -5;
+  }
+
+  static void UninstallTicker() {
+    tick_function_ = old_tick_function_;
+  }
+
+ private:
+  static volatile LONG ticker_;
+  static TickFunction old_tick_function_;
+};
+
+volatile LONG MockTimeTicks::ticker_;
+MockTimeTicks::TickFunction MockTimeTicks::old_tick_function_;
+
+HANDLE g_rollover_test_start;
+
+unsigned __stdcall RolloverTestThreadMain(void* param) {
+  int64 counter = reinterpret_cast<int64>(param);
+  DWORD rv = WaitForSingleObject(g_rollover_test_start, INFINITE);
+  EXPECT_EQ(rv, WAIT_OBJECT_0);
+
+  TimeTicks last = TimeTicks::Now();
+  for (int index = 0; index < counter; index++) {
+    TimeTicks now = TimeTicks::Now();
+    int64 milliseconds = (now - last).InMilliseconds();
+    EXPECT_GT(milliseconds, 0);
+    EXPECT_LT(milliseconds, 250);
+    last = now;
+  }
+  return 0;
+}
+
+} // namespace
+
+TEST(TimeTicks, Rollover) {
+  // The internal counter rolls over at ~49days.  We'll use a mock
+  // timer to test this case.
+  // Basic test algorithm:
+  //   1) Set clock to rollover - N
+  //   2) Create N threads
+  //   3) Start the threads
+  //   4) Each thread loops through TimeTicks() N times
+  //   5) Each thread verifies integrity of result.
+
+  const int kThreads = 8;
+  // Use int64 so we can cast into a void* without a compiler warning.
+  const int64 kChecks = 10;
+
+  // It takes a lot of iterations to reproduce the bug!
+  // (See bug 1081395)
+  for (int loop = 0; loop < 4096; loop++) {
+    // Setup
+    MockTimeTicks::InstallTicker();
+    g_rollover_test_start = CreateEvent(0, TRUE, FALSE, 0);
+    HANDLE threads[kThreads];
+
+    for (int index = 0; index < kThreads; index++) {
+      void* argument = reinterpret_cast<void*>(kChecks);
+      unsigned thread_id;
+      threads[index] = reinterpret_cast<HANDLE>(
+        _beginthreadex(NULL, 0, RolloverTestThreadMain, argument, 0,
+          &thread_id));
+      EXPECT_NE((HANDLE)NULL, threads[index]);
+    }
+
+    // Start!
+    SetEvent(g_rollover_test_start);
+
+    // Wait for threads to finish
+    for (int index = 0; index < kThreads; index++) {
+      DWORD rv = WaitForSingleObject(threads[index], INFINITE);
+      EXPECT_EQ(rv, WAIT_OBJECT_0);
+    }
+
+    CloseHandle(g_rollover_test_start);
+
+    // Teardown
+    MockTimeTicks::UninstallTicker();
+  }
+}
diff --git a/base/timer.cc b/base/timer.cc
new file mode 100644
index 0000000..4db8b88
--- /dev/null
+++ b/base/timer.cc
@@ -0,0 +1,346 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/timer.h"
+#include <mmsystem.h>
+
+#include "base/atomic.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/task.h"
+
+// Note about hi-resolution timers.
+// This class would *like* to provide high resolution timers.  Windows timers
+// using SetTimer() have a 10ms granularity.  We have to use WM_TIMER as a
+// wakeup mechanism because the application can enter modal windows loops where
+// it is not running our MessageLoop; the only way to have our timers fire in
+// these cases is to post messages there.
+//
+// To provide sub-10ms timers, we process timers directly from our main
+// MessageLoop.  For the common case, timers will be processed there as the
+// message loop does its normal work.  However, we *also* set the system timer
+// so that WM_TIMER events fire.  This mops up the case of timers not being
+// able to work in modal message loops.  It is possible for the SetTimer to
+// pop and have no pending timers, because they could have already been
+// processed by the message loop itself.
+//
+// We use a single SetTimer corresponding to the timer that will expire
+// soonest.  As new timers are created and destroyed, we update SetTimer.
+// Getting a spurrious SetTimer event firing is benign, as we'll just be
+// processing an empty timer queue.
+
+static const wchar_t kWndClass[] = L"Chrome_TimerMessageWindow";
+
+static LRESULT CALLBACK MessageWndProc(HWND hwnd,
+                                       UINT message,
+                                       WPARAM wparam,
+                                       LPARAM lparam) {
+  if (message == WM_TIMER) {
+    // Timer not firing? Maybe you're suffering from a WM_PAINTstorm. Make sure
+    // any WM_PAINT handler you have calls BeginPaint and EndPaint to validate
+    // the invalid region, otherwise you will be flooded with paint messages
+    // that trump WM_TIMER when PeekMessage is called.
+    UINT_PTR timer_id = static_cast<UINT_PTR>(wparam);
+    TimerManager* tm = reinterpret_cast<TimerManager*>(timer_id);
+    return tm->MessageWndProc(hwnd, message, wparam, lparam);
+  }
+
+  return DefWindowProc(hwnd, message, wparam, lparam);
+}
+
+// static
+int32 Timer::timer_id_counter_ = 0;
+
+//-----------------------------------------------------------------------------
+// Timer
+
+Timer::Timer(int delay, Task* task, bool repeating)
+    : delay_(delay),
+      task_(task),
+      repeating_(repeating) {
+      timer_id_ = base::AtomicIncrement(&timer_id_counter_);
+  DCHECK(delay >= 0);
+  Reset();
+}
+
+void Timer::Reset() {
+  creation_time_ = Time::Now();
+  fire_time_ = creation_time_ + TimeDelta::FromMilliseconds(delay_);
+  DHISTOGRAM_COUNTS(L"Timer.Durations", delay_);
+}
+
+//-----------------------------------------------------------------------------
+// TimerPQueue
+
+void TimerPQueue::RemoveTimer(Timer* timer) {
+  const std::vector<Timer*>::iterator location =
+      find(c.begin(), c.end(), timer);
+  if (location != c.end()) {
+    c.erase(location);
+    make_heap(c.begin(), c.end(), TimerComparison());
+  }
+}
+
+bool TimerPQueue::ContainsTimer(const Timer* timer) const {
+  return find(c.begin(), c.end(), timer) != c.end();
+}
+
+//-----------------------------------------------------------------------------
+// TimerManager
+
+TimerManager::TimerManager()
+    : use_broken_delay_(false),
+      message_hwnd_(NULL),
+      use_native_timers_(true),
+      message_loop_(NULL) {
+  // We've experimented with all sorts of timers, and initially tried
+  // to avoid using timeBeginPeriod because it does affect the system
+  // globally.  However, after much investigation, it turns out that all
+  // of the major plugins (flash, windows media 9-11, and quicktime)
+  // already use timeBeginPeriod to increase the speed of the clock.
+  // Since the browser must work with these plugins, the browser already
+  // needs to support a fast clock.  We may as well use this ourselves,
+  // as it really is the best timer mechanism for our needs.
+  timeBeginPeriod(1);
+
+  // Initialize the Message HWND in the constructor so that the window
+  // belongs to the same thread as the message loop (this is important!)
+  GetMessageHWND();
+}
+
+TimerManager::~TimerManager() {
+  // Match timeBeginPeriod() from construction.
+  timeEndPeriod(1);
+
+  if (message_hwnd_ != NULL)
+    DestroyWindow(message_hwnd_);
+
+  // Be nice to unit tests, and discard and delete all timers along with the
+  // embedded task objects by handing off to MessageLoop (which would have Run()
+  // and optionally deleted the objects).
+  while (timers_.size()) {
+    Timer* pending = timers_.top();
+    timers_.pop();
+    message_loop_->DiscardTimer(pending);
+  }
+}
+
+
+Timer* TimerManager::StartTimer(int delay, Task* task, bool repeating) {
+  Timer* t = new Timer(delay, task, repeating);
+  StartTimer(t);
+  return t;
+}
+
+void TimerManager::StopTimer(Timer* timer) {
+  // Make sure the timer is actually running.
+  if (!IsTimerRunning(timer))
+    return;
+  // Kill the active timer, and remove the pending entry from the queue.
+  if (timer != timers_.top()) {
+    timers_.RemoveTimer(timer);
+  } else {
+    timers_.pop();
+    UpdateWindowsWmTimer();  // We took away the head of our queue.
+  }
+}
+
+void TimerManager::ResetTimer(Timer* timer) {
+  StopTimer(timer);
+  timer->Reset();
+  StartTimer(timer);
+}
+
+bool TimerManager::IsTimerRunning(const Timer* timer) const {
+  return timers_.ContainsTimer(timer);
+}
+
+Timer* TimerManager::PeekTopTimer() {
+  if (timers_.empty())
+    return NULL;
+  return timers_.top();
+}
+
+bool TimerManager::RunSomePendingTimers() {
+  bool did_work = false;
+  bool allowed_to_run = message_loop()->NestableTasksAllowed();
+  // Process a small group of timers.  Cap the maximum number of timers we can
+  // process so we don't deny cycles to other parts of the process when lots of
+  // timers have been set.
+  const int kMaxTimersPerCall = 2;
+  for (int i = 0; i < kMaxTimersPerCall; ++i) {
+    if (timers_.empty() || GetCurrentDelay() > 0)
+      break;
+
+    // Get a pending timer.  Deal with updating the timers_ queue and setting
+    // the TopTimer.  We'll execute the timer task only after the timer queue
+    // is back in a consistent state.
+    Timer* pending = timers_.top();
+    // If pending task isn't invoked_later, then it must be possible to run it
+    // now (i.e., current task needs to be reentrant).
+    // TODO(jar): We may block tasks that we can queue from being popped.
+    if (!message_loop()->NestableTasksAllowed() &&
+        !pending->task()->is_owned_by_message_loop())
+      break;
+
+    timers_.pop();
+    did_work = true;
+
+    // If the timer is repeating, add it back to the list of timers to process.
+    if (pending->repeating()) {
+      pending->Reset();
+      timers_.push(pending);
+    }
+
+    message_loop()->RunTimerTask(pending);
+  }
+
+  // Restart the WM_TIMER (if necessary).
+  if (did_work)
+    UpdateWindowsWmTimer();
+
+  return did_work;
+}
+
+// Note: Caller is required to call timer->Reset() before calling StartTimer().
+// TODO(jar): change API so that Reset() is called as part of StartTimer, making
+// the API a little less error prone.
+void TimerManager::StartTimer(Timer* timer) {
+  // Make sure the timer is not running.
+  if (IsTimerRunning(timer))
+    return;
+
+  timers_.push(timer);  // Priority queue will sort the timer into place.
+
+  if (timers_.top() == timer)
+    UpdateWindowsWmTimer();  // We are new head of queue.
+}
+
+void TimerManager::UpdateWindowsWmTimer() {
+  if (!use_native_timers_)
+    return;
+
+  if (timers_.empty()) {
+    KillTimer(GetMessageHWND(), reinterpret_cast<UINT_PTR>(this));
+    return;
+  }
+
+  int delay = GetCurrentDelay();
+  if (delay < USER_TIMER_MINIMUM)
+    delay = USER_TIMER_MINIMUM;
+
+  // Simulates malfunctioning, early firing timers. Pending tasks should
+  // only be invoked when the delay they specify has elapsed.
+  if (use_broken_delay_)
+    delay = 10;
+
+  // Create a WM_TIMER event that will wake us up to check for any pending
+  // timers (in case the message loop was otherwise starving us).
+  SetTimer(GetMessageHWND(), reinterpret_cast<UINT_PTR>(this), delay, NULL);
+}
+
+int TimerManager::GetCurrentDelay() {
+  if (timers_.empty())
+    return -1;
+  int delay = timers_.top()->current_delay();
+  if (delay < 0)
+    delay = 0;
+  return delay;
+}
+
+int TimerManager::MessageWndProc(HWND hwnd, UINT message, WPARAM wparam,
+                                 LPARAM lparam) {
+  DCHECK(!lparam);
+  DCHECK(this == message_loop()->timer_manager());
+  if (message_loop()->NestableTasksAllowed())
+    RunSomePendingTimers();
+  else
+    UpdateWindowsWmTimer();
+  return 0;
+}
+
+MessageLoop* TimerManager::message_loop() {
+  if (!message_loop_)
+    message_loop_ = MessageLoop::current();
+  DCHECK(message_loop_ == MessageLoop::current());
+  return message_loop_;
+}
+
+
+HWND TimerManager::GetMessageHWND() {
+  if (!message_hwnd_) {
+    HINSTANCE hinst = GetModuleHandle(NULL);
+
+    WNDCLASSEX wc = {0};
+    wc.cbSize = sizeof(wc);
+    wc.lpfnWndProc = ::MessageWndProc;
+    wc.hInstance = hinst;
+    wc.lpszClassName = kWndClass;
+    RegisterClassEx(&wc);
+
+    message_hwnd_ = CreateWindow(kWndClass, 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0,
+                                 hinst, 0);
+    DCHECK(message_hwnd_);
+  }
+  return message_hwnd_;
+}
+
+//-----------------------------------------------------------------------------
+// SimpleTimer
+
+SimpleTimer::SimpleTimer(TimeDelta delay, Task* task, bool repeating)
+    : timer_(static_cast<int>(delay.InMilliseconds()), task, repeating),
+      owns_task_(true) {
+}
+
+SimpleTimer::~SimpleTimer() {
+  Stop();
+
+  if (owns_task_)
+    delete timer_.task();
+}
+
+void SimpleTimer::Start() {
+  DCHECK(timer_.task());
+  timer_.Reset();
+  MessageLoop::current()->timer_manager()->StartTimer(&timer_);
+}
+
+void SimpleTimer::Stop() {
+  MessageLoop::current()->timer_manager()->StopTimer(&timer_);
+}
+
+bool SimpleTimer::IsRunning() const {
+  return MessageLoop::current()->timer_manager()->IsTimerRunning(&timer_);
+}
+
+void SimpleTimer::Reset() {
+  DCHECK(timer_.task());
+  MessageLoop::current()->timer_manager()->ResetTimer(&timer_);
+}
diff --git a/base/timer.h b/base/timer.h
new file mode 100644
index 0000000..39eb313
--- /dev/null
+++ b/base/timer.h
@@ -0,0 +1,320 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_TIMER_H_
+#define BASE_TIMER_H_
+
+#include <math.h>
+#include <windows.h>
+
+#include <queue>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/time.h"
+
+// Timer/TimerManager are objects designed to help setting timers.
+// Goals of TimerManager:
+// - have only one Windows system timer for all app timer functionality
+// - work around bugs with timers firing arbitrarily earlier than specified
+// - provide the ability to run timers even if the application is in a
+//   windows modal app loop.
+
+class TimerManager;
+class Task;
+class MessageLoop;
+
+class Timer {
+ public:
+  Timer(int delay, Task* task, bool repeating);
+
+  Task* task() const { return task_; }
+  void set_task(Task* task) { task_ = task; }
+
+  int current_delay() const {
+    // Be careful here.  Timers have a precision of microseconds,
+    // but this API is in milliseconds.  If there are 5.5ms left,
+    // should the delay be 5 or 6?  It should be 6 to avoid timers
+    // firing early.  Implement ceiling by adding 999us prior to
+    // conversion to ms.
+    double delay = ceil((fire_time_ - Time::Now()).InMillisecondsF());
+    return static_cast<int>(delay);
+  }
+
+  const Time &fire_time() const { return fire_time_; }
+
+  bool repeating() const { return repeating_; }
+
+  // Update (or fill in) creation_time_, and calculate future fire_time_ based
+  // on current time plus delay_.
+  void Reset();
+
+  int id() const { return timer_id_; }
+
+ protected:
+  // Protected (rather than private) so that we can access from unit tests.
+
+  // The time when the timer should fire.
+  Time fire_time_;
+
+ private:
+  // A sequence number for all allocated times (used to break ties when
+  // comparing times in the TimerManager, and assure FIFO execution sequence).
+  static int32 timer_id_counter_;
+
+  // The task that is run when this timer fires.
+  Task* task_;
+
+  // Timer delay in milliseconds.
+  int delay_;
+
+  // A monotonically increasing timer id.  Used
+  // for ordering two timers which have the same
+  // timestamp in a FIFO manner.
+  int timer_id_;
+
+  // Whether or not this timer repeats.
+  const bool repeating_;
+
+  // The tick count when the timer was "created". (i.e. when its current
+  // iteration started.)
+  Time creation_time_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(Timer);
+};
+
+class TimerComparison {
+ public:
+  bool operator() (const Timer* t1, const Timer* t2) const {
+    const Time& f1 = t1->fire_time();
+    const Time& f2 = t2->fire_time();
+    // If the two timers have the same delay, revert to using
+    // the timer_id to maintain FIFO ordering.
+    if (f1 == f2) {
+      // Gracefully handle wrap as we try to return (t1->id() > t2->id());
+      int delta = t1->id() - t2->id();
+      // Assuming the delta is smaller than 2**31, we'll always get the right
+      // answer (in terms of sign of delta).
+      return delta > 0;
+    }
+    return f1 > f2;
+  }
+};
+
+// Subclass priority_queue to provide convenient access to removal from this
+// list.
+//
+// Terminology: The "pending" timer is the timer at the top of the queue,
+//              i.e. the timer whose task needs to be Run next.
+class TimerPQueue : public std::priority_queue<Timer*,
+                                               std::vector<Timer*>,
+                                               TimerComparison> {
+ public:
+  // Removes |timer| from the queue.
+  void RemoveTimer(Timer* timer);
+
+  // Returns true if the queue contains |timer|.
+  bool ContainsTimer(const Timer* timer) const;
+};
+
+// There is one TimerManager per thread, owned by the MessageLoop.
+// Timers can either be fired directly by the MessageLoop, or by
+// SetTimer and a WM_TIMER message.  The advantage of the former
+// is that we can make timers fire significantly faster than the 10ms
+// granularity provided by SetTimer().  The advantage of SetTimer()
+// is that modal message loops which don't run our MessageLoop
+// code will still be able to process WM_TIMER messages.
+//
+// Note:  TimerManager is not thread safe.  You cannot set timers
+//        onto a thread other than your own.
+class TimerManager {
+ public:
+  TimerManager();
+  ~TimerManager();
+
+  // Create and start a new timer. |task| is owned by the caller, as is the
+  // timer object that is returned.
+  Timer* StartTimer(int delay, Task* task, bool repeating);
+
+  // Flag indicating whether the timer manager should use the OS
+  // timers or not.  Default is true.  MessageLoops which are not reliably
+  // called due to nested windows message loops should set this to
+  // true.
+  bool use_native_timers() { return use_native_timers_; }
+  void set_use_native_timers(bool value) { use_native_timers_ = value; }
+
+  // Starts a timer.  This is a no-op if the timer is already started.
+  void StartTimer(Timer* timer);
+
+  // Stop a timer.  This is a no-op if the timer is already stopped.
+  void StopTimer(Timer* timer);
+
+  // Reset an existing timer, which may or may not be currently in the queue of
+  // upcoming timers.  The timer's parameters are unchanged; it simply begins
+  // counting down again as if it was just created.
+  void ResetTimer(Timer* timer);
+
+  // Returns true if |timer| is in the queue of upcoming timers.
+  bool IsTimerRunning(const Timer* timer) const;
+
+  // Run some small number of timers.
+  // Returns true if it runs a task, false otherwise.
+  bool RunSomePendingTimers();
+
+  // The number of milliseconds remaining until the pending timer (top of the
+  // pqueue) needs to be fired. Returns -1 if no timers are pending.
+  int GetCurrentDelay();
+
+  // A handler for WM_TIMER messages.
+  // If a task is not running currently, it runs some timer tasks (if there are
+  // some ready to fire), otherwise it just updates the WM_TIMER to be called
+  // again (hopefully when it is allowed to run a task).
+  int MessageWndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
+
+  // Return cached copy of MessageLoop::current().
+  MessageLoop* message_loop();
+
+#ifdef UNIT_TEST
+  // For testing only, used to simulate broken early-firing WM_TIMER
+  // notifications by setting arbitrarily small delays in SetTimer.
+  void set_use_broken_delay(bool use_broken_delay) {
+    use_broken_delay_ = use_broken_delay;
+  }
+#endif
+
+ protected:
+  // Peek at the timer which will fire soonest.
+  Timer* PeekTopTimer();
+
+ private:
+  // Update our Windows WM_TIMER to match our most immediately pending timer.
+  void UpdateWindowsWmTimer();
+
+  // Retrieve the Message Window that handles WM_TIMER messages from the
+  // system.
+  HWND GetMessageHWND();
+
+  TimerPQueue timers_;
+
+  bool use_broken_delay_;
+
+  HWND message_hwnd_;
+
+  // Flag to enable/disable use of native timers.
+  bool use_native_timers_;
+
+  // A lazily cached copy of MessageLoop::current.
+  MessageLoop* message_loop_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(TimerManager);
+};
+
+// A simple wrapper for the Timer / TimerManager API.  This is a helper class.
+// Use OneShotTimer or RepeatingTimer instead.
+class SimpleTimer {
+ public:
+  // Stops the timer.
+  ~SimpleTimer();
+
+  // Call this method to explicitly start the timer.  This is a no-op if the
+  // timer is already running.
+  void Start();
+
+  // Call this method to explicitly stop the timer.  This is a no-op if the
+  // timer is not running.
+  void Stop();
+
+  // Returns true if the timer is running (i.e., not stopped).
+  bool IsRunning() const;
+
+  // Short-hand for calling Stop and then Start.
+  void Reset();
+
+  // Get/Set the task to be run when this timer expires.  NOTE: The caller of
+  // set_task must be careful to ensure that the old task is properly deleted.
+  Task* task() const { return timer_.task(); }
+  void set_task(Task* task) {
+    timer_.set_task(task);
+    owns_task_ = true;
+  }
+
+  // Sets the task, but marks it so it shouldn't be deleted by the SimpleTimer.
+  void set_unowned_task(Task* task) {
+    timer_.set_task(task);
+    owns_task_ = false;
+  }
+
+ protected:
+  SimpleTimer(TimeDelta delay, Task* task, bool repeating);
+
+ private:
+  Timer timer_;
+
+  // Whether we need to clean up the Task* object for this Timer when
+  // we are deallocated. Defaults to true.
+  bool owns_task_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(SimpleTimer);
+};
+
+// A simple, one-shot timer.  The task is run after the specified delay once
+// the Start method is called.  The task is deleted when the timer object is
+// destroyed.
+class OneShotTimer : public SimpleTimer {
+ public:
+  // The task must be set using set_task before calling Start.
+  explicit OneShotTimer(TimeDelta delay)
+      : SimpleTimer(delay, NULL, false) {
+  }
+  // If task is null, then it must be set using set_task before calling Start.
+  OneShotTimer(TimeDelta delay, Task* task)
+      : SimpleTimer(delay, task, false) {
+  }
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(OneShotTimer);
+};
+
+// A simple, repeating timer.  The task is run at the specified interval once
+// the Start method is called.  The task is deleted when the timer object is
+// destroyed.
+class RepeatingTimer : public SimpleTimer {
+ public:
+  // The task must be set using set_task before calling Start.
+  explicit RepeatingTimer(TimeDelta interval)
+      : SimpleTimer(interval, NULL, true) {
+  }
+  // If task is null, then it must be set using set_task before calling Start.
+  RepeatingTimer(TimeDelta interval, Task* task)
+      : SimpleTimer(interval, task, true) {
+  }
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(RepeatingTimer);
+};
+
+#endif  // BASE_TIMER_H_
diff --git a/base/timer_unittest.cc b/base/timer_unittest.cc
new file mode 100644
index 0000000..814836b
--- /dev/null
+++ b/base/timer_unittest.cc
@@ -0,0 +1,332 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/message_loop.h"
+#include "base/task.h"
+#include "base/timer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+  class TimerTest : public testing::Test {
+  };
+};
+
+// A base class timer task that sanity-checks timer functionality and counts
+// the number of times it has run.  Handles all message loop and memory
+// management issues.
+class TimerTask : public Task {
+ public:
+  // Runs all timers to completion.  This returns only after all timers have
+  // finished firing.
+  static void RunTimers();
+
+  // Creates a new timer.  If |repeating| is true, the timer will repeat 10
+  // times before terminating.
+  //
+  // All timers are managed on the message loop of the thread that calls this
+  // function the first time.
+  TimerTask(int delay, bool repeating);
+
+  virtual ~TimerTask();
+
+  int iterations() const { return iterations_; }
+  const Timer* timer() const { return timer_; }
+
+  // Resets the timer, if it exists.
+  void Reset();
+
+  // Task
+  virtual void Run();
+
+ protected:
+  // Shuts down the message loop if necessary.
+  static void QuitMessageLoop();
+
+ private:
+  static MessageLoop* message_loop() {
+    static MessageLoop* loop = MessageLoop::current();
+    return loop;
+  }
+
+  static int timer_count_;
+  static bool loop_running_;
+
+  bool timer_running_;
+  int delay_;
+  TimeTicks start_ticks_;
+  int iterations_;
+  Timer* timer_;
+};
+
+// static
+void TimerTask::RunTimers() {
+  if (timer_count_ && !loop_running_) {
+    loop_running_ = true;
+    message_loop()->Run();
+  }
+}
+
+TimerTask::TimerTask(int delay, bool repeating)
+    : timer_running_(false),
+      delay_(delay),
+      start_ticks_(TimeTicks::Now()),
+      iterations_(0),
+      timer_(NULL) {
+  Reset();  // This will just set up the variables to indicate we have a
+            // running timer.
+  timer_ = message_loop()->timer_manager()->StartTimer(delay, this, repeating);
+}
+
+TimerTask::~TimerTask() {
+  if (timer_) {
+    message_loop()->timer_manager()->StopTimer(timer_);
+    delete timer_;
+  }
+  if (timer_running_) {
+    timer_running_ = false;
+    if (--timer_count_ <= 0)
+      QuitMessageLoop();
+  }
+}
+
+void TimerTask::Reset() {
+  if (!timer_running_) {
+    timer_running_ = true;
+    ++timer_count_;
+  }
+  if (timer_) {
+    start_ticks_ = TimeTicks::Now();
+    message_loop()->timer_manager()->ResetTimer(timer_);
+  }
+}
+
+void TimerTask::Run() {
+  ++iterations_;
+
+  // Test that we fired on or after the delay, not before.
+  const TimeTicks ticks = TimeTicks::Now();
+  EXPECT_LE(delay_, (ticks - start_ticks_).InMilliseconds());
+  // Note: Add the delay rather than using the ticks recorded.
+  //       Repeating timers have already started ticking before
+  //       this callback; we pretend they started *now*, then
+  //       it might seem like they fire early, when they do not.
+  start_ticks_ += TimeDelta::FromMilliseconds(delay_);
+
+  // If we're done running, shut down the message loop.
+  if (timer_->repeating() && (iterations_ < 10))
+    return;  // Iterate 10 times before terminating.
+  message_loop()->timer_manager()->StopTimer(timer_);
+  timer_running_ = false;
+  if (--timer_count_ <= 0)
+    QuitMessageLoop();
+}
+
+// static
+void TimerTask::QuitMessageLoop() {
+  if (loop_running_) {
+    message_loop()->Quit();
+    loop_running_ = false;
+  }
+}
+
+int TimerTask::timer_count_ = 0;
+bool TimerTask::loop_running_ = false;
+
+// A task that deletes itself when run.
+class DeletingTask : public TimerTask {
+ public:
+  DeletingTask(int delay, bool repeating) : TimerTask(delay, repeating) { }
+
+  // Task
+  virtual void Run();
+};
+
+void DeletingTask::Run() {
+  delete this;
+
+  // Can't call TimerTask::Run() here, we've destroyed ourselves.
+}
+
+// A class that resets another TimerTask when run.
+class ResettingTask : public TimerTask {
+ public:
+  ResettingTask(int delay, bool repeating, TimerTask* task)
+      : TimerTask(delay, repeating),
+        task_(task) {
+  }
+
+  virtual void Run();
+
+ private:
+  TimerTask* task_;
+};
+
+void ResettingTask::Run() {
+  task_->Reset();
+
+  TimerTask::Run();
+}
+
+// A class that quits the message loop when run.
+class QuittingTask : public TimerTask {
+ public:
+  QuittingTask(int delay, bool repeating) : TimerTask(delay, repeating) { }
+
+  virtual void Run();
+};
+
+void QuittingTask::Run() {
+  QuitMessageLoop();
+
+  TimerTask::Run();
+}
+
+void RunTimerTest() {
+  // Make sure oneshot timers work correctly.
+  TimerTask task1(100, false);
+  TimerTask::RunTimers();
+  EXPECT_EQ(1, task1.iterations());
+
+  // Make sure repeating timers work correctly.
+  TimerTask task2(10, true);
+  TimerTask task3(100, true);
+  TimerTask::RunTimers();
+  EXPECT_EQ(10, task2.iterations());
+  EXPECT_EQ(10, task3.iterations());
+}
+
+TEST(TimerTest, TimerComparison) {
+  // Make sure TimerComparison sorts correctly.
+  const TimerTask task1(10, false);
+  const Timer* timer1 = task1.timer();
+  const TimerTask task2(200, false);
+  const Timer* timer2 = task2.timer();
+  TimerComparison comparison;
+  EXPECT_FALSE(comparison(timer1, timer2));
+  EXPECT_TRUE(comparison(timer2, timer1));
+}
+
+TEST(TimerTest, TimerCase) {
+  RunTimerTest();
+}
+
+TEST(TimerTest, BrokenTimerCase) {
+  // Simulate faulty early-firing timers. The tasks in RunTimerTest should
+  // nevertheless be invoked after their specified delays, regardless of when
+  // WM_TIMER fires.
+  TimerManager* manager = MessageLoop::current()->timer_manager();
+  manager->set_use_broken_delay(true);
+  RunTimerTest();
+  manager->set_use_broken_delay(false);
+}
+
+TEST(TimerTest, DeleteFromRun) {
+  // Make sure TimerManager correctly handles a Task that deletes itself when
+  // run.
+  DeletingTask* deleting_task1 = new DeletingTask(50, true);
+  TimerTask timer_task(150, false);
+  DeletingTask* deleting_task2 = new DeletingTask(250, true);
+  TimerTask::RunTimers();
+  EXPECT_EQ(1, timer_task.iterations());
+}
+
+TEST(TimerTest, Reset) {
+  // Make sure resetting a timer after it has fired works.
+  TimerTask timer_task1(250, false);
+  TimerTask timer_task2(100, true);
+  ResettingTask resetting_task1(600, false, &timer_task1);
+  TimerTask::RunTimers();
+  EXPECT_EQ(2, timer_task1.iterations());
+  EXPECT_EQ(10, timer_task2.iterations());
+
+  // Make sure resetting a timer before it has fired works.  This will reset
+  // two timers, then stop the message loop between when they should have
+  // finally fired.
+  TimerTask timer_task3(100, false);
+  TimerTask timer_task4(600, false);
+  ResettingTask resetting_task3(50, false, &timer_task3);
+  ResettingTask resetting_task4(50, false, &timer_task4);
+  QuittingTask quitting_task(300, false);
+  TimerTask::RunTimers();
+  EXPECT_EQ(1, timer_task3.iterations());
+  EXPECT_EQ(0, timer_task4.iterations());
+}
+
+TEST(TimerTest, FifoOrder) {
+  // Creating timers with the same timeout should
+  // always compare to result in FIFO ordering.
+
+  // Derive from the timer so that we can set it's fire time.
+  // We have to do this, because otherwise, it's possible for
+  // two timers, created back to back, to have different times,
+  // and in that case, we aren't really testing what we want
+  // to test!
+  class MockTimer : public Timer {
+   public:
+    MockTimer(int delay) : Timer(delay, NULL, false) {}
+    void set_fire_time(const Time& t) { fire_time_ = t; }
+  };
+
+  class MockTimerManager : public TimerManager {
+   public:
+    // Pops the most-recent to fire timer and returns its timer id.
+    // Returns -1 if there are no timers in the list.
+    int pop() {
+      int rv = -1;
+      Timer* top = PeekTopTimer();
+      if (top) {
+        rv = top->id();
+        StopTimer(top);
+        delete top;
+      }
+      return rv;
+    }
+  };
+
+  MockTimer t1(0);
+  MockTimer t2(0);
+  t2.set_fire_time(t1.fire_time());
+  TimerComparison comparison;
+  EXPECT_TRUE(comparison(&t2, &t1));
+
+  // Issue a tight loop of timers; most will have the
+  // same timestamp; some will not.  Either way, since
+  // all are created with delay(0), the second timer
+  // must always be greater than the first.  Then, pop
+  // all the timers and verify that it's a FIFO list.
+  MockTimerManager manager;
+  const int kNumTimers = 1024;
+  for (int i=0; i < kNumTimers; i++)
+    Timer* timer = manager.StartTimer(0, NULL, false);
+
+  int last_id = -1;
+  int new_id = 0;
+  while((new_id = manager.pop()) > 0)
+    EXPECT_GT(new_id, last_id);
+}
\ No newline at end of file
diff --git a/base/tracked.cc b/base/tracked.cc
new file mode 100644
index 0000000..59c26f6
--- /dev/null
+++ b/base/tracked.cc
@@ -0,0 +1,113 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/tracked.h"
+
+#include "base/string_util.h"
+#include "base/tracked_objects.h"
+
+namespace tracked_objects {
+
+//------------------------------------------------------------------------------
+void Location::Write(bool display_filename, bool display_function_name,
+                     std::string* output) const {
+  StringAppendF(output, "%s[%d] ",
+      display_filename ? file_name_ : "line",
+      line_number_);
+
+  if (display_function_name) {
+    WriteFunctionName(output);
+    output->push_back(' ');
+  }
+}
+
+void Location::WriteFunctionName(std::string* output) const {
+  // Translate "<" to "&lt;" for HTML safety.
+  // TODO(jar): Support ASCII or html for logging in ASCII.
+  for (const char *p = function_name_; *p; p++) {
+    switch (*p) {
+      case '<':
+        output->append("&lt;");
+        break;
+
+      case '>':
+        output->append("&gt;");
+        break;
+
+      default:
+        output->push_back(*p);
+        break;
+    }
+  }
+}
+
+//------------------------------------------------------------------------------
+
+#ifndef TRACK_ALL_TASK_OBJECTS
+
+Tracked::Tracked() {}
+Tracked::~Tracked() {}
+void Tracked::SetBirthPlace(const Location& from_here) {}
+bool Tracked::MissingBirthplace() const { return false; }
+
+#else
+
+Tracked::Tracked() : tracked_births_(NULL), tracked_birth_time_(Time::Now()) {
+  if (!ThreadData::IsActive())
+    return;
+  SetBirthPlace(Location("NoFunctionName", "NeedToSetBirthPlace", -1));
+}
+
+Tracked::~Tracked() {
+  if (!ThreadData::IsActive() || !tracked_births_)
+    return;
+  ThreadData::current()->TallyADeath(*tracked_births_,
+                                     Time::Now() - tracked_birth_time_);
+}
+
+void Tracked::SetBirthPlace(const Location& from_here) {
+  if (!ThreadData::IsActive())
+    return;
+  if (tracked_births_)
+    tracked_births_->ForgetBirth();
+  ThreadData* current_thread_data = ThreadData::current();
+  if (!current_thread_data)
+    return;  // Shutdown started, and this thread wasn't registered.
+  tracked_births_ = current_thread_data->FindLifetime(from_here);
+  tracked_births_->RecordBirth();
+}
+
+bool Tracked::MissingBirthplace() const {
+  return -1 == tracked_births_->location().line_number();
+}
+
+#endif  // NDEBUG
+
+}  // namespace tracked_objects
+
diff --git a/base/tracked.h b/base/tracked.h
new file mode 100644
index 0000000..1b5d22a
--- /dev/null
+++ b/base/tracked.h
@@ -0,0 +1,129 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//------------------------------------------------------------------------------
+// Tracked is the base class for all tracked objects.  During construction, it
+// registers the fact that an instance was created, and at destruction time, it
+// records that event.  The instance may be tagged with a name, which is refered
+// to as its Location.  The Location is a file and line number, most
+// typically indicated where the object was constructed.  In some cases, as the
+// object's significance is refined (for example, a Task object is augmented to
+// do additonal things), its Location may be redefined to that later location.
+
+// Tracking includes (for each instance) recording the birth thread, death
+// thread, and duration of life (from construction to destruction).  All this
+// data is accumulated and filtered for review at about:objects.
+
+#ifndef BASE_TRACKED_H__
+#define BASE_TRACKED_H__
+
+#include <string>
+
+#include "base/time.h"
+
+#ifndef NDEBUG
+#define TRACK_ALL_TASK_OBJECTS
+#endif
+
+namespace tracked_objects {
+
+//------------------------------------------------------------------------------
+// Location provides basic info where of an object was constructed, or was
+// significantly brought to life.
+
+class Location {
+ public:
+  // Constructor should be called with a long-lived char*, such as __FILE__.
+  // It assumes the provided value will persist as a global constant, and it
+  // will not make a copy of it.
+  Location(const char* function_name, const char* file_name, int line_number)
+      : function_name_(function_name),
+        file_name_(file_name),
+        line_number_(line_number) { }
+
+  // Provide a default constructor for easy of debugging.
+  Location()
+      : function_name_("Unknown"),
+        file_name_("Unknown"),
+        line_number_(-1) { }
+
+  // Comparison operator for insertion into a std::map<> hash tables.
+  // All we need is *some* (any) hashing distinction.  Strings should already
+  // be unique, so we don't bother with strcmp or such.
+  // Use line number as the primary key (because it is fast, and usually gets us
+  // a difference), and then pointers as secondary keys (just to get some
+  // distinctions).
+  bool operator < (const Location& other) const {
+    if (line_number_ != other.line_number_)
+      return line_number_ < other.line_number_;
+    if (file_name_ != other.file_name_)
+      return file_name_ != other.file_name_;
+    return function_name_ < other.function_name_;
+  }
+
+  const char* function_name() const { return function_name_; }
+  const char* file_name()     const { return file_name_; }
+  int line_number()           const { return line_number_; }
+
+  void Write(bool display_filename, bool display_function_name,
+           std::string* output) const;
+
+  // Write function_name_ in HTML with '<' and '>' properly encoded.
+  void WriteFunctionName(std::string* output) const;
+
+ private:
+  const char* const function_name_;
+  const char* const file_name_;
+  const int line_number_;
+};
+
+
+//------------------------------------------------------------------------------
+
+
+class Births;
+
+class Tracked {
+ public:
+  Tracked();
+  virtual ~Tracked();
+  void SetBirthPlace(const Location& from_here);
+
+  bool MissingBirthplace() const;
+
+ private:
+  Births* tracked_births_;  // At same birthplace, and same thread.
+  const Time tracked_birth_time_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(Tracked);
+};
+
+}  // namespace tracked_objects
+
+#endif  // BASE_TRACKED_H__
diff --git a/base/tracked_objects.cc b/base/tracked_objects.cc
new file mode 100644
index 0000000..61c3639
--- /dev/null
+++ b/base/tracked_objects.cc
@@ -0,0 +1,918 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/tracked_objects.h"
+
+#include "base/string_util.h"
+
+namespace tracked_objects {
+
+// a TLS index to the TrackRegistry for the current thread.
+// static
+TLSSlot ThreadData::tls_index_ = -1;
+
+//------------------------------------------------------------------------------
+// Death data tallies durations when a death takes place.
+
+void DeathData::RecordDeath(const TimeDelta& duration) {
+  ++count_;
+  life_duration_ += duration;
+  int64 milliseconds = duration.InMilliseconds();
+  square_duration_ += milliseconds * milliseconds;
+}
+
+int DeathData::AverageMsDuration() const {
+  return static_cast<int>(life_duration_.InMilliseconds() / count_);
+}
+
+double DeathData::StandardDeviation() const {
+  double average = AverageMsDuration();
+  double variance = static_cast<float>(square_duration_)/count_
+                    - average * average;
+  return sqrt(variance);
+}
+
+
+void DeathData::AddDeathData(const DeathData& other) {
+  count_ += other.count_;
+  life_duration_ += other.life_duration_;
+  square_duration_ += other.square_duration_;
+}
+
+void DeathData::Write(std::string* output) const {
+  if (!count_)
+    return;
+  if (1 == count_)
+    StringAppendF(output, "(1)Life in %dms ", count_, AverageMsDuration());
+  else
+    StringAppendF(output, "(%d)Lives %dms/life ", count_, AverageMsDuration());
+}
+
+void DeathData::Clear() {
+  count_ = 0;
+  life_duration_ = TimeDelta();
+  square_duration_ = 0;
+}
+
+//------------------------------------------------------------------------------
+
+BirthOnThread::BirthOnThread(const Location& location)
+    : location_(location),
+      birth_thread_(ThreadData::current()) { }
+
+//------------------------------------------------------------------------------
+Births::Births(const Location& location)
+    : BirthOnThread(location),
+      birth_count_(0) { }
+
+//------------------------------------------------------------------------------
+// ThreadData maintains the central data for all births and death.
+
+// static
+ThreadData* ThreadData::first_ = NULL;
+// static
+Lock ThreadData::list_lock_;
+
+// static
+ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED;
+
+ThreadData::ThreadData() :  message_loop_(MessageLoop::current()) {}
+
+// static
+ThreadData* ThreadData::current() {
+  if (-1 == tls_index_)
+    return NULL;  // not yet initialized.
+
+  ThreadData* registry =
+      static_cast<ThreadData*>(ThreadLocalStorage::Get(tls_index_));
+  if (!registry) {
+    // We have to create a new registry for ThreadData.
+    bool too_late_to_create = false;
+    {
+      registry = new ThreadData;
+      AutoLock lock(list_lock_);
+      // Use lock to insure we have most recent status.
+      if (!IsActive()) {
+        too_late_to_create = true;
+      } else {
+        // Use lock to insert into list.
+        registry->next_ = first_;
+        first_ = registry;
+      }
+    }  // Release lock.
+    if (too_late_to_create) {
+      delete registry;
+      registry = NULL;
+    } else {
+      ThreadLocalStorage::Set(tls_index_, registry);
+    }
+  }
+  return registry;
+}
+
+// Do mininimal fixups for searching function names.
+static std::string UnescapeQuery(const std::string& query) {
+  std::string result;
+  for (size_t i = 0; i < query.size(); i++) {
+    char next = query[i];
+    if ('%' == next && i + 2 < query.size()) {
+      std::string hex = query.substr(i + 1, 2);
+      char replacement = '\0';
+      // Only bother with "<", ">", and " ".
+      if (LowerCaseEqualsASCII(hex, "3c"))
+        replacement ='<';
+      else if (LowerCaseEqualsASCII(hex, "3e"))
+        replacement = '>';
+      else if (hex == "20")
+        replacement = ' ';
+      if (replacement) {
+        next = replacement;
+        i += 2;
+      }
+    }
+    result.push_back(next);
+  }
+  return result;
+}
+
+// static
+void ThreadData::WriteHTML(const std::string& query, std::string* output) {
+  if (!ThreadData::IsActive())
+    return;  // Not yet initialized.
+
+  DCHECK(ThreadData::current());
+
+  output->append("<html><head><title>About Objects");
+  std::string escaped_query = UnescapeQuery(query);
+  if (!escaped_query.empty())
+    output->append(" - " + escaped_query);
+  output->append("</title></head><body><pre>");
+
+  DataCollector collected_data;  // Gather data.
+  collected_data.AddListOfLivingObjects();  // Add births that are still alive.
+
+  // Data Gathering is complete. Now to sort/process/render.
+  DataCollector::Collection* collection = collected_data.collection();
+
+  // Create filtering and sort comparison object.
+  Comparator comparator;
+  bool display_details = comparator.ParseQuery(escaped_query);
+
+  // Filter out acceptable (matching) instances.
+  DataCollector::Collection match_array;
+  for (DataCollector::Collection::iterator it = collection->begin();
+       it != collection->end(); ++it) {
+    if (comparator.Acceptable(*it))
+      match_array.push_back(*it);
+  }
+
+  comparator.Sort(&match_array);
+
+  WriteHTMLTotalAndSubtotals(match_array, comparator, output);
+
+  comparator.Clear();  // Delete tiebreaker_ instances.
+
+  output->append("</pre></body></html>");
+}
+
+// static
+void ThreadData::WriteHTMLTotalAndSubtotals(
+    const DataCollector::Collection& match_array,
+    const Comparator& comparator,
+    std::string* output) {
+  if (!match_array.size()) {
+    output->append("There were no tracked matches.");
+  } else {
+    // Aggregate during printing
+    Aggregation totals;
+    for (size_t i = 0; i < match_array.size(); ++i) {
+      totals.AddDeathSnapshot(match_array[i]);
+    }
+    output->append("Aggregate Stats: ");
+    totals.Write(output);
+    output->append("<hr><hr>");
+
+    Aggregation subtotals;
+    for (size_t i = 0; i < match_array.size(); ++i) {
+      if (0 == i || !comparator.Equivalent(match_array[i - 1],
+                                           match_array[i])) {
+        // Print group's defining characteristics.
+        comparator.WriteSortGrouping(match_array[i], output);
+        output->append("<br><br>");
+      }
+      comparator.WriteSnapshot(match_array[i], output);
+      output->append("<br>");
+      subtotals.AddDeathSnapshot(match_array[i]);
+      if (i + 1 >= match_array.size() ||
+          !comparator.Equivalent(match_array[i],
+                                 match_array[i + 1])) {
+        // Print aggregate stats for the group.
+        output->append("<br>");
+        subtotals.Write(output);
+        output->append("<br><hr><br>");
+        subtotals.Clear();
+      }
+    }
+  }
+}
+
+Births* ThreadData::FindLifetime(const Location& location) {
+  if (!message_loop_)  // In case message loop wasn't yet around...
+    message_loop_ = MessageLoop::current();  // Find it now.
+
+  BirthMap::iterator it = birth_map_.find(location);
+  if (it != birth_map_.end())
+    return it->second;
+  Births* tracker = new Births(location);
+
+  // Lock since the map may get relocated now, and other threads sometimes
+  // snapshot it (but they lock before copying it).
+  AutoLock lock(lock_);
+  birth_map_[location] = tracker;
+  return tracker;
+}
+
+void ThreadData::TallyADeath(const Births& lifetimes,
+                             const TimeDelta& duration) {
+  if (!message_loop_)  // In case message loop wasn't yet around...
+    message_loop_ = MessageLoop::current();  // Find it now.
+
+  DeathMap::iterator it = death_map_.find(&lifetimes);
+  if (it != death_map_.end()) {
+    it->second.RecordDeath(duration);
+    return;
+  }
+
+  AutoLock lock(lock_);  // Lock since the map may get relocated now.
+  death_map_[&lifetimes].RecordDeath(duration);
+}
+
+// static
+ThreadData* ThreadData::first() {
+  AutoLock lock(list_lock_);
+  return first_;
+}
+
+const std::string ThreadData::ThreadName() const {
+  if (message_loop_)
+    return message_loop_->thread_name();
+  return "ThreadWithoutMessageLoop";
+}
+
+// This may be called from another thread.
+void ThreadData::SnapshotBirthMap(BirthMap *output) const {
+  AutoLock lock(*const_cast<Lock*>(&lock_));
+  for (BirthMap::const_iterator it = birth_map_.begin();
+       it != birth_map_.end(); ++it)
+    (*output)[it->first] = it->second;
+}
+
+// This may be called from another thread.
+void ThreadData::SnapshotDeathMap(DeathMap *output) const {
+  AutoLock lock(*const_cast<Lock*>(&lock_));
+  for (DeathMap::const_iterator it = death_map_.begin();
+       it != death_map_.end(); ++it)
+    (*output)[it->first] = it->second;
+}
+
+void ThreadData::RunOnAllThreads(void (*function)()) {
+  ThreadData* list = first();  // Get existing list.
+
+  std::vector<MessageLoop*> message_loops;
+  for (ThreadData* it = list; it; it = it->next()) {
+    if (current() != it && it->message_loop())
+      message_loops.push_back(it->message_loop());
+  }
+
+  ThreadSafeDownCounter* counter =
+    new ThreadSafeDownCounter(message_loops.size() + 1);  // Extra one for us!
+
+  HANDLE completion_handle = CreateEvent(NULL, false, false, NULL);
+  // Tell all other threads to run.
+  for (size_t i = 0; i < message_loops.size(); ++i)
+    message_loops[i]->PostTask(FROM_HERE,
+        new RunTheStatic(function, completion_handle, counter));
+
+  // Also run Task on our thread.
+  RunTheStatic local_task(function, completion_handle, counter);
+  local_task.Run();
+
+  WaitForSingleObject(completion_handle, INFINITE);
+  int ret_val = CloseHandle(completion_handle);
+  DCHECK(ret_val);
+}
+
+// static
+bool ThreadData::StartTracking(bool status) {
+#ifndef TRACK_ALL_TASK_OBJECTS
+  return false;  // Not compiled in.
+#endif
+
+  if (!status) {
+    AutoLock lock(list_lock_);
+    DCHECK(status_ == ACTIVE || status_ == SHUTDOWN);
+    status_ = SHUTDOWN;
+    return true;
+  }
+  TLSSlot tls_index = ThreadLocalStorage::Alloc();
+  AutoLock lock(list_lock_);
+  DCHECK(status_ == UNINITIALIZED);
+  tls_index_ = tls_index;
+  CHECK(-1 != tls_index_);
+  status_ = ACTIVE;
+  return true;
+}
+
+// static
+bool ThreadData::IsActive() {
+  return status_ == ACTIVE;
+}
+
+// static
+void ThreadData::ShutdownMultiThreadTracking() {
+  // Using lock, guarantee that no new ThreadData instances will be created.
+  if (!StartTracking(false))
+    return;
+
+  RunOnAllThreads(ShutdownDisablingFurtherTracking);
+
+  // Now the *only* threads that might change the database are the threads with
+  // no messages loops.  They might still be adding data to their birth records,
+  // but since no objects are deleted on those threads, there will be no further
+  // access to to cross-thread data.
+  // We could do a cleanup on all threads except for the ones without
+  // MessageLoops, but we won't bother doing cleanup (destruction of data) yet.
+  return;
+}
+
+// static
+void ThreadData::ShutdownSingleThreadedCleanup() {
+  // We must be single threaded... but be careful anyway.
+  if (!StartTracking(false))
+    return;
+  ThreadData* thread_data_list;
+  {
+    AutoLock lock(list_lock_);
+    thread_data_list = first_;
+    first_ = NULL;
+  }
+
+  while (thread_data_list) {
+    ThreadData* next_thread_data = thread_data_list;
+    thread_data_list = thread_data_list->next();
+
+    for (BirthMap::iterator it = next_thread_data->birth_map_.begin();
+         next_thread_data->birth_map_.end() != it; ++it)
+      delete it->second;  // Delete the Birth Records.
+    next_thread_data->birth_map_.clear();
+    next_thread_data->death_map_.clear();
+    delete next_thread_data;  // Includes all Death Records.
+  }
+
+  CHECK(-1 != tls_index_);
+  ThreadLocalStorage::Free(tls_index_);
+  tls_index_ = -1;
+  status_ = UNINITIALIZED;
+}
+
+// static
+void ThreadData::ShutdownDisablingFurtherTracking() {
+  // Redundantly set status SHUTDOWN on this thread.
+  if (!StartTracking(false))
+    return;
+}
+
+
+//------------------------------------------------------------------------------
+
+ThreadData::ThreadSafeDownCounter::ThreadSafeDownCounter(size_t count)
+    : remaining_count_(count) {
+  DCHECK(remaining_count_ > 0);
+}
+
+bool ThreadData::ThreadSafeDownCounter::LastCaller() {
+  {
+    AutoLock lock(lock_);
+    if (--remaining_count_)
+      return false;
+  }  // Release lock, so we can delete everything in this instance.
+  delete this;
+  return true;
+}
+
+//------------------------------------------------------------------------------
+
+ThreadData::RunTheStatic::RunTheStatic(FunctionPointer function,
+                                       HANDLE completion_handle,
+                                       ThreadSafeDownCounter* counter)
+    : function_(function),
+      completion_handle_(completion_handle),
+      counter_(counter) {
+}
+
+void ThreadData::RunTheStatic::Run() {
+      function_();
+      if (counter_->LastCaller())
+        SetEvent(completion_handle_);
+    }
+
+
+//------------------------------------------------------------------------------
+// Individual 3-tuple of birth (place and thread) along with death thread, and
+// the accumulated stats for instances (DeathData).
+
+Snapshot::Snapshot(const BirthOnThread& birth_on_thread,
+                   const ThreadData& death_thread,
+                   const DeathData& death_data)
+    : birth_(&birth_on_thread),
+      death_thread_(&death_thread),
+      death_data_(death_data) {
+}
+
+Snapshot::Snapshot(const BirthOnThread& birth_on_thread, int count)
+    : birth_(&birth_on_thread),
+      death_thread_(NULL),
+      death_data_(DeathData(count)) {
+}
+
+const std::string Snapshot::DeathThreadName() const {
+  if (death_thread_)
+    return death_thread_->ThreadName();
+  return "Still_Alive";
+}
+
+void Snapshot::Write(std::string* output) const {
+  death_data_.Write(output);
+  StringAppendF(output, "%s->%s ",
+                birth_->birth_thread()->ThreadName().c_str(),
+                death_thread_->ThreadName().c_str());
+  birth_->location().Write(true, true, output);
+}
+
+void Snapshot::Add(const Snapshot& other) {
+  death_data_.AddDeathData(other.death_data_);
+}
+
+//------------------------------------------------------------------------------
+// DataCollector
+
+DataCollector::DataCollector() {
+  DCHECK(ThreadData::IsActive());
+
+  ThreadData* my_list = ThreadData::current()->first();
+
+  count_of_contributing_threads_ = 0;
+  for (ThreadData* thread_data = my_list;
+       thread_data;
+       thread_data = thread_data->next()) {
+    ++count_of_contributing_threads_;
+  }
+
+  // Gather data serially.  A different constructor could be used to do in
+  // parallel, and then invoke an OnCompletion task.
+  for (ThreadData* thread_data = my_list;
+       thread_data;
+       thread_data = thread_data->next()) {
+    Append(*thread_data);
+  }
+}
+
+void DataCollector::Append(const ThreadData& thread_data) {
+  // Get copy of data (which is done under ThreadData's lock).
+  ThreadData::BirthMap birth_map;
+  thread_data.SnapshotBirthMap(&birth_map);
+  ThreadData::DeathMap death_map;
+  thread_data.SnapshotDeathMap(&death_map);
+
+  // Use our lock to protect our accumulation activity.
+  AutoLock lock(accumulation_lock_);
+
+  DCHECK(count_of_contributing_threads_);
+
+  for (ThreadData::DeathMap::const_iterator it = death_map.begin();
+       it != death_map.end(); ++it) {
+    collection_.push_back(Snapshot(*it->first, thread_data, it->second));
+    global_birth_count_[it->first] -= it->first->birth_count();
+  }
+
+  for (ThreadData::BirthMap::const_iterator it = birth_map.begin();
+       it != birth_map.end(); ++it) {
+    global_birth_count_[it->second] += it->second->birth_count();
+  }
+
+  --count_of_contributing_threads_;
+}
+
+DataCollector::Collection* DataCollector::collection() {
+  DCHECK(!count_of_contributing_threads_);
+  return &collection_;
+}
+
+void DataCollector::AddListOfLivingObjects() {
+  DCHECK(!count_of_contributing_threads_);
+  for (BirthCount::iterator it = global_birth_count_.begin();
+       it != global_birth_count_.end(); ++it) {
+    if (it->second > 0)
+      collection_.push_back(Snapshot(*it->first, it->second));
+  }
+}
+
+//------------------------------------------------------------------------------
+// Aggregation
+
+void Aggregation::AddDeathSnapshot(const Snapshot& snapshot) {
+  AddBirth(snapshot.birth());
+  death_threads_[snapshot.death_thread()]++;
+  AddDeathData(snapshot.death_data());
+}
+
+void Aggregation::AddBirths(const Births& births) {
+  AddBirth(births);
+  birth_count_ += births.birth_count();
+}
+void Aggregation::AddBirth(const BirthOnThread& birth) {
+  AddBirthPlace(birth.location());
+  birth_threads_[birth.birth_thread()]++;
+}
+
+void Aggregation::AddBirthPlace(const Location& location) {
+  locations_[location]++;
+  birth_files_[location.file_name()]++;
+}
+
+void Aggregation::Write(std::string* output) const {
+  if (locations_.size() == 1) {
+    locations_.begin()->first.Write(true, true, output);
+  } else {
+    StringAppendF(output, "%d Locations. ", locations_.size());
+    if (birth_files_.size() > 1)
+      StringAppendF(output, "%d Files. ", birth_files_.size());
+    else
+      StringAppendF(output, "All born in %s. ",
+                    birth_files_.begin()->first.c_str());
+  }
+
+  if (birth_threads_.size() > 1)
+    StringAppendF(output, "%d BirthingThreads. ", birth_threads_.size());
+  else
+    StringAppendF(output, "All born on %s. ",
+                  birth_threads_.begin()->first->ThreadName().c_str());
+
+  if (death_threads_.size() > 1) {
+    StringAppendF(output, "%d DeathThreads. ", death_threads_.size());
+  } else {
+    if (death_threads_.begin()->first)
+      StringAppendF(output, "All deleted on %s. ",
+                  death_threads_.begin()->first->ThreadName().c_str());
+    else
+      output->append("All these objects are still alive.");
+  }
+
+  if (birth_count_ > 1)
+    StringAppendF(output, "Births=%d ", birth_count_);
+
+  DeathData::Write(output);
+}
+
+void Aggregation::Clear() {
+  birth_count_ = 0;
+  birth_files_.clear();
+  locations_.clear();
+  birth_threads_.clear();
+  DeathData::Clear();
+  death_threads_.clear();
+}
+
+//------------------------------------------------------------------------------
+// Comparison object for sorting.
+
+Comparator::Comparator()
+    : selector_(NIL),
+      tiebreaker_(NULL),
+      combined_selectors_(0),
+      use_tiebreaker_for_sort_only_(false) {}
+
+void Comparator::Clear() {
+  if (tiebreaker_) {
+    tiebreaker_->Clear();
+    delete tiebreaker_;
+    tiebreaker_ = NULL;
+  }
+  use_tiebreaker_for_sort_only_ = false;
+  selector_ = NIL;
+}
+
+void Comparator::Sort(DataCollector::Collection* collection) const {
+  std::sort(collection->begin(), collection->end(), *this);
+}
+
+
+bool Comparator::operator()(const Snapshot& left,
+                            const Snapshot& right) const {
+  switch (selector_) {
+    case BIRTH_THREAD:
+      if (left.birth_thread() != right.birth_thread() &&
+          left.birth_thread()->ThreadName() !=
+          right.birth_thread()->ThreadName())
+        return left.birth_thread()->ThreadName() <
+            right.birth_thread()->ThreadName();
+      break;
+
+    case DEATH_THREAD:
+      if (left.death_thread() != right.death_thread() &&
+          left.DeathThreadName() !=
+          right.DeathThreadName()) {
+        if (!left.death_thread())
+          return true;
+        if (!right.death_thread())
+          return false;
+        return left.DeathThreadName() <
+             right.DeathThreadName();
+      }
+      break;
+
+    case BIRTH_FILE:
+      if (left.location().file_name() != right.location().file_name()) {
+        int comp = strcmp(left.location().file_name(),
+                          right.location().file_name());
+        if (comp)
+          return 0 > comp;
+      }
+      break;
+
+    case BIRTH_FUNCTION:
+      if (left.location().function_name() != right.location().function_name()) {
+        int comp = strcmp(left.location().function_name(),
+                          right.location().function_name());
+        if (comp)
+          return 0 > comp;
+      }
+      break;
+
+    case BIRTH_LINE:
+      if (left.location().line_number() != right.location().line_number())
+        return left.location().line_number() <
+            right.location().line_number();
+      break;
+
+    case COUNT:
+      if (left.count() != right.count())
+        return left.count() > right.count();  // Sort large at front of vector.
+      break;
+
+    case AVERAGE_DURATION:
+      if (left.AverageMsDuration() != right.AverageMsDuration())
+        return left.AverageMsDuration() > right.AverageMsDuration();
+      break;
+  }
+  if (tiebreaker_)
+    return tiebreaker_->operator()(left, right);
+  return false;
+}
+
+bool Comparator::Equivalent(const Snapshot& left,
+                                const Snapshot& right) const {
+  switch (selector_) {
+    case BIRTH_THREAD:
+      if (left.birth_thread() != right.birth_thread() &&
+          left.birth_thread()->ThreadName() !=
+              right.birth_thread()->ThreadName())
+        return false;
+      break;
+
+    case DEATH_THREAD:
+      if (left.death_thread() != right.death_thread() &&
+          left.DeathThreadName() !=
+              right.DeathThreadName())
+        return false;
+      break;
+
+    case BIRTH_FILE:
+      if (left.location().file_name() != right.location().file_name()) {
+        int comp = strcmp(left.location().file_name(),
+                          right.location().file_name());
+        if (comp)
+          return false;
+      }
+      break;
+
+    case BIRTH_FUNCTION:
+      if (left.location().function_name() != right.location().function_name()) {
+        int comp = strcmp(left.location().function_name(),
+                          right.location().function_name());
+        if (comp)
+          return false;
+      }
+      break;
+
+    case COUNT:
+      if (left.count() != right.count())
+        return false;
+      break;
+
+    case AVERAGE_DURATION:
+      if (left.life_duration() != right.life_duration())
+        return false;
+      break;
+  }
+  if (tiebreaker_ && !use_tiebreaker_for_sort_only_)
+    return tiebreaker_->Equivalent(left, right);
+  return true;
+}
+
+bool Comparator::Acceptable(const Snapshot& sample) const {
+  if (required_.size()) {
+    switch (selector_) {
+      case BIRTH_THREAD:
+        if (sample.birth_thread()->ThreadName().find(required_)
+            == std::string.npos)
+          return false;
+        break;
+
+      case DEATH_THREAD:
+        if (sample.DeathThreadName().find(required_) == std::string.npos)
+          return false;
+        break;
+
+     case BIRTH_FILE:
+        if (!strstr(sample.location().file_name(), required_.c_str()))
+            return false;
+        break;
+
+     case BIRTH_FUNCTION:
+        if (!strstr(sample.location().function_name(), required_.c_str()))
+            return false;
+        break;
+    }
+  }
+  if (tiebreaker_ && !use_tiebreaker_for_sort_only_)
+    return tiebreaker_->Acceptable(sample);
+  return true;
+}
+
+void Comparator::SetTiebreaker(Selector selector, const std::string required) {
+  if (selector == selector_ || NIL == selector)
+    return;
+  combined_selectors_ |= selector;
+  if (NIL == selector_) {
+    selector_ = selector;
+    if (required.size())
+      required_ = required;
+    return;
+  }
+  if (tiebreaker_) {
+    if (use_tiebreaker_for_sort_only_) {
+      Comparator* temp = new Comparator;
+      temp->tiebreaker_ = tiebreaker_;
+      tiebreaker_ = temp;
+    }
+  } else {
+    tiebreaker_ = new Comparator;
+    DCHECK(!use_tiebreaker_for_sort_only_);
+  }
+  tiebreaker_->SetTiebreaker(selector, required);
+}
+
+bool Comparator::IsGroupedBy(Selector selector) const {
+  return 0 != (selector & combined_selectors_);
+}
+
+void Comparator::SetSubgroupTiebreaker(Selector selector) {
+  if (selector == selector_ || NIL == selector)
+    return;
+  if (!tiebreaker_) {
+    use_tiebreaker_for_sort_only_ = true;
+    tiebreaker_ = new Comparator;
+    tiebreaker_->SetTiebreaker(selector, "");
+  } else {
+    tiebreaker_->SetSubgroupTiebreaker(selector);
+  }
+}
+
+void Comparator::ParseKeyphrase(const std::string key_phrase) {
+  static std::map<const std::string, Selector> key_map;
+  static bool initialized = false;
+  if (!initialized) {
+    initialized = true;
+    key_map["count"]    = COUNT;
+    key_map["duration"] = AVERAGE_DURATION;
+    key_map["birth"]    = BIRTH_THREAD;
+    key_map["death"]    = DEATH_THREAD;
+    key_map["file"]     = BIRTH_FILE;
+    key_map["function"] = BIRTH_FUNCTION;
+    key_map["line"]     = BIRTH_LINE;
+  }
+
+  std::string required;
+  size_t equal_offset = key_phrase.find('=', 0);
+  if (key_phrase.npos != equal_offset)
+    required = key_phrase.substr(equal_offset + 1, key_phrase.npos);
+  std::string keyword(key_phrase.substr(0, equal_offset));
+  keyword = StringToLowerASCII(keyword);
+  if (key_map.end() == key_map.find(keyword))
+    return;
+  SetTiebreaker(key_map[keyword], required);
+}
+
+bool Comparator::ParseQuery(const std::string query) {
+  for (size_t i = 0; i < query.size();) {
+    size_t slash_offset = query.find('/', i);
+    ParseKeyphrase(query.substr(i, slash_offset - i));
+    if (query.npos == slash_offset)
+      break;
+    i = slash_offset + 1;
+  }
+
+  // Select subgroup ordering (if we want to display the subgroup)
+  SetSubgroupTiebreaker(COUNT);
+  SetSubgroupTiebreaker(AVERAGE_DURATION);
+  SetSubgroupTiebreaker(BIRTH_THREAD);
+  SetSubgroupTiebreaker(DEATH_THREAD);
+  SetSubgroupTiebreaker(BIRTH_FUNCTION);
+  SetSubgroupTiebreaker(BIRTH_FILE);
+  SetSubgroupTiebreaker(BIRTH_LINE);
+
+  return true;
+}
+
+bool Comparator::WriteSortGrouping(const Snapshot& sample,
+                                       std::string* output) const {
+  bool wrote_data = false;
+  switch (selector_) {
+    case NIL:
+      break;
+
+    case BIRTH_THREAD:
+      StringAppendF(output, "All new on %s ",
+                    sample.birth_thread()->ThreadName().c_str());
+      wrote_data = true;
+      break;
+
+    case DEATH_THREAD:
+      if (sample.death_thread())
+        StringAppendF(output, "All deleted on %s ",
+                      sample.DeathThreadName().c_str());
+      else
+        output->append("All still alive ");
+      wrote_data = true;
+      break;
+
+    case BIRTH_FILE:
+      StringAppendF(output, "All born in %s ",
+                    sample.location().file_name());
+      break;
+
+    case BIRTH_FUNCTION:
+      output->append("All born in ");
+      sample.location().WriteFunctionName(output);
+      output->push_back(' ');
+      break;
+  }
+  if (tiebreaker_ && !use_tiebreaker_for_sort_only_) {
+    wrote_data |= tiebreaker_->WriteSortGrouping(sample, output);
+  }
+  return wrote_data;
+}
+
+void Comparator::WriteSnapshot(const Snapshot& sample,
+                               std::string* output) const {
+  sample.death_data().Write(output);
+  if (!(combined_selectors_ & BIRTH_THREAD) ||
+      !(combined_selectors_ & DEATH_THREAD))
+    StringAppendF(output, "%s->%s ",
+                  (combined_selectors_ & BIRTH_THREAD) ? "*" :
+                    sample.birth().birth_thread()->ThreadName().c_str(),
+                  (combined_selectors_ & DEATH_THREAD) ? "*" :
+                    sample.DeathThreadName().c_str());
+  sample.birth().location().Write(!(combined_selectors_ & BIRTH_FILE),
+                                  !(combined_selectors_ & BIRTH_FUNCTION),
+                                  output);
+}
+
+}  // namespace tracked_objects
diff --git a/base/tracked_objects.h b/base/tracked_objects.h
new file mode 100644
index 0000000..23638e1
--- /dev/null
+++ b/base/tracked_objects.h
@@ -0,0 +1,509 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_TRACKED_OBJECTS_H_
+#define BASE_TRACKED_OBJECTS_H_
+
+//------------------------------------------------------------------------------
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/lock.h"
+#include "base/message_loop.h"
+#include "base/thread_local_storage.h"
+#include "base/tracked.h"
+
+
+namespace tracked_objects {
+
+//------------------------------------------------------------------------------
+// For a specific thread, and a specific birth place, the collection of all
+// death info (with tallies for each death thread, to prevent access conflicts).
+class ThreadData;
+class BirthOnThread {
+ public:
+  explicit BirthOnThread(const Location& location);
+
+  const Location location() const { return location_; }
+  const ThreadData* birth_thread() const { return birth_thread_; }
+
+ private:
+  // File/lineno of birth.  This defines the essence of the type, as the context
+  // of the birth (construction) often tell what the item is for.  This field
+  // is const, and hence safe to access from any thread.
+  const Location location_;
+
+  // The thread that records births into this object.  Only this thread is
+  // allowed to access birth_count_ (which changes over time).
+  const ThreadData* birth_thread_;  // The thread this birth took place on.
+
+  DISALLOW_EVIL_CONSTRUCTORS(BirthOnThread);
+};
+
+//------------------------------------------------------------------------------
+// A class for accumulating counts of births (without bothering with a map<>).
+
+class Births: public BirthOnThread {
+ public:
+  explicit Births(const Location& location);
+
+  int birth_count() const { return birth_count_; }
+
+  // When we have a birth we update the count for this BirhPLace.
+  void RecordBirth() { ++birth_count_; }
+
+  // When a birthplace is changed (updated), we need to decrement the counter
+  // for the old instance.
+  void ForgetBirth() { --birth_count_; }  // We corrected a birth place.
+
+ private:
+  // The number of births on this thread for our location_.
+  int birth_count_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(Births);
+};
+
+//------------------------------------------------------------------------------
+// Basic info summarizing multiple destructions of an object with a single
+// birthplace (fixed Location).  Used both on specific threads, and also used
+// in snapshots when integrating assembled data.
+
+class DeathData {
+ public:
+  // Default initializer.
+  DeathData() : count_(0), square_duration_(0) {}
+
+  // When deaths have not yet taken place, and we gather data from all the
+  // threads, we create DeathData stats that tally the number of births without
+  // a corrosponding death.
+  explicit DeathData(int count) : count_(count), square_duration_(0) {}
+
+  void RecordDeath(const TimeDelta& duration);
+
+  // Metrics accessors.
+  int count() const { return count_; }
+  TimeDelta life_duration() const { return life_duration_; }
+  int64 square_duration() const { return square_duration_; }
+  int AverageMsDuration() const;
+  double StandardDeviation() const;
+
+  // Accumulate metrics from other into this.
+  void AddDeathData(const DeathData& other);
+
+  // Simple print of internal state.
+  void Write(std::string* output) const;
+
+  void Clear();
+
+ private:
+  int count_;                // Number of destructions.
+  TimeDelta life_duration_;    // Sum of all lifetime durations.
+  int64 square_duration_;  // Sum of squares in milliseconds.
+};
+
+//------------------------------------------------------------------------------
+// A temporary collection of data that can be sorted and summarized.  It is
+// gathered (carefully) from many threads.  Instances are held in arrays and
+// processed, filtered, and rendered.
+// The source of this data was collected on many threads, and is asynchronously
+// changing.  The data in this instance is not asynchronously changing.
+
+class Snapshot {
+ public:
+  // When snapshotting a full life cycle set (birth-to-death), use this:
+  Snapshot(const BirthOnThread& birth_on_thread, const ThreadData& death_thread,
+           const DeathData& death_data);
+
+  // When snapshotting a birth, with no death yet, use this:
+  Snapshot(const BirthOnThread& birth_on_thread, int count);
+
+
+  const ThreadData* birth_thread() const { return birth_->birth_thread(); }
+  const Location location() const { return birth_->location(); }
+  const BirthOnThread& birth() const { return *birth_; }
+  const ThreadData* death_thread() const {return death_thread_; }
+  const DeathData& death_data() const { return death_data_; }
+  const std::string DeathThreadName() const;
+
+  int count() const { return death_data_.count(); }
+  TimeDelta life_duration() const { return death_data_.life_duration(); }
+  int64 square_duration() const { return death_data_.square_duration(); }
+  int AverageMsDuration() const { return death_data_.AverageMsDuration(); }
+
+  void Snapshot::Write(std::string* output) const;
+
+  void Add(const Snapshot& other);
+
+ private:
+  const BirthOnThread* birth_;  // Includes Location and birth_thread.
+  const ThreadData* death_thread_;
+  DeathData death_data_;
+};
+//------------------------------------------------------------------------------
+// DataCollector is a container class for Snapshot and BirthOnThread count
+// items.  It protects the gathering under locks, so that it could be called via
+// Posttask on any threads, such as all the target threads in parallel.
+
+class DataCollector {
+ public:
+  typedef std::vector<const Snapshot> Collection;
+
+  // Construct with a list of how many threads should contribute.  This helps us
+  // determine (in the async case) when we are done with all contributions.
+  DataCollector();
+
+  // Add all stats from the indicated thread into our arrays.  This function is
+  // mutex protected, and *could* be called from any threads (although current
+  // implementation serialized calls to Append).
+  void Append(const ThreadData& thread_data);
+
+  // After the accumulation phase, the following access is to process data.
+  Collection* collection();
+
+  // After collection of death data is complete, we can add entries for all the
+  // remaining living objects.
+  void AddListOfLivingObjects();
+
+ private:
+  // This instance may be provided to several threads to contribute data.  The
+  // following counter tracks how many more threads will contribute.  When it is
+  // zero, then all asynchronous contributions are complete, and locked access
+  // is no longer needed.
+  int count_of_contributing_threads_;
+
+  // The array that we collect data into.
+  Collection collection_;
+
+  // The total number of births recorded at each location for which we have not
+  // seen a death count.
+  typedef std::map<const BirthOnThread*, int> BirthCount;
+  BirthCount global_birth_count_;
+
+  Lock accumulation_lock_;  // Protects access during accumulation phase.
+
+  DISALLOW_EVIL_CONSTRUCTORS(DataCollector);
+};
+
+//------------------------------------------------------------------------------
+// Aggregation contains summaries (totals and subtotals) of groups of Snapshot
+// instances to provide printing of these collections on a single line.
+
+class Aggregation: public DeathData {
+ public:
+  Aggregation() : birth_count_(0) {}
+
+  void AddDeathSnapshot(const Snapshot& snapshot);
+  void AddBirths(const Births& births);
+  void AddBirth(const BirthOnThread& birth);
+  void AddBirthPlace(const Location& location);
+  void Write(std::string* output) const;
+  void Clear();
+
+ private:
+  int birth_count_;
+  std::map<std::string, int> birth_files_;
+  std::map<Location, int> locations_;
+  std::map<const ThreadData*, int> birth_threads_;
+  DeathData death_data_;
+  std::map<const ThreadData*, int> death_threads_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(Aggregation);
+};
+
+//------------------------------------------------------------------------------
+// Comparator does the comparison of Snapshot instances.  It is
+// used to order the instances in a vector.  It orders them into groups (for
+// aggregation), and can also order instances within the groups (for detailed
+// rendering of the instances).
+
+class Comparator {
+ public:
+  enum Selector {
+    NIL = 0,
+    BIRTH_THREAD = 1,
+    DEATH_THREAD = 2,
+    BIRTH_FILE = 4,
+    BIRTH_FUNCTION = 8,
+    BIRTH_LINE = 16,
+    COUNT = 32,
+    AVERAGE_DURATION = 64,
+    TOTAL_DURATION = 128,
+  };
+
+  explicit Comparator();
+
+  // Reset the comparator to a NIL selector.  Reset() and recursively delete any
+  // tiebreaker_ entries.  NOTE: We can't use a standard destructor, because
+  // the sort algorithm makes copies of this object, and then deletes them,
+  // which would cause problems (either we'd make expensive deep copies, or we'd
+  // do more thna one delete on a tiebreaker_.
+  void Clear();
+
+  // The less() operator for sorting the array via std::sort().
+  bool operator()(const Snapshot& left, const Snapshot& right) const;
+
+  void Sort(DataCollector::Collection* collection) const;
+
+  // Check to see if the items are sort equivalents (should be aggregated).
+  bool Equivalent(const Snapshot& left, const Snapshot& right) const;
+
+  // Check to see if all required fields are present in the given sample.
+  bool Acceptable(const Snapshot& sample) const;
+
+  // A comparator can be refined by specifying what to do if the selected basis
+  // for comparison is insufficient to establish an ordering.  This call adds
+  // the indicated attribute as the new "least significant" basis of comparison.
+  void SetTiebreaker(Selector selector, const std::string required);
+
+  // Indicate if this instance is set up to sort by the given Selector, thereby
+  // putting that information in the SortGrouping, so it is not needed in each
+  // printed line.
+  bool IsGroupedBy(Selector selector) const;
+
+  // Using the tiebreakers as set above, we mostly get an ordering, which
+  // equivalent groups.  If those groups are displayed (rather than just being
+  // aggregated, then the following is used to order them (within the group).
+  void SetSubgroupTiebreaker(Selector selector);
+
+  // Translate a keyword and restriction in URL path to a selector for sorting.
+  void ParseKeyphrase(const std::string key_phrase);
+
+  // Parse a query in an about:objects URL to decide on sort ordering.
+  bool ParseQuery(const std::string query);
+
+  // Output a header line that can be used to indicated what items will be
+  // collected in the group.  It lists all (potentially) tested attributes and
+  // their values (in the sample item).
+  bool WriteSortGrouping(const Snapshot& sample, std::string* output) const;
+
+  // Output a sample, with SortGroup details not displayed.
+  void WriteSnapshot(const Snapshot& sample, std::string* output) const;
+
+ private:
+  // The selector directs this instance to compare based on the specified
+  // members of the tested elements.
+  enum Selector selector_;
+
+  // For filtering into acceptable and unacceptable snapshot instance, the
+  // following is required to be a substring of the selector_ field.
+  std::string required_;
+
+  // If this instance can't decide on an ordering, we can consult a tie-breaker
+  // which may have a different basis of comparison.
+  Comparator* tiebreaker_;
+
+  // We or together all the selectors we sort on (not counting sub-group
+  // selectors), so that we can tell if we've decided to group on any given
+  // criteria.
+  int combined_selectors_;
+
+  // Some tiebreakrs are for subgroup ordering, and not for basic ordering (in
+  // preparation for aggregation).  The subgroup tiebreakers are not consulted
+  // when deciding if two items are in equivalent groups.  This flag tells us
+  // to ignore the tiebreaker when doing Equivalent() testing.
+  bool use_tiebreaker_for_sort_only_;
+};
+
+
+//------------------------------------------------------------------------------
+// For each thread, we have a ThreadData that stores all tracking info generated
+// on this thread.  This prevents the need for locking as data accumulates.
+
+class ThreadData {
+ public:
+  typedef std::map<Location, Births*> BirthMap;
+  typedef std::map<const Births*, DeathData> DeathMap;
+
+  ThreadData();
+
+  // Using Thread Local Store, find the current instance for collecting data.
+  // If an instance does not exist, construct one (and remember it for use on
+  // this thread.
+  // If shutdown has already started, and we don't yet have an instance, then
+  // return null.
+  static ThreadData* current();
+
+  // For a given about:objects URL, develop resulting HTML, and append to
+  // output.
+  static void WriteHTML(const std::string& query, std::string* output);
+
+  // For a given accumulated array of results, use the comparator to sort and
+  // subtotal, writing the results to the output.
+  static void WriteHTMLTotalAndSubtotals(
+      const DataCollector::Collection& match_array,
+      const Comparator& comparator, std::string* output);
+
+  // In this thread's data, find a place to record a new birth.
+  Births* FindLifetime(const Location& location);
+
+  // Find a place to record a death on this thread.
+  void TallyADeath(const Births& lifetimes, const TimeDelta& duration);
+
+  // (Thread safe) Get start of list of instances.
+  static ThreadData* first();
+  // Iterate through the null terminated list of instances.
+  ThreadData* next() const { return next_; }
+
+  MessageLoop* message_loop() const { return message_loop_; }
+  const std::string ThreadName() const;
+
+  // Using our lock, make a copy of the specified maps.  These calls may arrive
+  // from non-local threads.
+  void SnapshotBirthMap(BirthMap *output) const;
+  void SnapshotDeathMap(DeathMap *output) const;
+
+  static void RunOnAllThreads(void (*Func)());
+
+  // Set internal status_ to either become ACTIVE, or later, to be SHUTDOWN,
+  // based on argument being true or false respectively.
+  // IF tracking is not compiled in, this function will return false.
+  static bool StartTracking(bool status);
+  static bool IsActive();
+
+  // WARNING: ONLY call this function when all MessageLoops are still intact for
+  // all registered threads.  IF you call it later, you will crash.
+  // Note: You don't need to call it at all, and you can wait till you are
+  // single threaded (again) to do the cleanup via
+  // ShutdownSingleThreadedCleanup().
+  // Start the teardown (shutdown) process in a multi-thread mode by disabling
+  // further additions to thread database on all threads.  First it makes a
+  // local (locked) change to prevent any more threads from registering.  Then
+  // it Posts a Task to all registered threads to be sure they are aware that no
+  // more accumulation can take place.
+  static void ShutdownMultiThreadTracking();
+
+  // WARNING: ONLY call this function when you are running single threaded
+  // (again) and all message loops and threads have terminated.  Until that
+  // point some threads may still attempt to write into our data structures.
+  // Delete recursively all data structures, starting with the list of
+  // ThreadData instances.
+  static void ShutdownSingleThreadedCleanup();
+
+ private:
+  // Current allowable states of the tracking system.  The states always
+  // proceed towards SHUTDOWN, and never go backwards.
+  enum Status {
+    UNINITIALIZED,
+    ACTIVE,
+    SHUTDOWN,
+  };
+
+  // A class used to count down which is accessed by several threads.  This is
+  // used to make sure RunOnAllThreads() actually runs a task on the expected
+  // count of threads.
+  class ThreadSafeDownCounter {
+   public:
+    // Constructor sets the count, once and for all.
+    explicit ThreadSafeDownCounter(size_t count);
+
+    // Decrement the count, and return true if we hit zero.  Also delete this
+    // instance automatically when we hit zero.
+    bool LastCaller();
+
+   private:
+    size_t remaining_count_;
+    Lock lock_;  // protect access to remaining_count_.
+  };
+
+  // A Task class that runs a static method supplied, and checks to see if this
+  // is the last tasks instance (on last thread) that will run the method.
+  // IF this is the last run, then the supplied event is signalled.
+  class RunTheStatic : public Task {
+   public:
+    typedef void (*FunctionPointer)();
+    RunTheStatic(FunctionPointer function,
+                 HANDLE completion_handle,
+                 ThreadSafeDownCounter* counter);
+    // Run the supplied static method, and optionally set the event.
+    void Run();
+
+   private:
+    FunctionPointer function_;
+    HANDLE completion_handle_;
+    // Make sure enough tasks are called before completion is signaled.
+    ThreadSafeDownCounter* counter_;
+
+    DISALLOW_EVIL_CONSTRUCTORS(RunTheStatic);
+  };
+
+  // Each registered thread is called to set status_ to SHUTDOWN.
+  // This is done redundantly on every registered thread because it is not
+  // protected by a mutex.  Running on all threads guarantees we get the
+  // notification into the memory cache of all possible threads.
+  static void ShutdownDisablingFurtherTracking();
+
+  // We use thread local store to identify which ThreadData to interact with.
+  static TLSSlot tls_index_ ;
+
+  // Link to the most recently created instance (starts a null terminated list).
+  static ThreadData* first_;
+  // Protection for access to first_.
+  static Lock list_lock_;
+
+
+  // We set status_ to SHUTDOWN when we shut down the tracking service. This
+  // setting is redundantly established by all participating
+  // threads so that we are *guaranteed* (without locking) that all threads
+  // can "see" the status and avoid additional calls into the  service.
+  static Status status_;
+
+  // Link to next instance (null terminated list). Used to globally track all
+  // registered instances (corresponds to all registered threads where we keep
+  // data).
+  ThreadData* next_;
+
+  // The message loop where tasks needing to access this instance's private data
+  // should be directed.  Since some threads have no message loop, some
+  // instances have data that can't be (safely) modified externally.
+  MessageLoop* message_loop_;
+
+  // A map used on each thread to keep track of Births on this thread.
+  // This map should only be accessed on the thread it was constructed on.
+  // When a snapshot is needed, this structure can be locked in place for the
+  // duration of the snapshotting activity.
+  BirthMap birth_map_;
+
+  // Similar to birth_map_, this records informations about death of tracked
+  // instances (i.e., when a tracked instance was destroyed on this thread).
+  DeathMap death_map_;
+
+  // Lock to protect *some* access to BirthMap and DeathMap.  We only use
+  // locking protection when we are growing the maps, or using an iterator.  We
+  // only do writes to members from this thread, so the updates of values are
+  // atomic.  Folks can read from other threads, and get (via races) new or old
+  // data, but that is considered acceptable errors (mis-information).
+  Lock lock_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(ThreadData);
+};
+
+}  // namespace tracked_objects
+
+#endif  // BASE_TRACKED_OBJECTS_H_
diff --git a/base/tracked_objects_test.cc b/base/tracked_objects_test.cc
new file mode 100644
index 0000000..be30745
--- /dev/null
+++ b/base/tracked_objects_test.cc
@@ -0,0 +1,122 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Test of classes in the tracked_objects.h classes.
+
+#include "base/tracked_objects.h"
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace tracked_objects {
+
+class TrackedObjectsTest : public testing::Test {
+};
+
+TEST(TrackedObjectsTest, MinimalStartupShutdown) {
+  // Minimal test doesn't even create any tasks.
+  if (!ThreadData::StartTracking(true))
+    return;
+
+  EXPECT_FALSE(ThreadData::first());  // No activity even on this thread.
+  ThreadData* data = ThreadData::current();
+  EXPECT_TRUE(ThreadData::first());  // Now class was constructed.
+  EXPECT_TRUE(data);
+  EXPECT_TRUE(!data->next());
+  EXPECT_EQ(data, ThreadData::current());
+  ThreadData::BirthMap birth_map;
+  data->SnapshotBirthMap(&birth_map);
+  EXPECT_EQ(0u, birth_map.size());
+  ThreadData::DeathMap death_map;
+  data->SnapshotDeathMap(&death_map);
+  EXPECT_EQ(0u, death_map.size());
+  ThreadData::ShutdownSingleThreadedCleanup();
+
+  // Do it again, just to be sure we reset state completely.
+  ThreadData::StartTracking(true);
+  EXPECT_FALSE(ThreadData::first());  // No activity even on this thread.
+  data = ThreadData::current();
+  EXPECT_TRUE(ThreadData::first());  // Now class was constructed.
+  EXPECT_TRUE(data);
+  EXPECT_TRUE(!data->next());
+  EXPECT_EQ(data, ThreadData::current());
+  birth_map.clear();
+  data->SnapshotBirthMap(&birth_map);
+  EXPECT_EQ(0u, birth_map.size());
+  death_map.clear();
+  data->SnapshotDeathMap(&death_map);
+  EXPECT_EQ(0u, death_map.size());
+  ThreadData::ShutdownSingleThreadedCleanup();
+}
+
+class NoopTask : public Task {
+ public:
+  void Run() {}
+};
+
+TEST(TrackedObjectsTest, TinyStartupShutdown) {
+  if (!ThreadData::StartTracking(true))
+    return;
+
+  // Instigate tracking on a single task, or our thread.
+  NoopTask task;
+
+  const ThreadData* data = ThreadData::first();
+  EXPECT_TRUE(data);
+  EXPECT_TRUE(!data->next());
+  EXPECT_EQ(data, ThreadData::current());
+  ThreadData::BirthMap birth_map;
+  data->SnapshotBirthMap(&birth_map);
+  EXPECT_EQ(1u, birth_map.size());                         // 1 birth location.
+  EXPECT_EQ(1, birth_map.begin()->second->birth_count());  // 1 birth.
+  ThreadData::DeathMap death_map;
+  data->SnapshotDeathMap(&death_map);
+  EXPECT_EQ(0u, death_map.size());                         // No deaths.
+
+
+  // Now instigate a birth, and a death.
+  delete new NoopTask;
+
+  birth_map.clear();
+  data->SnapshotBirthMap(&birth_map);
+  EXPECT_EQ(1u, birth_map.size());                         // 1 birth location.
+  EXPECT_EQ(2, birth_map.begin()->second->birth_count());  // 2 births.
+  death_map.clear();
+  data->SnapshotDeathMap(&death_map);
+  EXPECT_EQ(1u, death_map.size());                         // 1 location.
+  EXPECT_EQ(1, death_map.begin()->second.count());         // 1 death.
+
+  // The births were at the same location as the one known death.
+  EXPECT_EQ(birth_map.begin()->second, death_map.begin()->first);
+
+  ThreadData::ShutdownSingleThreadedCleanup();
+}
+
+
+}  // namespace tracked_objects
+
diff --git a/base/tuple.h b/base/tuple.h
new file mode 100644
index 0000000..6222a41
--- /dev/null
+++ b/base/tuple.h
@@ -0,0 +1,687 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_TUPLE_H__
+#define BASE_TUPLE_H__
+
+// Traits ----------------------------------------------------------------------
+//
+// A simple traits class for tuple arguments.
+//
+// ValueType: the bare, nonref version of a type (same as the type for nonrefs).
+// RefType: the ref version of a type (same as the type for refs).
+// ParamType: what type to pass to functions (refs should not be constified).
+
+template <class P>
+struct TupleTraits {
+  typedef P ValueType;
+  typedef P& RefType;
+  typedef const P& ParamType;
+};
+
+template <class P>
+struct TupleTraits<P&> {
+  typedef P ValueType;
+  typedef P& RefType;
+  typedef P& ParamType;
+};
+
+// Tuple -----------------------------------------------------------------------
+//
+// This set of classes is useful for bundling 0 or more heterogeneous data types
+// into a single variable.  The advantage of this is that it greatly simplifies
+// function objects that need to take an arbitrary number of parameters; see
+// RunnableMethod and IPC::MessageWithTuple.
+//
+// Tuple0 is supplied to act as a 'void' type.  It can be used, for example, when
+// dispatching to a function that accepts no arguments (see the Dispatchers
+// below).
+// Tuple1<A> is rarely useful.  One such use is when A is non-const ref that you
+// want filled by the dispatchee, and the tuple is merely a container for that
+// output (a "tier").  See MakeRefTuple and its usages.
+
+struct Tuple0 {
+  typedef Tuple0 ValueTuple;
+  typedef Tuple0 RefTuple;
+};
+
+template <class A>
+struct Tuple1 {
+ public:
+  typedef A TypeA;
+  typedef Tuple1<typename TupleTraits<A>::ValueType> ValueTuple;
+  typedef Tuple1<typename TupleTraits<A>::RefType> RefTuple;
+
+  Tuple1() {}
+  explicit Tuple1(typename TupleTraits<A>::ParamType a) : a(a) {}
+
+  A a;
+};
+
+template <class A, class B>
+struct Tuple2 {
+ public:
+  typedef A TypeA;
+  typedef B TypeB;
+  typedef Tuple2<typename TupleTraits<A>::ValueType,
+                 typename TupleTraits<B>::ValueType> ValueTuple;
+  typedef Tuple2<typename TupleTraits<A>::RefType,
+                 typename TupleTraits<B>::RefType> RefTuple;
+
+  Tuple2() {}
+  Tuple2(typename TupleTraits<A>::ParamType a,
+         typename TupleTraits<B>::ParamType b)
+      : a(a), b(b) {
+  }
+
+  A a;
+  B b;
+};
+
+template <class A, class B, class C>
+struct Tuple3 {
+ public:
+  typedef A TypeA;
+  typedef B TypeB;
+  typedef C TypeC;
+  typedef Tuple3<typename TupleTraits<A>::ValueType,
+                 typename TupleTraits<B>::ValueType,
+                 typename TupleTraits<C>::ValueType> ValueTuple;
+  typedef Tuple3<typename TupleTraits<A>::RefType,
+                 typename TupleTraits<B>::RefType,
+                 typename TupleTraits<C>::RefType> RefTuple;
+
+  Tuple3() {}
+  Tuple3(typename TupleTraits<A>::ParamType a,
+         typename TupleTraits<B>::ParamType b,
+         typename TupleTraits<C>::ParamType c)
+      : a(a), b(b), c(c){
+  }
+
+  A a;
+  B b;
+  C c;
+};
+
+template <class A, class B, class C, class D>
+struct Tuple4 {
+ public:
+  typedef A TypeA;
+  typedef B TypeB;
+  typedef C TypeC;
+  typedef D TypeD;
+  typedef Tuple4<typename TupleTraits<A>::ValueType,
+                 typename TupleTraits<B>::ValueType,
+                 typename TupleTraits<C>::ValueType,
+                 typename TupleTraits<D>::ValueType> ValueTuple;
+  typedef Tuple4<typename TupleTraits<A>::RefType,
+                 typename TupleTraits<B>::RefType,
+                 typename TupleTraits<C>::RefType,
+                 typename TupleTraits<D>::RefType> RefTuple;
+
+  Tuple4() {}
+  Tuple4(typename TupleTraits<A>::ParamType a,
+         typename TupleTraits<B>::ParamType b,
+         typename TupleTraits<C>::ParamType c,
+         typename TupleTraits<D>::ParamType d)
+      : a(a), b(b), c(c), d(d) {
+  }
+
+  A a;
+  B b;
+  C c;
+  D d;
+};
+
+template <class A, class B, class C, class D, class E>
+struct Tuple5 {
+public:
+  typedef A TypeA;
+  typedef B TypeB;
+  typedef C TypeC;
+  typedef D TypeD;
+  typedef E TypeE;
+  typedef Tuple5<typename TupleTraits<A>::ValueType,
+    typename TupleTraits<B>::ValueType,
+    typename TupleTraits<C>::ValueType,
+    typename TupleTraits<D>::ValueType,
+    typename TupleTraits<E>::ValueType> ValueTuple;
+  typedef Tuple5<typename TupleTraits<A>::RefType,
+    typename TupleTraits<B>::RefType,
+    typename TupleTraits<C>::RefType,
+    typename TupleTraits<D>::RefType,
+    typename TupleTraits<E>::RefType> RefTuple;
+
+  Tuple5() {}
+  Tuple5(typename TupleTraits<A>::ParamType a,
+    typename TupleTraits<B>::ParamType b,
+    typename TupleTraits<C>::ParamType c,
+    typename TupleTraits<D>::ParamType d,
+    typename TupleTraits<E>::ParamType e)
+    : a(a), b(b), c(c), d(d), e(e) {
+  }
+
+  A a;
+  B b;
+  C c;
+  D d;
+  E e;
+};
+
+// Tuple creators -------------------------------------------------------------
+//
+// Helper functions for constructing tuples while inferring the template
+// argument types.
+
+inline Tuple0 MakeTuple() {
+  return Tuple0();
+}
+
+template <class A>
+inline Tuple1<A> MakeTuple(const A& a) {
+  return Tuple1<A>(a);
+}
+
+template <class A, class B>
+inline Tuple2<A, B> MakeTuple(const A& a, const B& b) {
+  return Tuple2<A, B>(a, b);
+}
+
+template <class A, class B, class C>
+inline Tuple3<A, B, C> MakeTuple(const A& a, const B& b, const C& c) {
+  return Tuple3<A, B, C>(a, b, c);
+}
+
+template <class A, class B, class C, class D>
+inline Tuple4<A, B, C, D> MakeTuple(const A& a, const B& b, const C& c,
+                                    const D& d) {
+  return Tuple4<A, B, C, D>(a, b, c, d);
+}
+
+template <class A, class B, class C, class D, class E>
+inline Tuple5<A, B, C, D, E> MakeTuple(const A& a, const B& b, const C& c,
+                                       const D& d, const E& e) {
+  return Tuple5<A, B, C, D, E>(a, b, c, d, e);
+}
+
+// The following set of helpers make what Boost refers to as "Tiers" - a tuple
+// of references.
+
+template <class A>
+inline Tuple1<A&> MakeRefTuple(A& a) {
+  return Tuple1<A&>(a);
+}
+
+template <class A, class B>
+inline Tuple2<A&, B&> MakeRefTuple(A& a, B& b) {
+  return Tuple2<A&, B&>(a, b);
+}
+
+template <class A, class B, class C>
+inline Tuple3<A&, B&, C&> MakeRefTuple(A& a, B& b, C& c) {
+  return Tuple3<A&, B&, C&>(a, b, c);
+}
+
+template <class A, class B, class C, class D>
+inline Tuple4<A&, B&, C&, D&> MakeRefTuple(A& a, B& b, C& c, D& d) {
+  return Tuple4<A&, B&, C&, D&>(a, b, c, d);
+}
+
+template <class A, class B, class C, class D, class E>
+inline Tuple5<A&, B&, C&, D&, E&> MakeRefTuple(A& a, B& b, C& c, D& d, E& e) {
+  return Tuple5<A&, B&, C&, D&, E&>(a, b, c, d, e);
+}
+
+// Dispatchers ----------------------------------------------------------------
+//
+// Helper functions that call the given method on an object, with the unpacked
+// tuple arguments.  Notice that they all have the same number of arguments,
+// so you need only write:
+//   DispatchToMethod(object, &Object::method, args);
+// This is very useful for templated dispatchers, since they don't need to know
+// what type |args| is.
+
+// Non-Static Dispatchers with no out params.
+
+template <class ObjT, class Method>
+inline void DispatchToMethod(ObjT* obj, Method method, const Tuple0& arg) {
+  (obj->*method)();
+}
+
+template <class ObjT, class Method, class A>
+inline void DispatchToMethod(ObjT* obj, Method method, const A& arg) {
+  (obj->*method)(arg);
+}
+
+template <class ObjT, class Method, class A>
+inline void DispatchToMethod(ObjT* obj, Method method, const Tuple1<A>& arg) {
+  (obj->*method)(arg.a);
+}
+
+template<class ObjT, class Method, class A, class B>
+inline void DispatchToMethod(ObjT* obj, Method method, const Tuple2<A, B>& arg) {
+  (obj->*method)(arg.a, arg.b);
+}
+
+template<class ObjT, class Method, class A, class B, class C>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple3<A, B, C>& arg) {
+  (obj->*method)(arg.a, arg.b, arg.c);
+}
+
+template<class ObjT, class Method, class A, class B, class C, class D>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple4<A, B, C, D>& arg) {
+  (obj->*method)(arg.a, arg.b, arg.c, arg.d);
+}
+
+template<class ObjT, class Method, class A, class B, class C, class D, class E>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple5<A, B, C, D, E>& arg) {
+  (obj->*method)(arg.a, arg.b, arg.c, arg.d, arg.e);
+}
+
+// Static Dispatchers with no out params.
+
+template <class Function>
+inline void DispatchToFunction(Function function, const Tuple0& arg) {
+  (*function)();
+}
+
+template <class Function, class A>
+inline void DispatchToFunction(Function function, const A& arg) {
+  (*function)(arg);
+}
+
+template <class Function, class A>
+inline void DispatchToFunction(Function function, const Tuple1<A>& arg) {
+  (*function)(arg.a);
+}
+
+template<class Function, class A, class B>
+inline void DispatchToFunction(Function function, const Tuple2<A, B>& arg) {
+  (*function)(arg.a, arg.b);
+}
+
+template<class Function, class A, class B, class C>
+inline void DispatchToFunction(Function function, const Tuple3<A, B, C>& arg) {
+  (*function)(arg.a, arg.b, arg.c);
+}
+
+template<class Function, class A, class B, class C, class D>
+inline void DispatchToFunction(Function function,
+                               const Tuple4<A, B, C, D>& arg) {
+  (*function)(arg.a, arg.b, arg.c, arg.d);
+}
+
+template<class Function, class A, class B, class C, class D, class E>
+inline void DispatchToFunction(Function function,
+                               const Tuple5<A, B, C, D, E>& arg) {
+  (*function)(arg.a, arg.b, arg.c, arg.d, arg.e);
+}
+
+// Dispatchers with 0 out param (as a Tuple0).
+
+template <class ObjT, class Method>
+inline void DispatchToMethod(ObjT* obj, Method method, const Tuple0& arg, Tuple0*) {
+  (obj->*method)();
+}
+
+template <class ObjT, class Method, class A>
+inline void DispatchToMethod(ObjT* obj, Method method, const A& arg, Tuple0*) {
+  (obj->*method)(arg);
+}
+
+template <class ObjT, class Method, class A>
+inline void DispatchToMethod(ObjT* obj, Method method, const Tuple1<A>& arg, Tuple0*) {
+  (obj->*method)(arg.a);
+}
+
+template<class ObjT, class Method, class A, class B>
+inline void DispatchToMethod(ObjT* obj, Method method, const Tuple2<A, B>& arg, Tuple0*) {
+  (obj->*method)(arg.a, arg.b);
+}
+
+template<class ObjT, class Method, class A, class B, class C>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple3<A, B, C>& arg, Tuple0*) {
+  (obj->*method)(arg.a, arg.b, arg.c);
+}
+
+template<class ObjT, class Method, class A, class B, class C, class D>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple4<A, B, C, D>& arg, Tuple0*) {
+  (obj->*method)(arg.a, arg.b, arg.c, arg.d);
+}
+
+template<class ObjT, class Method, class A, class B, class C, class D, class E>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple5<A, B, C, D, E>& arg, Tuple0*) {
+  (obj->*method)(arg.a, arg.b, arg.c, arg.d, arg.e);
+}
+
+// Dispatchers with 1 out param.
+
+template<class ObjT, class Method,
+         class OutA>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple0& in,
+                             Tuple1<OutA>* out) {
+  (obj->*method)(&out->a);
+}
+
+template<class ObjT, class Method, class InA,
+         class OutA>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const InA& in,
+                             Tuple1<OutA>* out) {
+  (obj->*method)(in, &out->a);
+}
+
+template<class ObjT, class Method, class InA,
+         class OutA>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple1<InA>& in,
+                             Tuple1<OutA>* out) {
+  (obj->*method)(in.a, &out->a);
+}
+
+template<class ObjT, class Method, class InA, class InB,
+         class OutA>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple2<InA, InB>& in,
+                             Tuple1<OutA>* out) {
+  (obj->*method)(in.a, in.b, &out->a);
+}
+
+template<class ObjT, class Method, class InA, class InB, class InC,
+         class OutA>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple3<InA, InB, InC>& in,
+                             Tuple1<OutA>* out) {
+  (obj->*method)(in.a, in.b, in.c, &out->a);
+}
+
+template<class ObjT, class Method, class InA, class InB, class InC, class InD,
+         class OutA>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple4<InA, InB, InC, InD>& in,
+                             Tuple1<OutA>* out) {
+  (obj->*method)(in.a, in.b, in.c, in.d, &out->a);
+}
+
+template<class ObjT, class Method,
+         class InA, class InB, class InC, class InD, class InE,
+         class OutA>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple5<InA, InB, InC, InD, InE>& in,
+                             Tuple1<OutA>* out) {
+  (obj->*method)(in.a, in.b, in.c, in.d, in.e, &out->a);
+}
+
+// Dispatchers with 2 out params.
+
+template<class ObjT, class Method,
+         class OutA, class OutB>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple0& in,
+                             Tuple2<OutA, OutB>* out) {
+  (obj->*method)(&out->a, &out->b);
+}
+
+template<class ObjT, class Method, class InA,
+         class OutA, class OutB>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const InA& in,
+                             Tuple2<OutA, OutB>* out) {
+  (obj->*method)(in, &out->a, &out->b);
+}
+
+template<class ObjT, class Method, class InA,
+         class OutA, class OutB>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple1<InA>& in,
+                             Tuple2<OutA, OutB>* out) {
+  (obj->*method)(in.a, &out->a, &out->b);
+}
+
+template<class ObjT, class Method, class InA, class InB,
+         class OutA, class OutB>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple2<InA, InB>& in,
+                             Tuple2<OutA, OutB>* out) {
+  (obj->*method)(in.a, in.b, &out->a, &out->b);
+}
+
+template<class ObjT, class Method, class InA, class InB, class InC,
+         class OutA, class OutB>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple3<InA, InB, InC>& in,
+                             Tuple2<OutA, OutB>* out) {
+  (obj->*method)(in.a, in.b, in.c, &out->a, &out->b);
+}
+
+template<class ObjT, class Method, class InA, class InB, class InC, class InD,
+         class OutA, class OutB>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple4<InA, InB, InC, InD>& in,
+                             Tuple2<OutA, OutB>* out) {
+  (obj->*method)(in.a, in.b, in.c, in.d, &out->a, &out->b);
+}
+
+template<class ObjT, class Method,
+         class InA, class InB, class InC, class InD, class InE,
+         class OutA, class OutB>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple5<InA, InB, InC, InD, InE>& in,
+                             Tuple2<OutA, OutB>* out) {
+  (obj->*method)(in.a, in.b, in.c, in.d, in.e, &out->a, &out->b);
+}
+
+// Dispatchers with 3 out params.
+
+template<class ObjT, class Method,
+         class OutA, class OutB, class OutC>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple0& in,
+                             Tuple3<OutA, OutB, OutC>* out) {
+  (obj->*method)(&out->a, &out->b, &out->c);
+}
+
+template<class ObjT, class Method, class InA,
+         class OutA, class OutB, class OutC>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const InA& in,
+                             Tuple3<OutA, OutB, OutC>* out) {
+  (obj->*method)(in, &out->a, &out->b, &out->c);
+}
+
+template<class ObjT, class Method, class InA,
+         class OutA, class OutB, class OutC>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple1<InA>& in,
+                             Tuple3<OutA, OutB, OutC>* out) {
+  (obj->*method)(in.a, &out->a, &out->b, &out->c);
+}
+
+template<class ObjT, class Method, class InA, class InB,
+         class OutA, class OutB, class OutC>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple2<InA, InB>& in,
+                             Tuple3<OutA, OutB, OutC>* out) {
+  (obj->*method)(in.a, in.b, &out->a, &out->b, &out->c);
+}
+
+template<class ObjT, class Method, class InA, class InB, class InC,
+         class OutA, class OutB, class OutC>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple3<InA, InB, InC>& in,
+                             Tuple3<OutA, OutB, OutC>* out) {
+  (obj->*method)(in.a, in.b, in.c, &out->a, &out->b, &out->c);
+}
+
+template<class ObjT, class Method, class InA, class InB, class InC, class InD,
+         class OutA, class OutB, class OutC>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple4<InA, InB, InC, InD>& in,
+                             Tuple3<OutA, OutB, OutC>* out) {
+  (obj->*method)(in.a, in.b, in.c, in.d, &out->a, &out->b, &out->c);
+}
+
+template<class ObjT, class Method,
+         class InA, class InB, class InC, class InD, class InE,
+         class OutA, class OutB, class OutC>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple5<InA, InB, InC, InD, InE>& in,
+                             Tuple3<OutA, OutB, OutC>* out) {
+  (obj->*method)(in.a, in.b, in.c, in.d, in.e, &out->a, &out->b, &out->c);
+}
+
+// Dispatchers with 4 out params.
+
+template<class ObjT, class Method,
+         class OutA, class OutB, class OutC, class OutD>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple0& in,
+                             Tuple4<OutA, OutB, OutC, OutD>* out) {
+  (obj->*method)(&out->a, &out->b, &out->c, &out->d);
+}
+
+template<class ObjT, class Method, class InA,
+         class OutA, class OutB, class OutC, class OutD>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const InA& in,
+                             Tuple4<OutA, OutB, OutC, OutD>* out) {
+  (obj->*method)(in, &out->a, &out->b, &out->c, &out->d);
+}
+
+template<class ObjT, class Method, class InA,
+         class OutA, class OutB, class OutC, class OutD>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple1<InA>& in,
+                             Tuple4<OutA, OutB, OutC, OutD>* out) {
+  (obj->*method)(in.a, &out->a, &out->b, &out->c, &out->d);
+}
+
+template<class ObjT, class Method, class InA, class InB,
+         class OutA, class OutB, class OutC, class OutD>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple2<InA, InB>& in,
+                             Tuple4<OutA, OutB, OutC, OutD>* out) {
+  (obj->*method)(in.a, in.b, &out->a, &out->b, &out->c, &out->d);
+}
+
+template<class ObjT, class Method, class InA, class InB, class InC,
+         class OutA, class OutB, class OutC, class OutD>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple3<InA, InB, InC>& in,
+                             Tuple4<OutA, OutB, OutC, OutD>* out) {
+  (obj->*method)(in.a, in.b, in.c, &out->a, &out->b, &out->c, &out->d);
+}
+
+template<class ObjT, class Method, class InA, class InB, class InC, class InD,
+         class OutA, class OutB, class OutC, class OutD>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple4<InA, InB, InC, InD>& in,
+                             Tuple4<OutA, OutB, OutC, OutD>* out) {
+  (obj->*method)(in.a, in.b, in.c, in.d, &out->a, &out->b, &out->c, &out->d);
+}
+
+template<class ObjT, class Method,
+         class InA, class InB, class InC, class InD, class InE,
+         class OutA, class OutB, class OutC, class OutD>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple5<InA, InB, InC, InD, InE>& in,
+                             Tuple4<OutA, OutB, OutC, OutD>* out) {
+  (obj->*method)(in.a, in.b, in.c, in.d, in.e,
+                 &out->a, &out->b, &out->c, &out->d);
+}
+
+// Dispatchers with 5 out params.
+
+template<class ObjT, class Method,
+         class OutA, class OutB, class OutC, class OutD, class OutE>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple0& in,
+                             Tuple5<OutA, OutB, OutC, OutD, OutE>* out) {
+  (obj->*method)(&out->a, &out->b, &out->c, &out->d, &out->e);
+}
+
+template<class ObjT, class Method, class InA,
+         class OutA, class OutB, class OutC, class OutD, class OutE>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const InA& in,
+                             Tuple5<OutA, OutB, OutC, OutD, OutE>* out) {
+  (obj->*method)(in, &out->a, &out->b, &out->c, &out->d, &out->e);
+}
+
+template<class ObjT, class Method, class InA,
+         class OutA, class OutB, class OutC, class OutD, class OutE>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple1<InA>& in,
+                             Tuple5<OutA, OutB, OutC, OutD, OutE>* out) {
+  (obj->*method)(in.a, &out->a, &out->b, &out->c, &out->d, &out->e);
+}
+
+template<class ObjT, class Method, class InA, class InB,
+         class OutA, class OutB, class OutC, class OutD, class OutE>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple2<InA, InB>& in,
+                             Tuple5<OutA, OutB, OutC, OutD, OutE>* out) {
+  (obj->*method)(in.a, in.b, &out->a, &out->b, &out->c, &out->d, &out->e);
+}
+
+template<class ObjT, class Method, class InA, class InB, class InC,
+         class OutA, class OutB, class OutC, class OutD, class OutE>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple3<InA, InB, InC>& in,
+                             Tuple5<OutA, OutB, OutC, OutD, OutE>* out) {
+  (obj->*method)(in.a, in.b, in.c, &out->a, &out->b, &out->c, &out->d, &out->e);
+}
+
+template<class ObjT, class Method, class InA, class InB, class InC, class InD,
+         class OutA, class OutB, class OutC, class OutD, class OutE>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple4<InA, InB, InC, InD>& in,
+                             Tuple5<OutA, OutB, OutC, OutD, OutE>* out) {
+  (obj->*method)(in.a, in.b, in.c, in.d, &out->a, &out->b, &out->c, &out->d,
+                 &out->e);
+}
+
+template<class ObjT, class Method,
+         class InA, class InB, class InC, class InD, class InE,
+         class OutA, class OutB, class OutC, class OutD, class OutE>
+inline void DispatchToMethod(ObjT* obj, Method method,
+                             const Tuple5<InA, InB, InC, InD, InE>& in,
+                             Tuple5<OutA, OutB, OutC, OutD, OutE>* out) {
+  (obj->*method)(in.a, in.b, in.c, in.d, in.e,
+                 &out->a, &out->b, &out->c, &out->d, &out->e);
+}
+
+#endif  // BASE_TUPLE_H__
diff --git a/base/values.cc b/base/values.cc
new file mode 100644
index 0000000..1cad628
--- /dev/null
+++ b/base/values.cc
@@ -0,0 +1,583 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/logging.h"
+#include "base/values.h"
+
+///////////////////// Value ////////////////////
+
+Value::~Value() {
+}
+
+// static
+Value* Value::CreateNullValue() {
+  return new Value(TYPE_NULL);
+}
+
+// static
+Value* Value::CreateBooleanValue(bool in_value) {
+  return new FundamentalValue(in_value);
+}
+
+// static
+Value* Value::CreateIntegerValue(int in_value) {
+  return new FundamentalValue(in_value);
+}
+
+// static
+Value* Value::CreateRealValue(double in_value) {
+  return new FundamentalValue(in_value);
+}
+
+// static
+Value* Value::CreateStringValue(const std::wstring& in_value) {
+  return new StringValue(in_value);
+}
+
+// static
+BinaryValue* Value::CreateBinaryValue(char* buffer, size_t size) {
+  return BinaryValue::Create(buffer, size);
+}
+
+bool Value::GetAsBoolean(bool* in_value) const {
+  return false;
+}
+
+bool Value::GetAsInteger(int* in_value) const {
+  return false;
+}
+
+bool Value::GetAsReal(double* in_value) const {
+  return false;
+}
+
+bool Value::GetAsString(std::wstring* in_value) const {
+  return false;
+}
+
+Value* Value::DeepCopy() const {
+  // This method should only be getting called for null Values--all subclasses
+  // need to provide their own implementation;.
+  DCHECK(IsType(TYPE_NULL));
+  return CreateNullValue();
+}
+
+bool Value::Equals(const Value* other) const {
+  // This method should only be getting called for null Values--all subclasses
+  // need to provide their own implementation;.
+  DCHECK(IsType(TYPE_NULL));
+  return other->IsType(TYPE_NULL);
+}
+
+///////////////////// FundamentalValue ////////////////////
+
+FundamentalValue::~FundamentalValue() {
+}
+
+bool FundamentalValue::GetAsBoolean(bool* out_value) const {
+  if (out_value && IsType(TYPE_BOOLEAN))
+    *out_value = boolean_value_;
+  return (IsType(TYPE_BOOLEAN));
+}
+
+bool FundamentalValue::GetAsInteger(int* out_value) const {
+  if (out_value && IsType(TYPE_INTEGER))
+    *out_value = integer_value_;
+  return (IsType(TYPE_INTEGER));
+}
+
+bool FundamentalValue::GetAsReal(double* out_value) const {
+  if (out_value && IsType(TYPE_REAL))
+    *out_value = real_value_;
+  return (IsType(TYPE_REAL));
+}
+
+Value* FundamentalValue::DeepCopy() const {
+  switch (GetType()) {
+    case TYPE_BOOLEAN:
+      return CreateBooleanValue(boolean_value_);
+
+    case TYPE_INTEGER:
+      return CreateIntegerValue(integer_value_);
+
+    case TYPE_REAL:
+      return CreateRealValue(real_value_);
+
+    default:
+      NOTREACHED();
+      return NULL;
+  }
+}
+
+bool FundamentalValue::Equals(const Value* other) const {
+  if (other->GetType() != GetType())
+    return false;
+
+  switch (GetType()) {
+    case TYPE_BOOLEAN: {
+      bool lhs, rhs;
+      return GetAsBoolean(&lhs) && other->GetAsBoolean(&rhs) && lhs == rhs;
+    }
+    case TYPE_INTEGER: {
+      int lhs, rhs;
+      return GetAsInteger(&lhs) && other->GetAsInteger(&rhs) && lhs == rhs;
+    }
+    case TYPE_REAL: {
+      double lhs, rhs;
+      return GetAsReal(&lhs) && other->GetAsReal(&rhs) && lhs == rhs;
+    }
+    default:
+      NOTREACHED();
+      return false;
+  }
+}
+
+///////////////////// StringValue ////////////////////
+
+StringValue::~StringValue() {
+}
+
+bool StringValue::GetAsString(std::wstring* out_value) const {
+  if (out_value)
+    *out_value = value_;
+  return true;
+}
+
+Value* StringValue::DeepCopy() const {
+  return CreateStringValue(value_);
+}
+
+bool StringValue::Equals(const Value* other) const {
+  if (other->GetType() != GetType())
+    return false;
+  std::wstring lhs, rhs;
+  return GetAsString(&lhs) && other->GetAsString(&rhs) && lhs == rhs;
+}
+
+///////////////////// BinaryValue ////////////////////
+
+//static
+BinaryValue* BinaryValue::Create(char* buffer, size_t size) {
+  if (!buffer)
+    return NULL;
+
+  return new BinaryValue(buffer, size);
+}
+
+//static
+BinaryValue* BinaryValue::CreateWithCopiedBuffer(char* buffer, size_t size) {
+  if (!buffer)
+    return NULL;
+
+  char* buffer_copy = new char[size];
+  memcpy_s(buffer_copy, size, buffer, size);
+  return new BinaryValue(buffer_copy, size);
+}
+
+
+BinaryValue::BinaryValue(char* buffer, size_t size)
+  : Value(TYPE_BINARY),
+    buffer_(buffer),
+    size_(size) {
+  DCHECK(buffer_);
+}
+
+BinaryValue::~BinaryValue() {
+  DCHECK(buffer_);
+  if (buffer_)
+    delete[] buffer_;
+}
+
+Value* BinaryValue::DeepCopy() const {
+  return CreateWithCopiedBuffer(buffer_, size_);
+}
+
+bool BinaryValue::Equals(const Value* other) const {
+  if (other->GetType() != GetType())
+    return false;
+  const BinaryValue* other_binary = static_cast<const BinaryValue*>(other);
+  if (other_binary->size_ != size_)
+    return false;
+  return !memcmp(buffer_, other_binary->buffer_, size_);
+}
+
+///////////////////// DictionaryValue ////////////////////
+
+DictionaryValue::~DictionaryValue() {
+  Clear();
+}
+
+void DictionaryValue::Clear() {
+  ValueMap::iterator dict_iterator = dictionary_.begin();
+  while (dict_iterator != dictionary_.end()) {
+    delete dict_iterator->second;
+    ++dict_iterator;
+  }
+
+  dictionary_.clear();
+}
+
+bool DictionaryValue::HasKey(const std::wstring& key) {
+  ValueMap::const_iterator current_entry = dictionary_.find(key);
+  DCHECK((current_entry == dictionary_.end()) || current_entry->second);
+  return current_entry != dictionary_.end();
+}
+
+void DictionaryValue::SetInCurrentNode(const std::wstring& key,
+                                       Value* in_value) {
+  // If there's an existing value here, we need to delete it, because
+  // we own all our children.
+  if (HasKey(key)) {
+    DCHECK(dictionary_[key] != in_value);  // This would be bogus
+    delete dictionary_[key];
+  }
+
+  dictionary_[key] = in_value;
+}
+
+bool DictionaryValue::Set(const std::wstring& path, Value* in_value) {
+  DCHECK(in_value);
+
+  std::wstring key = path;
+
+  size_t delimiter_position = path.find_first_of(L".", 0);
+  // If there isn't a dictionary delimiter in the path, we're done.
+  if (delimiter_position == std::wstring::npos) {
+    SetInCurrentNode(key, in_value);
+    return true;
+  } else {
+    key = path.substr(0, delimiter_position);
+  }
+
+  // Assume that we're indexing into a dictionary.
+  DictionaryValue* entry = NULL;
+  ValueType current_entry_type = TYPE_NULL;
+  if (!HasKey(key) || (dictionary_[key]->GetType() != TYPE_DICTIONARY)) {
+    entry = new DictionaryValue;
+    SetInCurrentNode(key, entry);
+  } else {
+    entry = static_cast<DictionaryValue*>(dictionary_[key]);
+  }
+
+  std::wstring remaining_path = path.substr(delimiter_position + 1);
+  return entry->Set(remaining_path, in_value);
+}
+
+bool DictionaryValue::SetBoolean(const std::wstring& path, bool in_value) {
+  return Set(path, CreateBooleanValue(in_value));
+}
+
+bool DictionaryValue::SetInteger(const std::wstring& path, int in_value) {
+  return Set(path, CreateIntegerValue(in_value));
+}
+
+bool DictionaryValue::SetReal(const std::wstring& path, double in_value) {
+  return Set(path, CreateRealValue(in_value));
+}
+
+bool DictionaryValue::SetString(const std::wstring& path,
+                                const std::wstring& in_value) {
+  return Set(path, CreateStringValue(in_value));
+}
+
+bool DictionaryValue::Get(const std::wstring& path, Value** out_value) const {
+  std::wstring key = path;
+
+  size_t delimiter_position = path.find_first_of(L".", 0);
+  if (delimiter_position != std::wstring::npos) {
+    key = path.substr(0, delimiter_position);
+  }
+
+  ValueMap::const_iterator entry_iterator = dictionary_.find(key);
+  if (entry_iterator == dictionary_.end())
+    return false;
+  Value* entry = entry_iterator->second;
+
+  if (delimiter_position == std::wstring::npos) {
+    if (out_value)
+      *out_value = entry;
+    return true;
+  }
+
+  if (entry->IsType(TYPE_DICTIONARY)) {
+    DictionaryValue* dictionary = static_cast<DictionaryValue*>(entry);
+    return dictionary->Get(path.substr(delimiter_position + 1), out_value);
+  }
+
+  return false;
+}
+
+bool DictionaryValue::GetBoolean(const std::wstring& path,
+                                 bool* bool_value) const {
+  Value* value;
+  if (!Get(path, &value))
+    return false;
+
+  return value->GetAsBoolean(bool_value);
+}
+
+bool DictionaryValue::GetInteger(const std::wstring& path,
+                                 int* out_value) const {
+  Value* value;
+  if (!Get(path, &value))
+    return false;
+
+  return value->GetAsInteger(out_value);
+}
+
+bool DictionaryValue::GetReal(const std::wstring& path,
+                              double* out_value) const {
+  Value* value;
+  if (!Get(path, &value))
+    return false;
+
+  return value->GetAsReal(out_value);
+}
+
+bool DictionaryValue::GetString(const std::wstring& path,
+                                std::wstring* out_value) const {
+  Value* value;
+  if (!Get(path, &value))
+    return false;
+
+  return value->GetAsString(out_value);
+}
+
+bool DictionaryValue::GetBinary(const std::wstring& path,
+                                BinaryValue** out_value) const {
+  Value* value;
+  bool result = Get(path, &value);
+  if (!result || !value->IsType(TYPE_BINARY))
+    return false;
+
+  if (out_value)
+    *out_value = static_cast<BinaryValue*>(value);
+
+  return true;
+}
+
+bool DictionaryValue::GetDictionary(const std::wstring& path,
+                                    DictionaryValue** out_value) const {
+  Value* value;
+  bool result = Get(path, &value);
+  if (!result || !value->IsType(TYPE_DICTIONARY))
+    return false;
+
+  if (out_value)
+    *out_value = static_cast<DictionaryValue*>(value);
+
+  return true;
+}
+
+bool DictionaryValue::GetList(const std::wstring& path,
+                              ListValue** out_value) const {
+  Value* value;
+  bool result = Get(path, &value);
+  if (!result || !value->IsType(TYPE_LIST))
+    return false;
+
+  if (out_value)
+    *out_value = static_cast<ListValue*>(value);
+
+  return true;
+}
+
+bool DictionaryValue::Remove(const std::wstring& path, Value** out_value) {
+  std::wstring key = path;
+
+  size_t delimiter_position = path.find_first_of(L".", 0);
+  if (delimiter_position != std::wstring::npos) {
+    key = path.substr(0, delimiter_position);
+  }
+
+  ValueMap::iterator entry_iterator = dictionary_.find(key);
+  if (entry_iterator == dictionary_.end())
+    return false;
+  Value* entry = entry_iterator->second;
+
+  if (delimiter_position == std::wstring::npos) {
+    if (out_value)
+      *out_value = entry;
+    else
+      delete entry;
+
+    dictionary_.erase(entry_iterator);
+    return true;
+  }
+
+  if (entry->IsType(TYPE_DICTIONARY)) {
+    DictionaryValue* dictionary = static_cast<DictionaryValue*>(entry);
+    return dictionary->Remove(path.substr(delimiter_position + 1), out_value);
+  }
+
+  return false;
+}
+
+Value* DictionaryValue::DeepCopy() const {
+  DictionaryValue* result = new DictionaryValue;
+
+  ValueMap::const_iterator current_entry = dictionary_.begin();
+  while (current_entry != dictionary_.end()) {
+    result->Set(current_entry->first, current_entry->second->DeepCopy());
+    ++current_entry;
+  }
+
+  return result;
+}
+
+bool DictionaryValue::Equals(const Value* other) const {
+  if (other->GetType() != GetType())
+    return false;
+
+  const DictionaryValue* other_dict =
+      static_cast<const DictionaryValue*>(other);
+  key_iterator lhs_it(begin_keys());
+  key_iterator rhs_it(other_dict->begin_keys());
+  while (lhs_it != end_keys() && rhs_it != other_dict->end_keys()) {
+    Value* lhs;
+    Value* rhs;
+    if (!Get(*lhs_it, &lhs) || !other_dict->Get(*rhs_it, &rhs) ||
+        !lhs->Equals(rhs)) {
+      return false;
+    }
+    ++lhs_it;
+    ++rhs_it;
+  }
+  if (lhs_it != end_keys() || rhs_it != other_dict->end_keys())
+    return false;
+
+  return true;
+}
+
+///////////////////// ListValue ////////////////////
+
+ListValue::~ListValue() {
+  Clear();
+}
+
+void ListValue::Clear() {
+  ValueVector::iterator list_iterator = list_.begin();
+  while (list_iterator != list_.end()) {
+    delete *list_iterator;
+    ++list_iterator;
+  }
+  list_.clear();
+}
+
+bool ListValue::Set(size_t index, Value* in_value) {
+  if (!in_value)
+    return false;
+
+  if (index >= list_.size()) {
+    // Pad out any intermediate indexes with null settings
+    while (index > list_.size())
+      Append(CreateNullValue());
+    Append(in_value);
+  } else {
+    DCHECK(list_[index] != in_value);
+    delete list_[index];
+    list_[index] = in_value;
+  }
+  return true;
+}
+
+bool ListValue::Get(size_t index, Value** out_value) const {
+  if (index >= list_.size())
+    return false;
+
+  if (out_value)
+    *out_value = list_[index];
+
+  return true;
+}
+
+bool ListValue::GetDictionary(size_t index,
+                              DictionaryValue** out_value) const {
+  Value* value;
+  bool result = Get(index, &value);
+  if (!result || !value->IsType(TYPE_DICTIONARY))
+    return false;
+
+  if (out_value)
+    *out_value = static_cast<DictionaryValue*>(value);
+
+  return true;
+}
+
+bool ListValue::Remove(size_t index, Value** out_value) {
+  if (index >= list_.size())
+    return false;
+
+  if (out_value)
+    *out_value = list_[index];
+  else
+    delete list_[index];
+
+  ValueVector::iterator entry = list_.begin();
+  entry += index;
+
+  list_.erase(entry);
+  return true;
+}
+
+void ListValue::Append(Value* in_value) {
+  DCHECK(in_value);
+  list_.push_back(in_value);
+}
+
+Value* ListValue::DeepCopy() const {
+  ListValue* result = new ListValue;
+
+  ValueVector::const_iterator current_entry = list_.begin();
+  while (current_entry != list_.end()) {
+    result->Append((*current_entry)->DeepCopy());
+    ++current_entry;
+  }
+
+  return result;
+}
+
+bool ListValue::Equals(const Value* other) const {
+  if (other->GetType() != GetType())
+    return false;
+
+  const ListValue* other_list =
+      static_cast<const ListValue*>(other);
+  const_iterator lhs_it, rhs_it;
+  for (lhs_it = begin(), rhs_it = other_list->begin();
+       lhs_it != end() && rhs_it != other_list->end();
+       ++lhs_it, ++rhs_it) {
+    if (!(*lhs_it)->Equals(*rhs_it))
+      return false;
+  }
+  if (lhs_it != end() || rhs_it != other_list->end())
+    return false;
+
+  return true;
+}
diff --git a/base/values.h b/base/values.h
new file mode 100644
index 0000000..4f135d0
--- /dev/null
+++ b/base/values.h
@@ -0,0 +1,382 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file specifies a recursive data storage class called Value
+// intended for storing setting and other persistable data.
+// It includes the ability to specify (recursive) lists and dictionaries, so
+// it's fairly expressive.  However, the API is optimized for the common case,
+// namely storing a hierarchical tree of simple values.  Given a
+// DictionaryValue root, you can easily do things like:
+//
+// root->SetString(L"global.pages.homepage", L"http://goateleporter.com");
+// std::wstring homepage = L"http://google.com";  // default/fallback value
+// root->GetString(L"global.pages.homepage", &homepage);
+//
+// where "global" and "pages" are also DictionaryValues, and "homepage"
+// is a string setting.  If some elements of the path didn't exist yet,
+// the SetString() method would create the missing elements and attach them
+// to root before attaching the homepage value.
+
+#ifndef CHROME_COMMON_VALUES_H__
+#define CHROME_COMMON_VALUES_H__
+
+#include <iterator>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+
+class Value;
+class FundamentalValue;
+class StringValue;
+class BinaryValue;
+class DictionaryValue;
+class ListValue;
+
+typedef std::vector<Value*> ValueVector;
+typedef std::map<std::wstring, Value*> ValueMap;
+
+// The Value class is the base class for Values.  A Value can be
+// instantiated via the Create*Value() factory methods, or by directly
+// creating instances of the subclasses.
+class Value {
+ public:
+  virtual ~Value();
+
+  // Convenience methods for creating Value objects for various
+  // kinds of values without thinking about which class implements them.
+  // These can always be expected to return a valid Value*.
+  static Value* CreateNullValue();
+  static Value* CreateBooleanValue(bool in_value);
+  static Value* CreateIntegerValue(int in_value);
+  static Value* CreateRealValue(double in_value);
+  static Value* CreateStringValue(const std::wstring& in_value);
+
+  // This one can return NULL if the input isn't valid.  If the return value
+  // is non-null, the new object has taken ownership of the buffer pointer.
+  static BinaryValue* CreateBinaryValue(char* buffer, size_t size);
+
+  typedef enum {
+    TYPE_NULL = 0,
+    TYPE_BOOLEAN,
+    TYPE_INTEGER,
+    TYPE_REAL,
+    TYPE_STRING,
+    TYPE_BINARY,
+    TYPE_DICTIONARY,
+    TYPE_LIST
+  } ValueType;
+
+  // Returns the type of the value stored by the current Value object.
+  // Each type will be implemented by only one subclass of Value, so it's
+  // safe to use the ValueType to determine whether you can cast from
+  // Value* to (Implementing Class)*.  Also, a Value object never changes
+  // its type after construction.
+  ValueType GetType() const { return type_; }
+
+  // Returns true if the current object represents a given type.
+  bool IsType(ValueType type) const { return type == type_; }
+
+  // These methods allow the convenient retrieval of settings.
+  // If the current setting object can be converted into the given type,
+  // the value is returned through the "value" parameter and true is returned;
+  // otherwise, false is returned and "value" is unchanged.
+  virtual bool GetAsBoolean(bool* out_value) const;
+  virtual bool GetAsInteger(int* out_value) const;
+  virtual bool GetAsReal(double* out_value) const;
+  virtual bool GetAsString(std::wstring* out_value) const;
+
+  // This creates a deep copy of the entire Value tree, and returns a pointer
+  // to the copy.  The caller gets ownership of the copy, of course.
+  virtual Value* DeepCopy() const;
+
+  // Compares if two Value objects have equal contents.
+  virtual bool Equals(const Value* other) const;
+
+ protected:
+  // This isn't safe for end-users (they should use the Create*Value()
+  // static methods above), but it's useful for subclasses.
+  Value(ValueType type) : type_(type) {}
+
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(Value);
+  Value();
+
+  ValueType type_;
+};
+
+// FundamentalValue represents the simple fundamental types of values.
+class FundamentalValue : public Value {
+ public:
+  FundamentalValue(bool in_value)
+    : Value(TYPE_BOOLEAN), boolean_value_(in_value) {}
+  FundamentalValue(int in_value)
+    : Value(TYPE_INTEGER), integer_value_(in_value) {}
+  FundamentalValue(double in_value)
+    : Value(TYPE_REAL), real_value_(in_value) {}
+  ~FundamentalValue();
+
+  // Subclassed methods
+  virtual bool GetAsBoolean(bool* out_value) const;
+  virtual bool GetAsInteger(int* out_value) const;
+  virtual bool GetAsReal(double* out_value) const;
+  virtual Value* DeepCopy() const;
+  virtual bool Equals(const Value* other) const;
+
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(FundamentalValue);
+
+  union {
+    bool boolean_value_;
+    int integer_value_;
+    double real_value_;
+  };
+};
+
+class StringValue : public Value {
+ public:
+  StringValue(const std::wstring& in_value)
+    : Value(TYPE_STRING), value_(in_value) {}
+  ~StringValue();
+
+  // Subclassed methods
+  bool GetAsString(std::wstring* out_value) const;
+  Value* DeepCopy() const;
+  virtual bool Equals(const Value* other) const;
+
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(StringValue);
+
+  std::wstring value_;
+};
+
+class BinaryValue: public Value {
+public:
+  // Creates a Value to represent a binary buffer.  The new object takes
+  // ownership of the pointer passed in, if successful.
+  // Returns NULL if buffer is NULL.
+  static BinaryValue* Create(char* buffer, size_t size);
+
+  // For situations where you want to keep ownership of your buffer, this
+  // factory method creates a new BinaryValue by copying the contents of the
+  // buffer that's passed in.
+  // Returns NULL if buffer is NULL.
+  static BinaryValue* CreateWithCopiedBuffer(char* buffer, size_t size);
+
+  BinaryValue::~BinaryValue();
+
+  // Subclassed methods
+  Value* DeepCopy() const;
+  virtual bool Equals(const Value* other) const;
+
+  size_t GetSize() const { return size_; }
+  char* GetBuffer() { return buffer_; }
+
+private:
+  DISALLOW_EVIL_CONSTRUCTORS(BinaryValue);
+
+  // Constructor is private so that only objects with valid buffer pointers
+  // and size values can be created.
+  BinaryValue::BinaryValue(char* buffer, size_t size);
+
+  char* buffer_;
+  size_t size_;
+};
+
+class DictionaryValue : public Value {
+ public:
+  DictionaryValue() : Value(TYPE_DICTIONARY) {}
+  ~DictionaryValue();
+
+  // Subclassed methods
+  Value* DeepCopy() const;
+  virtual bool Equals(const Value* other) const;
+
+  // Returns true if the current dictionary has a value for the given key.
+  bool DictionaryValue::HasKey(const std::wstring& key);
+
+  // Clears any current contents of this dictionary.
+  void DictionaryValue::Clear();
+
+  // Sets the Value associated with the given path starting from this object.
+  // A path has the form "<key>" or "<key>.<key>.[...]", where "." indexes
+  // into the next DictionaryValue down.  Obviously, "." can't be used
+  // within a key, but there are no other restrictions on keys.
+  // If the key at any step of the way doesn't exist, or exists but isn't
+  // a DictionaryValue, a new DictionaryValue will be created and attached
+  // to the path in that location.
+  // Note that the dictionary takes ownership of the value
+  // referenced by in_value.
+  bool Set(const std::wstring& path, Value* in_value);
+
+  // Convenience forms of Set().  These methods will replace any existing
+  // value at that path, even if it has a different type.
+  bool SetBoolean(const std::wstring& path, bool in_value);
+  bool SetInteger(const std::wstring& path, int in_value);
+  bool SetReal(const std::wstring& path, double in_value);
+  bool SetString(const std::wstring& path, const std::wstring& in_value);
+
+  // Gets the Value associated with the given path starting from this object.
+  // A path has the form "<key>" or "<key>.<key>.[...]", where "." indexes
+  // into the next DictionaryValue down.  If the path can be resolved
+  // successfully, the value for the last key in the path will be returned
+  // through the "value" parameter, and the function will return true.
+  // Otherwise, it will return false and "value" will be untouched.
+  // Note that the dictionary always owns the value that's returned.
+  bool Get(const std::wstring& path, Value** out_value) const;
+
+  // These are convenience forms of Get().  The value will be retrieved
+  // and the return value will be true if the path is valid and the value at
+  // the end of the path can be returned in the form specified.
+  bool GetBoolean(const std::wstring& path, bool* out_value) const;
+  bool GetInteger(const std::wstring& path, int* out_value) const;
+  bool GetReal(const std::wstring& path, double* out_value) const;
+  bool GetString(const std::wstring& path, std::wstring* out_value) const;
+  bool GetBinary(const std::wstring& path, BinaryValue** out_value) const;
+  bool GetDictionary(const std::wstring& path,
+                     DictionaryValue** out_value) const;
+  bool GetList(const std::wstring& path, ListValue** out_value) const;
+
+  // Removes the Value with the specified path from this dictionary (or one
+  // of its child dictionaries, if the path is more than just a local key).
+  // If |out_value| is non-NULL, the removed Value AND ITS OWNERSHIP will be
+  // passed out via out_value.  If |out_value| is NULL, the removed value will
+  // be deleted.  This method returns true if |path| is a valid path; otherwise
+  // it will return false and the DictionaryValue object will be unchanged.
+  bool Remove(const std::wstring& path, Value** out_value);
+
+  // This class provides an iterator for the keys in the dictionary.
+  // It can't be used to modify the dictionary.
+  class key_iterator
+    : private std::iterator<std::input_iterator_tag, const std::wstring> {
+  public:
+    key_iterator(ValueMap::const_iterator itr) { itr_ = itr; }
+    key_iterator operator++() { ++itr_; return *this; }
+    const std::wstring& operator*() { return itr_->first; }
+    bool operator!=(const key_iterator& other) { return itr_ != other.itr_; }
+    bool operator==(const key_iterator& other) { return itr_ == other.itr_; }
+
+  private:
+    ValueMap::const_iterator itr_;
+  };
+
+  key_iterator begin_keys() const { return key_iterator(dictionary_.begin()); }
+  key_iterator end_keys() const { return key_iterator(dictionary_.end()); }
+
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(DictionaryValue);
+
+  // Associates the value |in_value| with the |key|.  This method should be
+  // used instead of "dictionary_[key] = foo" so that any previous value can
+  // be properly deleted.
+  void SetInCurrentNode(const std::wstring& key, Value* in_value);
+
+  ValueMap dictionary_;
+};
+
+// This type of Value represents a list of other Value values.
+// TODO(jhughes): Flesh this out.
+class ListValue : public Value {
+ public:
+  ListValue() : Value(TYPE_LIST) {}
+  ~ListValue();
+
+  // Subclassed methods
+  Value* DeepCopy() const;
+  virtual bool Equals(const Value* other) const;
+
+  // Clears the contents of this ListValue
+  void Clear();
+
+  // Returns the number of Values in this list.
+  size_t GetSize() const { return list_.size(); }
+
+  // Sets the list item at the given index to be the Value specified by
+  // the value given.  If the index beyond the current end of the list, null
+  // Values will be used to pad out the list.
+  // Returns true if successful, or false if the index was negative or
+  // the value is a null pointer.
+  bool Set(size_t index, Value* in_value);
+
+  // Gets the Value at the given index.  Modifies value (and returns true)
+  // only if the index falls within the current list range.
+  // Note that the list always owns the Value passed out via out_value.
+  bool Get(size_t index, Value** out_value) const;
+
+  // Convenience forms of Get().  Modifies value (and returns true) only if
+  // the index is valid and the Value at that index can be returned in
+  // the specified form.
+  bool GetDictionary(size_t index, DictionaryValue** out_value) const;
+
+  // Removes the Value with the specified index from this list.
+  // If |out_value| is non-NULL, the removed Value AND ITS OWNERSHIP will be
+  // passed out via out_value.  If |out_value| is NULL, the removed value will
+  // be deleted.  This method returns true if |index| is valid; otherwise
+  // it will return false and the ListValue object will be unchanged.
+  bool Remove(size_t index, Value** out_value);
+
+  // Appends a Value to the end of the list.
+  void Append(Value* in_value);
+
+  // Iteration
+  typedef ValueVector::iterator iterator;
+  typedef ValueVector::const_iterator const_iterator;
+
+  ListValue::iterator begin() { return list_.begin(); }
+  ListValue::iterator end() { return list_.end(); }
+
+  ListValue::const_iterator begin() const { return list_.begin(); }
+  ListValue::const_iterator end() const { return list_.end(); }
+
+  ListValue::iterator Erase(iterator item) {
+    return list_.erase(item);
+  }
+
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(ListValue);
+
+  ValueVector list_;
+};
+
+// This interface is implemented by classes that know how to serialize and
+// deserialize Value objects.
+class ValueSerializer {
+ public:
+  virtual bool Serialize(const Value& root) = 0;
+
+  // This method deserializes the subclass-specific format into a Value object.
+  // The method should return true if and only if the root parameter is set
+  // to a complete Value representation of the serialized form.  If the
+  // return value is true, the caller takes ownership of the objects pointed
+  // to by root.  If the return value is false, root should be unchanged.
+  virtual bool Deserialize(Value** root) = 0;
+};
+
+#endif  // CHROME_COMMON_VALUES_H__
diff --git a/base/values_unittest.cc b/base/values_unittest.cc
new file mode 100644
index 0000000..1470a0d
--- /dev/null
+++ b/base/values_unittest.cc
@@ -0,0 +1,403 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/values.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class ValuesTest: public testing::Test {
+};
+
+TEST(ValuesTest, Basic) {
+  // Test basic dictionary getting/setting
+  DictionaryValue settings;
+  std::wstring homepage = L"http://google.com";
+  ASSERT_FALSE(
+    settings.GetString(L"global.homepage", &homepage));
+  ASSERT_EQ(std::wstring(L"http://google.com"), homepage);
+
+  ASSERT_FALSE(settings.Get(L"global", NULL));
+  ASSERT_TRUE(settings.Set(L"global", Value::CreateBooleanValue(true)));
+  ASSERT_TRUE(settings.Get(L"global", NULL));
+  ASSERT_TRUE(settings.SetString(L"global.homepage", L"http://scurvy.com"));
+  ASSERT_TRUE(settings.Get(L"global", NULL));
+  homepage = L"http://google.com";
+  ASSERT_TRUE(settings.GetString(L"global.homepage", &homepage));
+  ASSERT_EQ(std::wstring(L"http://scurvy.com"), homepage);
+
+  // Test storing a dictionary in a list.
+  ListValue* toolbar_bookmarks;
+  ASSERT_FALSE(
+    settings.GetList(L"global.toolbar.bookmarks", &toolbar_bookmarks));
+
+  toolbar_bookmarks = new ListValue;
+  settings.Set(L"global.toolbar.bookmarks", toolbar_bookmarks);
+  ASSERT_TRUE(
+    settings.GetList(L"global.toolbar.bookmarks", &toolbar_bookmarks));
+
+  DictionaryValue* new_bookmark = new DictionaryValue;
+  new_bookmark->SetString(L"name", L"Froogle");
+  new_bookmark->SetString(L"url", L"http://froogle.com");
+  toolbar_bookmarks->Append(new_bookmark);
+
+  ListValue* bookmark_list;
+  ASSERT_TRUE(settings.GetList(L"global.toolbar.bookmarks", &bookmark_list));
+  DictionaryValue* bookmark;
+  ASSERT_EQ(1, bookmark_list->GetSize());
+  ASSERT_TRUE(bookmark_list->GetDictionary(0, &bookmark));
+  std::wstring bookmark_name = L"Unnamed";
+  ASSERT_TRUE(bookmark->GetString(L"name", &bookmark_name));
+  ASSERT_EQ(std::wstring(L"Froogle"), bookmark_name);
+  std::wstring bookmark_url;
+  ASSERT_TRUE(bookmark->GetString(L"url", &bookmark_url));
+  ASSERT_EQ(std::wstring(L"http://froogle.com"), bookmark_url);
+}
+
+TEST(ValuesTest, BinaryValue) {
+  char* buffer = NULL;
+  // Passing a null buffer pointer doesn't yield a BinaryValue
+  BinaryValue* binary = BinaryValue::Create(buffer, 0);
+  ASSERT_FALSE(binary);
+
+  // If you want to represent an empty binary value, use a zero-length buffer.
+  buffer = new char[0];
+  ASSERT_TRUE(buffer);
+  binary = BinaryValue::Create(buffer, 0);
+  ASSERT_TRUE(binary);
+  ASSERT_TRUE(binary->GetBuffer());
+  ASSERT_EQ(buffer, binary->GetBuffer());
+  ASSERT_EQ(0, binary->GetSize());
+  delete binary;
+  binary = NULL;
+
+  // Test the common case of a non-empty buffer
+  buffer = new char[15];
+  binary = BinaryValue::Create(buffer, 15);
+  ASSERT_TRUE(binary);
+  ASSERT_TRUE(binary->GetBuffer());
+  ASSERT_EQ(buffer, binary->GetBuffer());
+  ASSERT_EQ(15, binary->GetSize());
+  delete binary;
+  binary = NULL;
+
+  char stack_buffer[42];
+  memset(stack_buffer, '!', 42);
+  binary = BinaryValue::CreateWithCopiedBuffer(stack_buffer, 42);
+  ASSERT_TRUE(binary);
+  ASSERT_TRUE(binary->GetBuffer());
+  ASSERT_NE(stack_buffer, binary->GetBuffer());
+  ASSERT_EQ(42, binary->GetSize());
+  ASSERT_EQ(0, memcmp(stack_buffer, binary->GetBuffer(), binary->GetSize()));
+  delete binary;
+}
+
+// This is a Value object that allows us to tell if it's been
+// properly deleted by modifying the value of external flag on destruction.
+class DeletionTestValue : public Value {
+public:
+  DeletionTestValue(bool* deletion_flag) : Value(TYPE_NULL) {
+    Init(deletion_flag);  // Separate function so that we can use ASSERT_*
+  }
+
+  void Init(bool* deletion_flag) {
+    ASSERT_TRUE(deletion_flag);
+    deletion_flag_ = deletion_flag;
+    *deletion_flag_ = false;
+  }
+
+  ~DeletionTestValue() {
+    *deletion_flag_ = true;
+  }
+
+private:
+  bool* deletion_flag_;
+};
+
+TEST(ValuesTest, ListDeletion) {
+  bool deletion_flag = true;
+
+  {
+    ListValue list;
+    list.Append(new DeletionTestValue(&deletion_flag));
+    EXPECT_FALSE(deletion_flag);
+  }
+  EXPECT_TRUE(deletion_flag);
+
+  {
+    ListValue list;
+    list.Append(new DeletionTestValue(&deletion_flag));
+    EXPECT_FALSE(deletion_flag);
+    list.Clear();
+    EXPECT_TRUE(deletion_flag);
+  }
+
+  {
+    ListValue list;
+    list.Append(new DeletionTestValue(&deletion_flag));
+    EXPECT_FALSE(deletion_flag);
+    EXPECT_TRUE(list.Set(0, Value::CreateNullValue()));
+    EXPECT_TRUE(deletion_flag);
+  }
+}
+
+TEST(ValuesTest, ListRemoval) {
+  bool deletion_flag = true;
+  Value* removed_item = NULL;
+
+  {
+    ListValue list;
+    list.Append(new DeletionTestValue(&deletion_flag));
+    EXPECT_FALSE(deletion_flag);
+    EXPECT_EQ(1, list.GetSize());
+    EXPECT_FALSE(list.Remove(-1, &removed_item));
+    EXPECT_FALSE(list.Remove(1, &removed_item));
+    EXPECT_TRUE(list.Remove(0, &removed_item));
+    ASSERT_TRUE(removed_item);
+    EXPECT_EQ(0, list.GetSize());
+  }
+  EXPECT_FALSE(deletion_flag);
+  delete removed_item;
+  removed_item = NULL;
+  EXPECT_TRUE(deletion_flag);
+
+  {
+    ListValue list;
+    list.Append(new DeletionTestValue(&deletion_flag));
+    EXPECT_FALSE(deletion_flag);
+    EXPECT_TRUE(list.Remove(0, NULL));
+    EXPECT_TRUE(deletion_flag);
+    EXPECT_EQ(0, list.GetSize());
+  }
+}
+
+TEST(ValuesTest, DictionaryDeletion) {
+  std::wstring key = L"test";
+  bool deletion_flag = true;
+
+  {
+    DictionaryValue dict;
+    dict.Set(key, new DeletionTestValue(&deletion_flag));
+    EXPECT_FALSE(deletion_flag);
+  }
+  EXPECT_TRUE(deletion_flag);
+
+  {
+    DictionaryValue dict;
+    dict.Set(key, new DeletionTestValue(&deletion_flag));
+    EXPECT_FALSE(deletion_flag);
+    dict.Clear();
+    EXPECT_TRUE(deletion_flag);
+  }
+
+  {
+    DictionaryValue dict;
+    dict.Set(key, new DeletionTestValue(&deletion_flag));
+    EXPECT_FALSE(deletion_flag);
+    dict.Set(key, Value::CreateNullValue());
+    EXPECT_TRUE(deletion_flag);
+  }
+}
+
+TEST(ValuesTest, DictionaryRemoval) {
+  std::wstring key = L"test";
+  bool deletion_flag = true;
+  Value* removed_item = NULL;
+
+  {
+    DictionaryValue dict;
+    dict.Set(key, new DeletionTestValue(&deletion_flag));
+    EXPECT_FALSE(deletion_flag);
+    EXPECT_TRUE(dict.HasKey(key));
+    EXPECT_FALSE(dict.Remove(L"absent key", &removed_item));
+    EXPECT_TRUE(dict.Remove(key, &removed_item));
+    EXPECT_FALSE(dict.HasKey(key));
+    ASSERT_TRUE(removed_item);
+  }
+  EXPECT_FALSE(deletion_flag);
+  delete removed_item;
+  removed_item = NULL;
+  EXPECT_TRUE(deletion_flag);
+
+  {
+    DictionaryValue dict;
+    dict.Set(key, new DeletionTestValue(&deletion_flag));
+    EXPECT_FALSE(deletion_flag);
+    EXPECT_TRUE(dict.HasKey(key));
+    EXPECT_TRUE(dict.Remove(key, NULL));
+    EXPECT_TRUE(deletion_flag);
+    EXPECT_FALSE(dict.HasKey(key));
+  }
+}
+
+TEST(ValuesTest, DeepCopy) {
+  DictionaryValue original_dict;
+  Value* original_null = Value::CreateNullValue();
+  original_dict.Set(L"null", original_null);
+  Value* original_bool = Value::CreateBooleanValue(true);
+  original_dict.Set(L"bool", original_bool);
+  Value* original_int = Value::CreateIntegerValue(42);
+  original_dict.Set(L"int", original_int);
+  Value* original_real = Value::CreateRealValue(3.14);
+  original_dict.Set(L"real", original_real);
+  Value* original_string = Value::CreateStringValue(L"peek-a-boo");
+  original_dict.Set(L"string", original_string);
+
+  char* original_buffer = new char[42];
+  memset(original_buffer, '!', 42);
+  BinaryValue* original_binary = Value::CreateBinaryValue(original_buffer, 42);
+  original_dict.Set(L"binary", original_binary);
+
+  ListValue* original_list = new ListValue();
+  Value* original_list_element_0 = Value::CreateIntegerValue(0);
+  original_list->Append(original_list_element_0);
+  Value* original_list_element_1 = Value::CreateIntegerValue(1);
+  original_list->Append(original_list_element_1);
+  original_dict.Set(L"list", original_list);
+
+  DictionaryValue* copy_dict =
+    static_cast<DictionaryValue*>(original_dict.DeepCopy());
+  ASSERT_TRUE(copy_dict);
+  ASSERT_NE(copy_dict, &original_dict);
+
+  Value* copy_null = NULL;
+  ASSERT_TRUE(copy_dict->Get(L"null", &copy_null));
+  ASSERT_TRUE(copy_null);
+  ASSERT_NE(copy_null, original_null);
+  ASSERT_TRUE(copy_null->IsType(Value::TYPE_NULL));
+
+  Value* copy_bool = NULL;
+  ASSERT_TRUE(copy_dict->Get(L"bool", &copy_bool));
+  ASSERT_TRUE(copy_bool);
+  ASSERT_NE(copy_bool, original_bool);
+  ASSERT_TRUE(copy_bool->IsType(Value::TYPE_BOOLEAN));
+  bool copy_bool_value = false;
+  ASSERT_TRUE(copy_bool->GetAsBoolean(&copy_bool_value));
+  ASSERT_TRUE(copy_bool_value);
+
+  Value* copy_int = NULL;
+  ASSERT_TRUE(copy_dict->Get(L"int", &copy_int));
+  ASSERT_TRUE(copy_int);
+  ASSERT_NE(copy_int, original_int);
+  ASSERT_TRUE(copy_int->IsType(Value::TYPE_INTEGER));
+  int copy_int_value = 0;
+  ASSERT_TRUE(copy_int->GetAsInteger(&copy_int_value));
+  ASSERT_EQ(42, copy_int_value);
+
+  Value* copy_real = NULL;
+  ASSERT_TRUE(copy_dict->Get(L"real", &copy_real));
+  ASSERT_TRUE(copy_real);
+  ASSERT_NE(copy_real, original_real);
+  ASSERT_TRUE(copy_real->IsType(Value::TYPE_REAL));
+  double copy_real_value = 0;
+  ASSERT_TRUE(copy_real->GetAsReal(&copy_real_value));
+  ASSERT_EQ(3.14, copy_real_value);
+
+  Value* copy_string = NULL;
+  ASSERT_TRUE(copy_dict->Get(L"string", &copy_string));
+  ASSERT_TRUE(copy_string);
+  ASSERT_NE(copy_string, original_string);
+  ASSERT_TRUE(copy_string->IsType(Value::TYPE_STRING));
+  std::wstring copy_string_value;
+  ASSERT_TRUE(copy_string->GetAsString(&copy_string_value));
+  ASSERT_EQ(std::wstring(L"peek-a-boo"), copy_string_value);
+
+  Value* copy_binary = NULL;
+  ASSERT_TRUE(copy_dict->Get(L"binary", &copy_binary));
+  ASSERT_TRUE(copy_binary);
+  ASSERT_NE(copy_binary, original_binary);
+  ASSERT_TRUE(copy_binary->IsType(Value::TYPE_BINARY));
+  ASSERT_NE(original_binary->GetBuffer(),
+    static_cast<BinaryValue*>(copy_binary)->GetBuffer());
+  ASSERT_EQ(original_binary->GetSize(),
+    static_cast<BinaryValue*>(copy_binary)->GetSize());
+  ASSERT_EQ(0, memcmp(original_binary->GetBuffer(),
+               static_cast<BinaryValue*>(copy_binary)->GetBuffer(),
+               original_binary->GetSize()));
+
+  Value* copy_value = NULL;
+  ASSERT_TRUE(copy_dict->Get(L"list", &copy_value));
+  ASSERT_TRUE(copy_value);
+  ASSERT_NE(copy_value, original_list);
+  ASSERT_TRUE(copy_value->IsType(Value::TYPE_LIST));
+  ListValue* copy_list = static_cast<ListValue*>(copy_value);
+  ASSERT_EQ(2, copy_list->GetSize());
+
+  Value* copy_list_element_0;
+  ASSERT_TRUE(copy_list->Get(0, &copy_list_element_0));
+  ASSERT_TRUE(copy_list_element_0);
+  ASSERT_NE(copy_list_element_0, original_list_element_0);
+  int copy_list_element_0_value;
+  ASSERT_TRUE(copy_list_element_0->GetAsInteger(&copy_list_element_0_value));
+  ASSERT_EQ(0, copy_list_element_0_value);
+
+  Value* copy_list_element_1;
+  ASSERT_TRUE(copy_list->Get(1, &copy_list_element_1));
+  ASSERT_TRUE(copy_list_element_1);
+  ASSERT_NE(copy_list_element_1, original_list_element_1);
+  int copy_list_element_1_value;
+  ASSERT_TRUE(copy_list_element_1->GetAsInteger(&copy_list_element_1_value));
+  ASSERT_EQ(1, copy_list_element_1_value);
+
+  delete copy_dict;
+}
+
+TEST(ValuesTest, Equals) {
+  Value* null1 = Value::CreateNullValue();
+  Value* null2 = Value::CreateNullValue();
+  EXPECT_NE(null1, null2);
+  EXPECT_TRUE(null1->Equals(null2));
+
+  Value* boolean = Value::CreateBooleanValue(false);
+  EXPECT_FALSE(null1->Equals(boolean));
+  delete null1;
+  delete null2;
+  delete boolean;
+
+  DictionaryValue dv;
+  dv.SetBoolean(L"a", false);
+  dv.SetInteger(L"b", 2);
+  dv.SetReal(L"c", 2.5);
+  dv.SetString(L"d", L"string");
+  dv.Set(L"e", Value::CreateNullValue());
+
+  DictionaryValue* copy = static_cast<DictionaryValue*>(dv.DeepCopy());
+  EXPECT_TRUE(dv.Equals(copy));
+
+  ListValue* list = new ListValue;
+  list->Append(Value::CreateNullValue());
+  list->Append(new DictionaryValue);
+  dv.Set(L"f", list);
+
+  EXPECT_FALSE(dv.Equals(copy));
+  copy->Set(L"f", list->DeepCopy());
+  EXPECT_TRUE(dv.Equals(copy));
+
+  list->Append(Value::CreateBooleanValue(true));
+  EXPECT_FALSE(dv.Equals(copy));
+  delete copy;
+}
diff --git a/base/watchdog.cc b/base/watchdog.cc
new file mode 100644
index 0000000..1d9c183
--- /dev/null
+++ b/base/watchdog.cc
@@ -0,0 +1,171 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/watchdog.h"
+#include "base/string_util.h"
+#include "base/thread.h"
+
+//------------------------------------------------------------------------------
+// Public API methods.
+
+// Start thread running in a Disarmed state.
+Watchdog::Watchdog(const TimeDelta& duration,
+                   const std::wstring& thread_watched_name,
+                   bool enabled)
+  : lock_(),
+    condition_variable_(&lock_),
+    state_(DISARMED),
+    duration_(duration),
+    thread_watched_name_(thread_watched_name),
+    handle_(NULL),
+    thread_id_(0) {
+  if (!enabled)
+    return;  // Don't start thread, or doing anything really.
+  handle_ = CreateThread(NULL,  // security
+                         0,    // Default stack size.
+                         Watchdog::ThreadStart,
+                         reinterpret_cast<void*>(this),
+                         CREATE_SUSPENDED,
+                         &thread_id_);
+  DCHECK(NULL != handle_);
+  if (NULL == handle_)
+    return ;
+  ResumeThread(handle_);  // WINAPI call.
+}
+
+// Notify watchdog thread, and wait for it to finish up.
+Watchdog::~Watchdog() {
+  if (NULL == handle_)
+    return;
+  {
+    AutoLock lock(lock_);
+    state_ = SHUTDOWN;
+  }
+  condition_variable_.Signal();
+  DWORD results = WaitForSingleObject(handle_, INFINITE);
+  DCHECK(WAIT_OBJECT_0 == results);
+  CloseHandle(handle_);
+  handle_ = NULL;
+}
+
+void Watchdog::Arm() {
+  ArmAtStartTime(TimeTicks::Now());
+}
+
+void Watchdog::ArmSomeTimeDeltaAgo(const TimeDelta& time_delta) {
+  ArmAtStartTime(TimeTicks::Now() - time_delta);
+}
+
+// Start clock for watchdog.
+void Watchdog::ArmAtStartTime(const TimeTicks start_time) {
+  {
+    AutoLock lock(lock_);
+    start_time_ = start_time;
+    state_ = ARMED;
+  }
+  // Force watchdog to wake up, and go to sleep with the timer ticking with the
+  // proper duration.
+  condition_variable_.Signal();
+}
+
+// Disable watchdog so that it won't do anything when time expires.
+void Watchdog::Disarm() {
+  if (NULL == handle_)
+    return;
+  AutoLock lock(lock_);
+  state_ = DISARMED;
+  // We don't need to signal, as the watchdog will eventually wake up, and it
+  // will check its state and time, and act accordingly.
+}
+
+//------------------------------------------------------------------------------
+// Internal private methods that the watchdog thread uses.
+
+// static
+DWORD __stdcall Watchdog::ThreadStart(void* pThis) {
+  Watchdog* watchdog = reinterpret_cast<Watchdog*>(pThis);
+  return watchdog->Run();
+}
+
+unsigned Watchdog::Run() {
+  SetThreadName();
+  TimeDelta remaining_duration;
+  while (1) {
+    AutoLock lock(lock_);
+    while (DISARMED == state_)
+      condition_variable_.Wait();
+    if (SHUTDOWN == state_)
+      return 0;
+    DCHECK(ARMED == state_);
+    remaining_duration = duration_ - (TimeTicks::Now() - start_time_);
+    if (remaining_duration.InMilliseconds() > 0) {
+      // Spurios wake?  Timer drifts?  Go back to sleep for remaining time.
+      condition_variable_.TimedWait(remaining_duration);
+    } else {
+      // We overslept, so this seems like a real alarm.
+      // Watch out for a user that stopped the debugger on a different alarm!
+      {
+        AutoLock static_lock(static_lock_);
+        if (last_debugged_alarm_time_ > start_time_) {
+          // False alarm: we started our clock before the debugger break (last
+          // alarm time).
+          start_time_ += last_debugged_alarm_delay_;
+          if (last_debugged_alarm_time_ > start_time_)
+            state_ = DISARMED;  // Too many alarms must have taken place.
+          continue;
+        }
+      }
+      state_ = DISARMED;  // Only alarm at most once.
+      TimeTicks last_alarm_time = TimeTicks::Now();
+      Alarm();  // Set a break point here to debug on alarms.
+      TimeDelta last_alarm_delay = TimeTicks::Now() - last_alarm_time;
+      if (last_alarm_delay > TimeDelta::FromMilliseconds(2)) {
+        // Ignore race of two alarms/breaks going off at roughly the same time.
+        AutoLock static_lock(static_lock_);
+        // This was a real debugger break.
+        last_debugged_alarm_time_ = last_alarm_time;
+        last_debugged_alarm_delay_ = last_alarm_delay;
+      }
+    }
+  }
+}
+
+void Watchdog::SetThreadName() const {
+  std::string name = StringPrintf("%s Watchdog",
+                                  WideToASCII(thread_watched_name_).c_str());
+  Thread::SetThreadName(name.c_str(), thread_id_);
+  DLOG(INFO) << "Watchdog active: " << name;
+}
+
+// static
+Lock Watchdog::static_lock_;  // Lock for access of static data...
+// static
+TimeTicks Watchdog::last_debugged_alarm_time_ = TimeTicks();
+// static
+TimeDelta Watchdog::last_debugged_alarm_delay_;
diff --git a/base/watchdog.h b/base/watchdog.h
new file mode 100644
index 0000000..2871941
--- /dev/null
+++ b/base/watchdog.h
@@ -0,0 +1,111 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// The Watchdog class creates a second thread that can Alarm if a specific
+// duration of time passes without proper attention.  The duration of time is
+// specified at construction time.  The Watchdog may be used many times by
+// simply calling Arm() (to start timing) and Disarm() (to reset the timer).
+// The Watchdog is typically used under a debugger, where the stack traces on
+// other threads can be examined if/when the Watchdog alarms.
+
+// Some watchdogs will be enabled or disabled via command line switches. To
+// facilitate such code, an "enabled" argument for the constuctor can be used
+// to permanently disable the watchdog.  Disabled watchdogs don't even spawn
+// a second thread, and their methods call (Arm() and Disarm()) return very
+// quickly.
+
+#ifndef BASE_WATCHDOG_H__
+#define BASE_WATCHDOG_H__
+
+#include <string>
+
+#include "base/condition_variable.h"
+#include "base/lock.h"
+#include "base/time.h"
+
+class Watchdog {
+ public:
+  // TODO(JAR)change default arg to required arg after all users have migrated.
+  // Constructor specifies how long the Watchdog will wait before alarming.
+  Watchdog(const TimeDelta& duration,
+           const std::wstring& thread_watched_name,
+           bool enabled = true);
+  virtual ~Watchdog();
+
+  // Start timing, and alarm when time expires (unless we're disarm()ed.)
+  void Arm();  // Arm  starting now.
+  void ArmSomeTimeDeltaAgo(const TimeDelta& time_delta);
+  void ArmAtStartTime(const TimeTicks start_time);
+
+  // Reset time, and do not set off the alarm.
+  void Disarm();
+
+  // Alarm is called if the time expires after an Arm() without someone calling
+  // Disarm().  This method can be overridden to create testable classes.
+  virtual void Alarm() {
+    DLOG(INFO) << "Watchdog alarmed for " << thread_watched_name_;
+  }
+
+ private:
+  enum State {ARMED, DISARMED, SHUTDOWN };
+
+  // Windows thread start callback
+  static DWORD WINAPI ThreadStart(void* pThis);
+
+  // Loop and test function for our watchdog thread.
+  unsigned Run();
+  void Watchdog::SetThreadName() const;
+
+  Lock lock_;  // Mutex for state_.
+  ConditionVariable condition_variable_;
+  State state_;
+  const TimeDelta duration_;  // How long after start_time_ do we alarm?
+  const std::wstring thread_watched_name_;
+  HANDLE handle_;  // Handle for watchdog thread.
+  DWORD thread_id_;  // Also for watchdog thread.
+
+  TimeTicks start_time_;  // Start of epoch, and alarm after duration_.
+
+  // When the debugger breaks (when we alarm), all the other alarms that are
+  // armed will expire (also alarm).  To diminish this effect, we track any
+  // delay due to debugger breaks, and we *try* to adjust the effective start
+  // time of other alarms to step past the debugging break.
+  // Without this safety net, any alarm will typically trigger a host of follow
+  // on alarms from callers that specify old times.
+  static Lock static_lock_;  // Lock for access of static data...
+  // When did we last alarm and get stuck (for a while) in a debugger?
+  static TimeTicks last_debugged_alarm_time_;
+  // How long did we sit on a break in the debugger?
+  static TimeDelta last_debugged_alarm_delay_;
+
+
+  DISALLOW_EVIL_CONSTRUCTORS(Watchdog);
+};
+
+#endif  // BASE_WATCHDOG_H__
diff --git a/base/watchdog_test.cc b/base/watchdog_test.cc
new file mode 100644
index 0000000..3f569af
--- /dev/null
+++ b/base/watchdog_test.cc
@@ -0,0 +1,153 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Tests for Watchdog class.
+
+#include "base/logging.h"
+#include "base/watchdog.h"
+#include "base/spin_wait.h"
+#include "base/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+//------------------------------------------------------------------------------
+// Provide a derived class to facilitate testing.
+
+// TODO(JAR): Remove default argument from constructor, and make mandatory.
+class WatchdogCounter : public Watchdog {
+ public:
+  WatchdogCounter(const TimeDelta& duration,
+                  const std::wstring& thread_watched_name,
+                  bool enabled = true)
+      : Watchdog(duration, thread_watched_name, enabled), alarm_counter_(0) {
+  }
+
+  virtual ~WatchdogCounter() {}
+
+  virtual void Alarm() {
+    alarm_counter_++;
+    Watchdog::Alarm();
+  }
+
+  int alarm_counter() { return alarm_counter_; }
+
+ private:
+  int alarm_counter_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(WatchdogCounter);
+};
+
+class WatchdogTest : public testing::Test {
+};
+
+
+//------------------------------------------------------------------------------
+// Actual tests
+
+// Minimal constructor/destructor test.
+TEST(WatchdogTest, StartupShutdownTest) {
+  Watchdog watchdog1(TimeDelta::FromMilliseconds(300), L"Disabled", false);
+  Watchdog watchdog2(TimeDelta::FromMilliseconds(300), L"Enabled", true);
+
+  // The following test is depricated, and should be removed when the
+  // default argument constructor is no longer accepted.
+  Watchdog watchdog3(TimeDelta::FromMilliseconds(300), L"Default");
+}
+
+// Test ability to call Arm and Disarm repeatedly.
+TEST(WatchdogTest, ArmDisarmTest) {
+  Watchdog watchdog1(TimeDelta::FromMilliseconds(300), L"Disabled", false);
+  watchdog1.Arm();
+  watchdog1.Disarm();
+  watchdog1.Arm();
+  watchdog1.Disarm();
+
+  Watchdog watchdog2(TimeDelta::FromMilliseconds(300), L"Enabled", true);
+  watchdog2.Arm();
+  watchdog2.Disarm();
+  watchdog2.Arm();
+  watchdog2.Disarm();
+
+  // The following test is depricated, and should be removed when the
+  // default argument constructor is no longer accepted.
+  Watchdog watchdog3(TimeDelta::FromMilliseconds(300), L"Default");
+  watchdog3.Arm();
+  watchdog3.Disarm();
+  watchdog3.Arm();
+  watchdog3.Disarm();
+}
+
+// Make sure a basic alarm fires when the time has expired.
+TEST(WatchdogTest, AlarmTest) {
+  WatchdogCounter watchdog(TimeDelta::FromMilliseconds(10), L"Enabled", true);
+  watchdog.Arm();
+  SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromSeconds(1),
+                                   watchdog.alarm_counter() > 0);
+  EXPECT_EQ(1, watchdog.alarm_counter());
+
+  // Set a time greater than the timeout into the past.
+  watchdog.ArmSomeTimeDeltaAgo(TimeDelta::FromSeconds(2));
+  // It should instantly go off, but certainly in less than a second.
+  SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromSeconds(1),
+                                   watchdog.alarm_counter() > 1);
+
+  EXPECT_EQ(2, watchdog.alarm_counter());
+}
+
+// Make sure a disable alarm does nothing, even if we arm it.
+TEST(WatchdogTest, ConstructorDisabledTest) {
+  WatchdogCounter watchdog(TimeDelta::FromMilliseconds(10), L"Disabled", false);
+  watchdog.Arm();
+  // Alarm should not fire, as it was disabled.
+  Sleep(500);
+  EXPECT_EQ(0, watchdog.alarm_counter());
+}
+
+// Make sure Disarming will prevent firing, even after Arming.
+TEST(WatchdogTest, DisarmTest) {
+  WatchdogCounter watchdog(TimeDelta::FromSeconds(1), L"Enabled", true);
+  watchdog.Arm();
+  Sleep(100);  // Don't sleep too long
+  watchdog.Disarm();
+  // Alarm should not fire.
+  Sleep(1500);
+  EXPECT_EQ(0, watchdog.alarm_counter());
+
+  // ...but even after disarming, we can still use the alarm...
+  // Set a time greater than the timeout into the past.
+  watchdog.ArmSomeTimeDeltaAgo(TimeDelta::FromSeconds(2));
+  // It should almost instantly go off, but certainly in less than a second.
+  SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromSeconds(1),
+                                   watchdog.alarm_counter() > 0);
+
+  EXPECT_EQ(1, watchdog.alarm_counter());
+}
+
+}  // namespace
diff --git a/base/windows_message_list.h b/base/windows_message_list.h
new file mode 100644
index 0000000..7a60fb0
--- /dev/null
+++ b/base/windows_message_list.h
@@ -0,0 +1,274 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// WARNING: DO NOT USE standard header file protection.
+// This file may be include several times in its entirety.
+
+// This file contains a list of all messages supported by Windows as would be
+// handled in a message loop.  We only list the messages provided in
+// <winuser.h>, and do not currently include (the otherwise undefined)
+// #define WM_SYSTIMER 0x118
+
+// By using various macro tricks, this list can be used to create pretty print
+// functions for the messages.  See message_loop.cc for an example.
+
+// Start list of Windows Messages given in <winuser.h>
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NULL)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CREATE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DESTROY)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOVE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SIZE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_ACTIVATE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SETFOCUS)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_KILLFOCUS)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_ENABLE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SETREDRAW)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SETTEXT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_GETTEXT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_GETTEXTLENGTH)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_PAINT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CLOSE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_QUERYENDSESSION)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_QUERYOPEN)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_ENDSESSION)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_QUIT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_ERASEBKGND)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SYSCOLORCHANGE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SHOWWINDOW)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_WININICHANGE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SETTINGCHANGE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DEVMODECHANGE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_ACTIVATEAPP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_FONTCHANGE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_TIMECHANGE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CANCELMODE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SETCURSOR)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOUSEACTIVATE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CHILDACTIVATE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_QUEUESYNC)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_GETMINMAXINFO)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_PAINTICON)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_ICONERASEBKGND)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NEXTDLGCTL)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SPOOLERSTATUS)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DRAWITEM)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MEASUREITEM)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DELETEITEM)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_VKEYTOITEM)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CHARTOITEM)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SETFONT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_GETFONT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SETHOTKEY)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_GETHOTKEY)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_QUERYDRAGICON)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_COMPAREITEM)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_GETOBJECT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_COMPACTING)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_COMMNOTIFY)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_WINDOWPOSCHANGING)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_WINDOWPOSCHANGED)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_POWER)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_COPYDATA)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CANCELJOURNAL)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NOTIFY)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_INPUTLANGCHANGEREQUEST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_INPUTLANGCHANGE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_TCARD)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_HELP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_USERCHANGED)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NOTIFYFORMAT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CONTEXTMENU)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_STYLECHANGING)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_STYLECHANGED)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DISPLAYCHANGE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_GETICON)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SETICON)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCCREATE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCDESTROY)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCCALCSIZE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCHITTEST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCPAINT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCACTIVATE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_GETDLGCODE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SYNCPAINT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCMOUSEMOVE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCLBUTTONDOWN)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCLBUTTONUP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCLBUTTONDBLCLK)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCRBUTTONDOWN)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCRBUTTONUP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCRBUTTONDBLCLK)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCMBUTTONDOWN)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCMBUTTONUP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCMBUTTONDBLCLK)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCXBUTTONDOWN)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCXBUTTONUP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCXBUTTONDBLCLK)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_INPUT_DEVICE_CHANGE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_INPUT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_KEYFIRST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_KEYDOWN)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_KEYUP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CHAR)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DEADCHAR)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SYSKEYDOWN)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SYSKEYUP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SYSCHAR)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SYSDEADCHAR)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_UNICHAR)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_KEYLAST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_KEYLAST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_STARTCOMPOSITION)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_ENDCOMPOSITION)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_COMPOSITION)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_KEYLAST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_INITDIALOG)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_COMMAND)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SYSCOMMAND)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_TIMER)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_HSCROLL)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_VSCROLL)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_INITMENU)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_INITMENUPOPUP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MENUSELECT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MENUCHAR)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_ENTERIDLE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MENURBUTTONUP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MENUDRAG)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MENUGETOBJECT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_UNINITMENUPOPUP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MENUCOMMAND)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CHANGEUISTATE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_UPDATEUISTATE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_QUERYUISTATE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CTLCOLORMSGBOX)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CTLCOLOREDIT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CTLCOLORLISTBOX)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CTLCOLORBTN)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CTLCOLORDLG)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CTLCOLORSCROLLBAR)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CTLCOLORSTATIC)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOUSEFIRST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOUSEMOVE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_LBUTTONDOWN)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_LBUTTONUP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_LBUTTONDBLCLK)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_RBUTTONDOWN)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_RBUTTONUP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_RBUTTONDBLCLK)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MBUTTONDOWN)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MBUTTONUP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MBUTTONDBLCLK)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOUSEWHEEL)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_XBUTTONDOWN)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_XBUTTONUP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_XBUTTONDBLCLK)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOUSEHWHEEL)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOUSELAST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOUSELAST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOUSELAST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOUSELAST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_PARENTNOTIFY)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_ENTERMENULOOP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_EXITMENULOOP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NEXTMENU)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SIZING)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CAPTURECHANGED)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOVING)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_POWERBROADCAST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DEVICECHANGE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDICREATE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDIDESTROY)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDIACTIVATE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDIRESTORE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDINEXT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDIMAXIMIZE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDITILE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDICASCADE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDIICONARRANGE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDIGETACTIVE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDISETMENU)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_ENTERSIZEMOVE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_EXITSIZEMOVE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DROPFILES)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDIREFRESHMENU)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_SETCONTEXT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_NOTIFY)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_CONTROL)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_COMPOSITIONFULL)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_SELECT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_CHAR)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_REQUEST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_KEYDOWN)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_KEYUP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOUSEHOVER)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOUSELEAVE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCMOUSEHOVER)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCMOUSELEAVE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_WTSSESSION_CHANGE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_TABLET_FIRST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_TABLET_LAST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CUT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_COPY)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_PASTE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CLEAR)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_UNDO)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_RENDERFORMAT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_RENDERALLFORMATS)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DESTROYCLIPBOARD)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DRAWCLIPBOARD)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_PAINTCLIPBOARD)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_VSCROLLCLIPBOARD)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SIZECLIPBOARD)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_ASKCBFORMATNAME)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CHANGECBCHAIN)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_HSCROLLCLIPBOARD)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_QUERYNEWPALETTE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_PALETTEISCHANGING)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_PALETTECHANGED)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_HOTKEY)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_PRINT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_PRINTCLIENT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_APPCOMMAND)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_THEMECHANGED)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CLIPBOARDUPDATE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DWMCOMPOSITIONCHANGED)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DWMNCRENDERINGCHANGED)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DWMCOLORIZATIONCOLORCHANGED)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DWMWINDOWMAXIMIZEDCHANGE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_GETTITLEBARINFOEX)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_HANDHELDFIRST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_HANDHELDLAST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_AFXFIRST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_AFXLAST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_PENWINFIRST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_PENWINLAST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_APP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_USER)
+// End list of Windows Messages given in <winuser.h>
diff --git a/base/wmi_util.cc b/base/wmi_util.cc
new file mode 100644
index 0000000..e9159e8
--- /dev/null
+++ b/base/wmi_util.cc
@@ -0,0 +1,145 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+#include <atlbase.h>
+
+#pragma comment(lib, "wbemuuid.lib")
+
+#include "base/wmi_util.h"
+
+bool WMIUtil::CreateLocalConnection(bool set_blanket,
+                                    IWbemServices** wmi_services) {
+  CComPtr<IWbemLocator> wmi_locator;
+  HRESULT hr = wmi_locator.CoCreateInstance(CLSID_WbemLocator, NULL,
+                                            CLSCTX_INPROC_SERVER);
+  if (FAILED(hr))
+    return false;
+
+  CComPtr<IWbemServices> wmi_services_r;
+  hr = wmi_locator->ConnectServer(CComBSTR(L"ROOT\\CIMV2"), NULL, NULL, 0, NULL,
+                                  0, 0, &wmi_services_r);
+  if (FAILED(hr))
+    return false;
+
+  if (set_blanket) {
+    hr = ::CoSetProxyBlanket(wmi_services_r,
+                             RPC_C_AUTHN_WINNT,
+                             RPC_C_AUTHZ_NONE,
+                             NULL,
+                             RPC_C_AUTHN_LEVEL_CALL,
+                             RPC_C_IMP_LEVEL_IMPERSONATE,
+                             NULL,
+                             EOAC_NONE);
+    if (FAILED(hr))
+      return false;
+  }
+
+  *wmi_services = wmi_services_r.Detach();
+  return true;
+}
+
+bool WMIUtil::CreateClassMethodObject(IWbemServices* wmi_services,
+                                      const std::wstring& class_name,
+                                      const std::wstring& method_name,
+                                      IWbemClassObject** class_instance) {
+  // We attempt to instantiate a COM object that represents a WMI object plus
+  // a method rolled into one entity.
+  CComBSTR b_class_name(class_name.c_str());
+  CComBSTR b_method_name(method_name.c_str());
+  CComPtr<IWbemClassObject> class_object = NULL;
+  HRESULT hr;
+  hr = wmi_services->GetObject(b_class_name, 0, NULL, &class_object, NULL);
+  if (FAILED(hr))
+    return false;
+
+  CComPtr<IWbemClassObject> params_def = NULL;
+  hr = class_object->GetMethod(b_method_name, 0, &params_def, NULL);
+  if (FAILED(hr))
+    return false;
+
+  if (NULL == params_def) {
+    // You hit this special case if the WMI class is not a CIM class. MSDN
+    // sometimes tells you this. Welcome to WMI hell.
+    return false;
+  }
+
+  hr = params_def->SpawnInstance(0, class_instance);
+  return(SUCCEEDED(hr));
+}
+
+bool SetParameter(IWbemClassObject* class_method,
+                  const std::wstring& parameter_name, VARIANT* parameter) {
+  HRESULT hr = class_method->Put(parameter_name.c_str(), 0, parameter, 0);
+  return SUCCEEDED(hr);
+}
+
+
+// The code in Launch() basically calls the Create Method of the Win32_Process
+// CIM class is documented here:
+// http://msdn2.microsoft.com/en-us/library/aa389388(VS.85).aspx
+
+bool WMIProcessUtil::Launch(const std::wstring& command_line, int* process_id) {
+  CComPtr<IWbemServices> wmi_local;
+  if (!WMIUtil::CreateLocalConnection(true, &wmi_local))
+    return false;
+
+  const wchar_t class_name[] = L"Win32_Process";
+  const wchar_t method_name[] = L"Create";
+  CComPtr<IWbemClassObject> process_create;
+  if (!WMIUtil::CreateClassMethodObject(wmi_local, class_name, method_name,
+                                        &process_create))
+    return false;
+
+  CComVariant b_command_line(command_line.c_str());
+  if (!SetParameter(process_create, L"CommandLine", &b_command_line))
+    return false;
+
+  CComPtr<IWbemClassObject> out_params;
+  HRESULT hr = wmi_local->ExecMethod(CComBSTR(class_name),
+                                     CComBSTR(method_name), 0, NULL,
+                                     process_create, &out_params, NULL);
+  if (FAILED(hr))
+    return false;
+
+  CComVariant ret_value;
+  hr = out_params->Get(L"ReturnValue", 0, &ret_value, NULL, 0);
+  if (FAILED(hr) || (0 != ret_value.uintVal))
+    return false;
+
+  CComVariant pid;
+  hr = out_params->Get(L"ProcessId", 0, &pid, NULL, 0);
+  if (FAILED(hr) || (0 == pid.intVal))
+    return false;
+
+  if (process_id)
+    *process_id = pid.intVal;
+
+  return true;
+}
diff --git a/base/wmi_util.h b/base/wmi_util.h
new file mode 100644
index 0000000..737b740
--- /dev/null
+++ b/base/wmi_util.h
@@ -0,0 +1,98 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// WMI (Windows Management and Instrumentation) is a big, complex, COM-based
+// API that can be used to perform all sorts of things. Sometimes is the best
+// way to accomplish something under windows but its lack of an approachable
+// C++ interface prevents its use. This collection of fucntions is a step in
+// that direction.
+// There are two classes; WMIUtil and WMIProcessUtil. The first
+// one contain generic helpers and the second one contains the only
+// functionality that is needed right now which is to use WMI to launch a
+// process.
+// To use any function on this header you must call CoInitialize or
+// CoInitializeEx beforehand.
+//
+// For more information about WMI programming:
+// http://msdn2.microsoft.com/en-us/library/aa384642(VS.85).aspx
+
+#ifndef BASE_WMI_UTIL_H__
+#define BASE_WMI_UTIL_H__
+
+#include <string>
+#include <wbemidl.h>
+
+class WMIUtil {
+ public:
+  // Creates an instance of the WMI service connected to the local computer and
+  // returns its COM interface. If 'set-blanket' is set to true, the basic COM
+  // security blanket is applied to the returned interface. This is almost
+  // always desirable unless you set the parameter to false and apply a custom
+  // COM security blanket.
+  // Returns true if succeeded and 'wmi_services': the pointer to the service.
+  // When done with the interface you must call Release();
+  static bool CreateLocalConnection(bool set_blanket,
+                                    IWbemServices** wmi_services);
+
+  // Creates a WMI method using from a WMI class named 'class_name' that
+  // contains a method named 'method_name'. Only WMI classes that are CIM
+  // classes can be created using this function.
+  // Returns true if succeeded and 'class_instance' returns a pointer to the
+  // WMI method that you can fill with parameter values using SetParameter.
+  // When done with the interface you must call Release();
+  static bool CreateClassMethodObject(IWbemServices* wmi_services,
+                                      const std::wstring& class_name,
+                                      const std::wstring& method_name,
+                                      IWbemClassObject** class_instance);
+
+  // Fills a single parameter given an instanced 'class_method'. Returns true
+  // if operation succeeded. When all the parameters are set the method can
+  // be executed using IWbemServices::ExecMethod().
+  static bool SetParameter(IWbemClassObject* class_method,
+                           const std::wstring& parameter_name,
+                           VARIANT* parameter);
+};
+
+// This class contains functionality of the WMI class 'Win32_Process'
+// more info: http://msdn2.microsoft.com/en-us/library/aa394372(VS.85).aspx
+class WMIProcessUtil {
+ public:
+  // Creates a new process from 'command_line'. The advantage over CreateProcess
+  // is that it allows you to always break out from a Job object that the caller
+  // is attached to even if the Job object flags prevent that.
+  // Returns true and the process id in process_id if the process is launched
+  // successful. False otherwise.
+  // Note that a fully qualified path must be specified in most cases unless
+  // the program is not in the search path of winmgmt.exe.
+  // Processes created this way are children of wmiprvse.exe and run with the
+  // caller credentials.
+  static bool Launch(const std::wstring& command_line, int* process_id);
+};
+
+#endif  // BASE_WMI_UTIL_H__
diff --git a/base/wmi_util_unittest.cc b/base/wmi_util_unittest.cc
new file mode 100644
index 0000000..40853a4
--- /dev/null
+++ b/base/wmi_util_unittest.cc
@@ -0,0 +1,80 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "base/wmi_util.h"
+
+TEST(WMIUtilTest, TestLocalConnectionSecurityBlanket) {
+  ::CoInitialize(NULL);
+  IWbemServices* services = NULL;
+  EXPECT_TRUE(WMIUtil::CreateLocalConnection(true, &services));
+  ASSERT_TRUE(NULL != services);
+  ULONG refs = services->Release();
+  EXPECT_EQ(refs, 0);
+  ::CoUninitialize();
+}
+
+TEST(WMIUtilTest, TestLocalConnectionNoSecurityBlanket) {
+  ::CoInitialize(NULL);
+  IWbemServices* services = NULL;
+  EXPECT_TRUE(WMIUtil::CreateLocalConnection(false, &services));
+  ASSERT_TRUE(NULL != services);
+  ULONG refs = services->Release();
+  EXPECT_EQ(refs, 0);
+  ::CoUninitialize();
+}
+
+TEST(WMIUtilTest, TestCreateClassMethod) {
+  ::CoInitialize(NULL);
+  IWbemServices* wmi_services = NULL;
+  EXPECT_TRUE(WMIUtil::CreateLocalConnection(true, &wmi_services));
+  ASSERT_TRUE(NULL != wmi_services);
+  IWbemClassObject* class_method = NULL;
+  EXPECT_TRUE(WMIUtil::CreateClassMethodObject(wmi_services,
+                                               L"Win32_ShortcutFile",
+                                               L"Rename", &class_method));
+  ASSERT_TRUE(NULL != class_method);
+  ULONG refs = class_method->Release();
+  EXPECT_EQ(refs, 0);
+  refs = wmi_services->Release();
+  EXPECT_EQ(refs, 0);
+  ::CoUninitialize();
+}
+
+// Creates an instance of cmd which executes 'echo' and exits immediately.
+TEST(WMIUtilTest, TestLaunchProcess) {
+  ::CoInitialize(NULL);
+  int pid = 0;
+  bool result = WMIProcessUtil::Launch(L"cmd.exe /c echo excelent!", &pid);
+  EXPECT_TRUE(result);
+  EXPECT_GT(pid, 0);
+  ::CoUninitialize();
+}
diff --git a/base/word_iterator.cc b/base/word_iterator.cc
new file mode 100644
index 0000000..1291630
--- /dev/null
+++ b/base/word_iterator.cc
@@ -0,0 +1,88 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/logging.h"
+#include "base/word_iterator.h"
+#include "unicode/ubrk.h"
+
+const int WordIterator::npos = -1;
+
+WordIterator::WordIterator(const std::wstring& str, BreakType break_type)
+    : iter_(NULL),
+      string_(str),
+      break_type_(break_type),
+      prev_(npos),
+      pos_(0) {
+}
+
+WordIterator::~WordIterator() {
+  if (iter_)
+    ubrk_close(iter_);
+}
+
+bool WordIterator::Init() {
+  UErrorCode status = U_ZERO_ERROR;
+  UBreakIteratorType break_type;
+  switch (break_type_) {
+    case BREAK_WORD:
+      break_type = UBRK_WORD;
+      break;
+    case BREAK_LINE:
+      break_type = UBRK_LINE;
+      break;
+    default:
+      NOTREACHED();
+      break_type = UBRK_LINE;
+  }
+  iter_ = ubrk_open(break_type, NULL,
+                    string_.data(), static_cast<int32_t>(string_.size()),
+                    &status);
+  if (U_FAILURE(status)) {
+    NOTREACHED() << "ubrk_open failed";
+    return false;
+  }
+  ubrk_first(iter_);  // Move the iterator to the beginning of the string.
+  return true;
+}
+
+bool WordIterator::Advance() {
+  prev_ = pos_;
+  const int32_t pos = ubrk_next(iter_);
+  if (pos == UBRK_DONE) {
+    pos_ = npos;
+    return false;
+  } else {
+    pos_ = static_cast<int>(pos);
+    return true;
+  }
+}
+
+bool WordIterator::IsWord() const {
+  return (ubrk_getRuleStatus(iter_) != UBRK_WORD_NONE);
+}
\ No newline at end of file
diff --git a/base/word_iterator.h b/base/word_iterator.h
new file mode 100644
index 0000000..fe86411
--- /dev/null
+++ b/base/word_iterator.h
@@ -0,0 +1,110 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_WORD_ITERATOR_H__
+#define BASE_WORD_ITERATOR_H__
+
+#include "base/basictypes.h"
+
+// The WordIterator class iterates through the words and word breaks
+// in a string.  (In the string " foo bar! ", the word breaks are at the
+// periods in ". .foo. .bar.!. .".)
+//
+// To extract the words from a string, move a WordIterator through the
+// string and test whether IsWord() is true.  E.g.,
+//   WordIterator iter(str, WordIterator::BREAK_WORD);
+//   if (!iter.Init()) return false;
+//   while (iter.Advance()) {
+//     if (iter.IsWord()) {
+//       // region [iter.prev(),iter.pos()) contains a word.
+//       LOG(INFO) << "word: " << iter.GetWord();
+//     }
+//   }
+
+
+class WordIterator {
+ public:
+  enum BreakType {
+    BREAK_WORD,
+    BREAK_LINE
+  };
+
+  // Requires |str| to live as long as the WordIterator does.
+  WordIterator(const std::wstring& str, BreakType break_type);
+  ~WordIterator();
+
+  // Init() must be called before any of the iterators are valid.
+  // Returns false if ICU failed to initialize.
+  bool Init();
+
+  // Return the current break position within the string,
+  // or WordIterator::npos when done.
+  int pos() const { return pos_; }
+  // Return the value of pos() returned before Advance() was last called.
+  int prev() const { return prev_; }
+
+  // A special position value indicating "end of string".
+  static const int npos;
+
+  // Advance to the next break.  Returns false if we've run past the end of
+  // the string.  (Note that the very last "word break" is after the final
+  // character in the string, and when we advance to that position it's the
+  // last time Advance() returns true.)
+  bool Advance();
+
+  // Returns true if the break we just hit is the end of a word.
+  // (Otherwise, the break iterator just skipped over e.g. whitespace
+  // or punctuation.)
+  bool IsWord() const;
+
+  // Return the word between prev() and pos().
+  // Advance() must have been called successfully at least once
+  // for pos() to have advanced to somewhere useful.
+  std::wstring GetWord() const {
+    DCHECK(prev_ >= 0 && pos_ >= 0);
+    return string_.substr(prev_, pos_ - prev_);
+  }
+
+ private:
+  // ICU iterator.
+  void* iter_;
+
+  // The string we're iterating over.
+  const std::wstring& string_;
+
+  // The breaking style (word/line).
+  BreakType break_type_;
+
+  // Previous and current iterator positions.
+  int prev_, pos_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(WordIterator);
+};
+
+#endif  // BASE_WORD_ITERATOR_H__
\ No newline at end of file
diff --git a/base/worker_pool.cc b/base/worker_pool.cc
new file mode 100644
index 0000000..e70edad
--- /dev/null
+++ b/base/worker_pool.cc
@@ -0,0 +1,57 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/worker_pool.h"
+
+#include "base/logging.h"
+
+namespace {
+
+DWORD CALLBACK WorkItemCallback(void* param) {
+  Task* task = static_cast<Task*>(param);
+  task->Run();
+  WorkerPool::RecycleTask(task);
+  return 0;
+}
+
+}  // namespace
+
+bool WorkerPool::Run(Task* task, bool slow) {
+  ULONG flags = 0;
+  if (slow)
+    flags |= WT_EXECUTELONGFUNCTION;
+
+  if (!QueueUserWorkItem(WorkItemCallback, task, flags)) {
+    DLOG(ERROR) << "QueueUserWorkItem failed: " << GetLastError();
+    RecycleTask(task);
+    return false;
+  }
+
+  return true;
+}
diff --git a/base/worker_pool.h b/base/worker_pool.h
new file mode 100644
index 0000000..3b0039f
--- /dev/null
+++ b/base/worker_pool.h
@@ -0,0 +1,50 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_WORKER_POOL_H_
+#define BASE_WORKER_POOL_H_
+
+#include "base/task.h"
+
+// This is a facility that runs tasks that don't require a specific thread or
+// a message loop.
+class WorkerPool {
+ public:
+  // This function posts |task| to run on a worker thread. |slow| should be used
+  // for tasks that will take a long time to execute. |task| will be recycled
+  // even if the function fails.
+  static bool Run(Task* task, bool slow);
+
+  // Recycles the task.
+  static void RecycleTask(Task* task) {
+    task->RecycleOrDelete();
+  }
+};
+
+#endif  // BASE_WORKER_POOL_H_